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