ActiveRecord находит идентичный набор в моделях many_to_many

У меня есть анти-шаблон в моем коде Rails 3, и мне было интересно, как это сделать правильно.

Допустим, клиент заказывает картофель фри и гамбургер. Я хочу узнать, был ли такой заказ ранее. Для простоты каждый элемент можно заказать только один раз в заказе (поэтому никаких «два гамбургера, пожалуйста»), а также нет дублирующих заказов.

Модели:

Order (attributes: id)
  has_many :items_orders
  has_many :items, :through => :items_orders

Item (attributes: id, name) 
  has_many :items_orders
  has_many :orders,:through => :items_orders

ItemsOrder (attributes: id, item_id, order_id)
  belongs_to :order
  belongs_to :item 
  validates_uniqueness_of :item_id, :scope => :order_id

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

order = [1, 2]

1 и 2 соответствуют идентификаторам продуктов картофеля фри и гамбургеров.

candidates = Order.find(
  :all, 
  :include => :items_orders, 
  :conditions =>  ["items_orders.item_id in (?)", order])

previous_order = nil

candidates.each do |candidate|
  if candidate.items.collect{|i| i.id} == order
    previous_order = candidate
    break
  end 
end

Я использую MySQL и Postgress, поэтому я также открыт для стандартного решения SQL, которое игнорирует большую часть ActiveRecord.


person Sjors Provoost    schedule 25.02.2011    source источник
comment
Не могли бы вы перечислить атрибуты и отношения Order, Item' and ItemOrder`?   -  person Chirantan    schedule 25.02.2011
comment
Помогает ли мой ответ ниже, @SjorsProvoost? Дайте мне знать, если у вас есть какие-либо проблемы?   -  person Paul Russell    schedule 04.03.2011
comment
@PaulRussell, это определенно будет намного быстрее, чем у меня сейчас, спасибо. Я просто надеялся, что будет решение, не требующее избыточности (или, по крайней мере, такое, которое позволит базе данных позаботиться об этом).   -  person Sjors Provoost    schedule 09.03.2011


Ответы (1)


Предполагая, что вы хотите найти только идентичные заказы, у меня возникнет соблазн использовать для этого хэш. Я не могу протестировать код, который собираюсь написать, поэтому, пожалуйста, проверьте его, прежде чем полагаться на него!

Что-то вроде этого: - Создайте новый атрибут order_hash в вашей модели Order с помощью миграции. - Добавьте хук before_save, который обновляет этот атрибут, используя, например. хэш MD5 строк заказа. - Добавить метод поиска похожих заказов, который использует хэш для быстрого поиска других заказов, которые совпадают.

Код будет выглядеть примерно так:

class Order < ActiveRecord
  def before_save
    self.order_hash = calculate_hash
  end

  def find_similar
    Order.where(:order_hash => self.calculate_hash)
  end

  def calculate_hash
    d = Digest::MD5.new()
    self.items.order(:id).each do |i|
      d << i.id
    end
    d.hexdigest
  end
end

Этот код позволит вам создавать и заказывать, а затем вызов order.find_similar должен возвращать список заказов с одинаковыми прикрепленными элементами. Вы можете применить точно такой же подход, даже если у вас есть количество товаров и т. д. Единственным ограничением является то, что вы должны всегда искать одинаковые заказы, а не просто похожие друг на друга.

Приносим извинения, если код изобилует ошибками — надеюсь, вы сможете его понять!

person Paul Russell    schedule 25.02.2011