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)
Другие вопросы по тегам