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.

1 ответ

Решение

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

Примерно так: - Создать новый атрибут order_hash в вашей модели заказа с помощью миграции. - Добавить 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 должен вернуть список заказов с такими же предметами. Вы можете применить точно такой же подход, даже если у вас есть количество товара и т. Д. Единственное ограничение заключается в том, что вы всегда должны искать заказы, которые являются одинаковыми, а не только неопределенно похожими.

Извиняюсь, если код изобилует ошибками - надеюсь, вы сможете разобраться в этом!

Другие вопросы по тегам