Как рассчитать наиболее популярную комбинацию строк заказа? (или любое подобное расположение БД ордер/строки ордеров)


Я использую Ruby on Rails. У меня есть пара моделей, которые соответствуют нормальному порядку / порядку линий, т. е.

class Order
  has_many :order_lines
end

class OrderLines
  belongs_to :order
  belongs_to :product
end

class Product
  has_many :order_lines
end

(значительно упрощено по сравнению с моей реальной моделью!)

Довольно просто вычислить наиболее популярные отдельные продукты через строку заказа, но какой волшебный ruby-fu я мог бы использовать для вычисления самой популярной комбинации(ов) заказанных продуктов.

Ура!, Грэм

2 2

2 ответа:

Мое предложение состоит в том, чтобы создать массив a из Product.id чисел для каждого порядка, а затем сделать эквивалент

h = Hash.new(0)
# for each a
  h[a.sort.hash] += 1
Вам, естественно, нужно будет рассмотреть масштаб вашей операции и насколько вы готовы приблизить результаты.

Внешнее Решение

Создайте "комбинационную" модель и проиндексируйте таблицу по хэшу,после чего каждый ордер может увеличить поле счетчика. Другое поле будет точно записывать, на какую комбинацию ссылается это хэш-значение к.

решение в памяти

Посмотрите на последние 100 заказов и пересчитайте популярность заказа в памяти, когда вам это нужно. Hash#sort даст вам упорядоченный список хэшей популярности. Вы можете либо создать составной объект, который помнит, какая комбинация порядка была подсчитана, либо просто сканировать исходные данные в поисках хэш-значения.

Спасибо за подсказку digitalross. Я последовал идее внешнего решения и сделал следующее. Он немного отличается от предложения, поскольку он хранит запись отдельных order_combos, а не хранит счетчик, так что можно запросить по дате, например, самые популярные топ-10 заказов на прошлой неделе.

Я создал метод в своем заказе, который преобразует список элементов заказа в строку, разделенную запятыми.

def to_s
  order_lines.sort.map { |ol| ol.id }.join(",")
end

Затем я добавил фильтр, чтобы комбо создавалось каждый раз, когда заказ сделан.

after_save :create_order_combo

def create_order_combo
  oc = OrderCombo.create(:user => user, :combo => self.to_s)
end

И, наконец, мой класс OrderCombo выглядит примерно так, как показано ниже. Я также включил кэшированную версию метода.

class OrderCombo

  belongs_to :user

  scope :by_user, lambda{ |user| where(:user_id => user.id) }

  def self.top_n_orders_by_user(user,count=10)
    OrderCombo.by_user(user).count(:group => :combo).sort { |a,b| a[1] <=> b[1] }.reverse[0..count-1]
  end

  def self.cached_top_orders_by_user(user,count=10)
    Rails.cache.fetch("order_combo_#{user.id.to_s}_#{count.to_s}", :expiry => 10.minutes) { OrderCombo.top_n_orders_by_user(user, count) }
  end
end

Это не идеально, так как не учитывает возросшую популярность, когда кто-то заказывает больше одного предмета в заказе.