How would I create an activerecord scope(s) that would return customers who's next visit resulted in a purchase

StackOverflow https://stackoverflow.com/questions/17722749

Frage

I have three activerecord models: Customer, Visit and Campaign.

Customer    has_many   :visits
Visit       belongs_to :customer
Campaign    has_many   :visits    

The Visit model tracks everytime a particular customer visits a website, pages visited, ads displayed and most importantly if they made a purchase. A Campaign is a series of Ads the customers see during there visits to the site. Each Campaign lasts 1 hour (24 campaigns a day) and has many visits.

What I'm trying to do is develop some activerecord scopes or class methods that will enable me to identify "Next Visit Purchases".

For example, on July 4th the fourth campaign of the day had 100 visits by customers. I want to be able to look at the next visit for each of those customers and identify those visits/customers that had/made a purchase on that next visit. What I'm finding difficult to wrap my mind around is that customers subsequent visits aren't all on the same day, yet I want to identify the "Next Visit" and those that resulted in a Purchase.

What I envisioned is something like:

Campaign.find(2232).next_visit.purchase     #where next_visit and purchase are scopes

or

Visit.find(5445).next_visit.purchase 

I have a purchase flag in the visits model so the purchase scope is fairly straight forward.

scope, :purchase, where(:purchase_flag => true)

Also based on the Railscast #215, if I create this scope on the visits model I can then use joins and merge to apply them to the Customer and Campaign models.

Campaign.joins(:visits).merge(Visit.purchase)

Is this the correct approach? If so, how do I define my Next scope and if not, what would you suggest as an alternative approach.

Update: I've gotten some great responses. Just curious to know if the general consensus is that Deepak's approach is on point or are the other responses preferable.

War es hilfreich?

Lösung 2

So what you want is to measure the efficiency of a campaign, and have data about the customers that came back after the campaign and did purchases.

I propose:

class Campaign

  def next_visits
    # Wrapping the whole code of this method in @next_visits will perform local caching
    # There is a good railscast that explain it in details (5 minutes long video)
    # You can see it there: http://railscasts.com/episodes/1-caching-with-instance-variables
    @next_visits ||= begin
      # This will be the starting date of "next visits"
      since = self.end_of_campaign # I assume you can get the TimeDate of the end of campain

      # List of ids of customers that participated to the campaign
      customers_ids = self.visits.uniq(:customer_id).pluck(:customer_id)

      # Return the visit records
      next_visits = Visit.where(purchase_flag: true, customer_id: customers_ids)
      next_visits.where('created_at > ?', since).first
    end
  end

end

Then you call Campaign.find(123).next_visits

EDIT

You should use correct TZ with the variable since

About @next_visits ||= begin ... end: This is a caching technique. First time you call the method, all the code inside the begin blok will be executed, and the result (the records) will be stored in the instance variable @next_visits and returned to the caller.

Next time you call the method, the cached result stroed in @next_visits will be directly returned without hitting your database. That is good for performance.

More info about that http://railscasts.com/episodes/1-caching-with-instance-variables

Andere Tipps

I dont think scope is the right thing here, because you need to call it on an object. You could implement it as a method in visit.rb.

Could be like:

def next_visit
  Visit.where(['date > ?', self.date]).order(:date).limit(1).first
end

EDIT: For you to be able to chain methods

def next_visits
  Visit.where(['date > ?', self.date])
end

In the current database structure it is quite clumsy to achieve what you need and likely be very inefficient as well.

I have a different approach to this problem:

  • When a purchase is made - it is best opportunity to know which campaign/visit (check the last visit of the user and find the campaign) led to this purchase.
  • To capture these information, create appropriate relationship - with custom name - between campaign and visit.
  • Populate these in after_save/after_create callback of appropriate model (most likely Purchase or Visit).

Once the data is captured in above manner, queries are relatively easy.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top