Search code examples
ruby-on-railsruby-on-rails-4mongoidmongoid4

Mongoid: How do I query for all object where the number of has_many object are > 0


I have a Gift model:

class Gift
  include Mongoid::Document
  include Mongoid::Timestamps

  has_many :gift_units, :inverse_of => :gift
end

And I have a GiftUnit model:

class GiftUnit
  include Mongoid::Document
  include Mongoid::Timestamps

  belongs_to :gift, :inverse_of => :gift_units
end

Some of my gifts have gift_units, but others have not. How do I query for all the gifts where gift.gift_units.size > 0?

Fyi: Gift.where(:gift_units.exists => true) does not return anything.


Solution

  • That has_many is an assertion about the structure of GiftUnit, not the structure of Gift. When you say something like this:

    class A
      has_many :bs
    end
    

    you are saying that instance of B have an a_id field whose values are ids for A instances, i.e. for any b which is an instance of B, you can say A.find(b.a_id) and get an instance of A back.

    MongoDB doesn't support JOINs so anything in a Gift.where has to be a Gift field. But your Gifts have no gift_units field so Gift.where(:gift_units.exists => true) will never give you anything.

    You could probably use aggregation through GiftUnit to find what you're looking for but a counter cache on your belongs_to relation should work better. If you had this:

    belongs_to :gift, :inverse_of => :gift_units, :counter_cache => true
    

    then you would get a gift_units_count field in your Gifts and you could:

    Gift.where(:gift_units_count.gt => 0)
    

    to find what you're looking for. You might have to add the gift_units_count field to Gift yourself, I'm finding conflicting information about this but I'm told (by a reliable source) in the comments that Mongoid4 creates the field itself.

    If you're adding the counter cache to existing documents then you'll have to use update_counters to initialize them before you can query on them.