Mongoid: Как запросить все объекты, у которых номер объекта has_many> 0
У меня есть Gift
модель:
class Gift
include Mongoid::Document
include Mongoid::Timestamps
has_many :gift_units, :inverse_of => :gift
end
И у меня есть GiftUnit
модель:
class GiftUnit
include Mongoid::Document
include Mongoid::Timestamps
belongs_to :gift, :inverse_of => :gift_units
end
У некоторых моих подарков есть gift_units, а у других нет. Как я могу запросить все подарки, где gift.gift_units.size > 0
?
Fyi: Gift.where(:gift_units.exists => true)
ничего не возвращает.
2 ответа
Тот has_many
это утверждение о структуре GiftUnit
, а не структура Gift
, Когда вы говорите что-то вроде этого:
class A
has_many :bs
end
Вы говорите, что случай B
есть a_id
поле, значения которого id
с для A
экземпляры, т.е. для любого b
который является примером B
, ты можешь сказать A.find(b.a_id)
и получить экземпляр A
назад.
MongoDB не поддерживает JOIN, так что ничего в Gift.where
должен быть Gift
поле. Но твой Gift
у них нет gift_units
поле так Gift.where(:gift_units.exists => true)
никогда не даст вам ничего.
Вы могли бы использовать агрегацию через GiftUnit
чтобы найти то, что вы ищете, но счетчик кэша на вашем belongs_to
отношение должно работать лучше. Если у вас было это:
belongs_to :gift, :inverse_of => :gift_units, :counter_cache => true
тогда вы получите gift_units_count
поле в вашем Gift
и вы могли бы:
Gift.where(:gift_units_count.gt => 0)
чтобы найти то, что вы ищете. Возможно, вам придется добавить gift_units_count
поле для Gift
Вы сами находите противоречивую информацию об этом, но в комментариях мне сообщают (из надежного источника), что Mongoid4 создает само поле.
Если вы добавляете кеш счетчика в существующие документы, вам придется использовать update_counters
инициализировать их, прежде чем вы можете запросить их.
Я пытался найти решение этой проблемы уже несколько раз и всегда сдавался. Я только что понял, как это можно легко имитировать. Это может быть не очень масштабируемый способ, но он работает для ограниченного числа объектов. Ключом к этому является предложение из этой документации, где говорится:
Методы класса в моделях, которые возвращают объекты критериев, также обрабатываются как области видимости и могут быть также связаны в цепочку.
Итак, сделав это, вы можете определить функцию класса следующим образом:
def self.with_units
ids = Gift.all.select{|g| g.gift_units.count > 0}.map(&:id)
Gift.where(:id.in => ids)
end
Преимущество состоит в том, что вы можете выполнять все виды запросов в связанной модели (GiftUnits) и возвращать те экземпляры Gift, где эти запросы удовлетворяются (как это было для меня), и, что наиболее важно, вы можете связывать дальнейшие запросы, например, так:
Gift.with_units.where(:some_field => some_value)