Как мне создать область(и) ActiveRecord, которая будет возвращать клиентов, следующее посещение которых привело к покупке

У меня есть три модели ActiveRecord: Customer, Visit и Campaign.

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

Модель посещения отслеживает каждый раз, когда конкретный клиент посещает веб-сайт, посещенные страницы, отображаемую рекламу и, что наиболее важно, совершает ли он покупку. Кампания — это серия рекламных объявлений, которые клиенты видят во время посещения сайта. Каждая кампания длится 1 час (24 кампании в день) и имеет много посещений.

Что я пытаюсь сделать, так это разработать некоторые области ActiveRecord или методы класса, которые позволят мне идентифицировать «Покупки при следующем посещении».

Например, 4 июля четвертая кампания дня насчитывала 100 посещений клиентов. Я хочу иметь возможность просмотреть следующее посещение для каждого из этих клиентов и определить те посещения/клиенты, которые совершили/совершили покупку при следующем посещении. Что мне трудно понять, так это то, что последующие посещения клиентов не все в один и тот же день, но я хочу определить «следующее посещение» и те, которые привели к покупке.

Я представлял что-то вроде:

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

or

Visit.find(5445).next_visit.purchase 

У меня есть флаг покупки в модели посещений, поэтому объем покупки довольно прост.

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

Также на основе Railscast #215, если я создам эту область в модели посещений я могу затем использовать объединения и слияние, чтобы применить их к моделям Customer и Campaign.

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

Это правильный подход? Если да, то как мне определить область действия Next, а если нет, то что бы вы предложили в качестве альтернативного подхода.

Обновление: я получил отличные ответы. Просто любопытно узнать, есть ли общий консенсус в том, что подход Дипака правильный или предпочтительнее другие ответы.


person Mutuelinvestor    schedule 18.07.2013    source источник
comment
Пользователь 2564200 не считает, что прицелы — это то, что нужно. Согласны ли другие с такой оценкой?   -  person Mutuelinvestor    schedule 18.07.2013
comment
Да я тоже так же думаю. Пожалуйста, смотрите мое предложение ниже в качестве ответа.   -  person Deepak Kumar    schedule 21.07.2013


Ответы (3)


Итак, вам нужно измерить эффективность кампании и получить данные о клиентах, которые вернулись после кампании и совершили покупки.

Я предлагаю:

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

Затем вы звоните Campaign.find(123).next_visits

ИЗМЕНИТЬ

Вы должны использовать правильный TZ с переменной since

О @next_visits ||= begin ... end: это метод кэширования. При первом вызове метода весь код внутри блока begin будет выполнен, а результат (записи) будет сохранен в переменной экземпляра @next_visits и возвращен вызывающей стороне.

В следующий раз, когда вы вызовете этот метод, кэшированный результат, сохраненный в @next_visits, будет возвращен напрямую, не затрагивая вашу базу данных. Это хорошо для производительности.

Подробнее об этом http://railscasts.com/episodes/1-caching-with-instance-variables

person Benj    schedule 25.07.2013
comment
Я не уверен, что понимаю вас, когда вы говорите, что используйте правильный TZ с переменной с тех пор. Часовой пояс? - person Mutuelinvestor; 26.07.2013
comment
Да, часовой пояс, я считаю, что приведенный мной пример кода будет работать, но у меня нет подробностей о том, как вы управляете часовым поясом в своем приложении, мое редактирование просто для того, чтобы напомнить вам, чтобы вы применяли свои соглашения и здесь. - person Benj; 26.07.2013
comment
Отредактировал мой пример: я просто немного подвел переменную экземпляра, чтобы выполнить локальное кэширование. - person Benj; 26.07.2013
comment
Бенджамин – Ваше решение имеет для меня большой смысл. Всего пара вопросов. 1. Нужен ли next_visit.where('created_at › ?', с тех пор) first. В противном случае он вернул бы все посещения после кампании вместо следующего посещения. 2. Что делает @next_visits ||= begin. Еще раз спасибо. Я думал, что Бегин должен быть в паре со Спасателем. - person Mutuelinvestor; 26.07.2013
comment
Хорошо, я отредактировал свой ответ, чтобы ответить на эти 2 пункта, и да: вы можете использовать спасательный блок, если хотите, но это не обязательно. Кстати, это код Rails 4, вы используете его или предыдущую версию? Дайте мне знать, чтобы я в конечном итоге отредактировал свой ответ - person Benj; 26.07.2013

Я не думаю, что область действия здесь подходит, потому что вам нужно вызывать ее для объекта. Вы можете реализовать его как метод в visit.rb.

Может быть так:

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

EDIT: чтобы вы могли связывать методы

def next_visits
  Visit.where(['date > ?', self.date])
end
person Santhosh    schedule 18.07.2013
comment
Таким образом, невозможно иметь, возможно, третью область, которая сводит ее к конкретному посещению, а затем применяет область действия Next_visit и покупки: Visit.number(100).next_visit.purchase или действительно имеет значение, является ли это областью действия или нет. Я думал, что область действия может обеспечить большую гибкость и возможности объединения в цепочки. Во всех случаях спасибо за ответ. - person Mutuelinvestor; 18.07.2013
comment
Если вы ожидаете, что next_visit вернет только один объект посещения, то нет смысла связывать с ним методы. Если вы хотите получить несколько next_visits, просто удалите вызов .first и limit из моего ответа, и вы получите активный объект отношения, который можно будет объединить в цепочку. - person Santhosh; 18.07.2013

В текущей структуре базы данных довольно неуклюже добиться того, что вам нужно, и, вероятно, это будет очень неэффективно.

У меня другой подход к этой проблеме:

  • Когда покупка совершена - это лучшая возможность узнать, какая кампания/посещение (проверьте последний визит пользователя и найдите кампанию) привели к этой покупке.
  • Чтобы получить эту информацию, создайте соответствующую связь — с произвольным именем — между кампанией и посещением.
  • Заполните их в обратном вызове after_save/after_create соответствующей модели (скорее всего, покупка или посещение).

После того, как данные собраны вышеописанным способом, запросы становятся относительно простыми.

person Deepak Kumar    schedule 21.07.2013
comment
Спасибо за ответ, я просто хочу уточнить, что вы предлагаете. Поэтому мы предлагаем новую модель, которая фиксирует кампании, покупки или просто ассоциации. - person Mutuelinvestor; 21.07.2013
comment
Достаточно просто установить связь между визитом и кампанией. Вам придется использовать собственное имя, поскольку у вас уже есть связь между кампанией и посещением. - person Deepak Kumar; 21.07.2013
comment
В качестве альтернативы вы можете создать связь между Кампанией и Покупкой (если у вас есть для этого модель). Это зависит от вашего требования. - person Deepak Kumar; 21.07.2013
comment
Спасибо. Теперь я следую. - person Mutuelinvestor; 21.07.2013