Question

I have a GiftCategory model:

class GiftCategory
  include Mongoid::Document

  field :gifts_count, type: Integer
  has_many :gifts, :inverse_of => :gift_category
end

And I have a Gift model:

class Gift
  include Mongoid::Document

  field :gift_units_count, type: Integer
  has_many :gift_units,      :inverse_of => :gift
  belongs_to :gift_category, :inverse_of => :gifts, :counter_cache => true

  after_save :update_counter

  def update_counter
    self.gift_category_id_change.each do |e|
      GiftCategory.reset_counters(e, :gifts) unless e.nil?
    end
  end
end

The update_countermethod allows me keep count of how many Gift objects belongs a GiftCategory. This way I can e.g. query only for GiftCategory objects that have some Gift objects:

GiftCategory.where(:gifts_count.gt => 0)

But as you can see, a Gift has a gift_units_count field as well. This field keeps count of the number units available of the Gift. How can I query for GiftCategory objects that has Gift objects with gift_units_count > 0?

I think that a solution could be something like described here, but I can't get closer myself.

Was it helpful?

Solution 2

That isn't inherently possible, since the document is referenced.

It's important to remember that GiftCategory doesn't actually contain a Gift. Instead, the Gift records have a field called gift_category_id. You essentially would need to find Gift records which have a gifts_unit_count > 0, then compile a list of their gift_category_id fields, unique them, and then retrieve those records.

This would do roughly what I've stated above:

gift_category_ids = Gift.where(:gifts_unit_count.gt => 0).map {|g| g.gift_category_id}.uniq
for gift_category_id in gift_category_ids
  gift_category = GiftCategory.find(gift_category_id)
  # do something with this
end

As far as I know, Mongoid is not willing to do something like this for you. As someone commented above, you may want to consider embedding, which would allow you to query this way, since the fields will be stored in the same document.

OTHER TIPS

I tried to find a solution for this problem several times already and always gave up. I just got an idea how this can be easily mimicked. It might not be a very scalable way, but it works for limited object counts. The key to this is a sentence from this documentation where it says:

Class methods on models that return criteria objects are also treated like scopes, and can be chained as well.

So, instead of writing the update_counter after save hook function and in order to save the GiftCategory.gifts_count field, you can define a class function like so:

def self.with_gifts
  ids = GiftCategory.all.select{|gc| gc.gifts.gift_units_count > 0}.pluck(:id)
  GiftCategory.where(:id.in => ids)
end

The advantage is, that you can do all kinds of queries on the associated (Gift) model and return those GiftCategory instances, where those queries are satisfied (which was the case for me) and most importantly you can chain further queries like so:

GiftCategories.with_gifts.where(:some_field => some_value)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top