Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I find records by "count" of association using rails and mongoid?

With these models:

class Week
  has_many :proofs
end
class Proof
  belongs_to :week
end

I want to do something like:

Week.where(:proof.count.gt => 0)

To find only weeks that have multiple proofs.

There is one answer that seems to address this:

Can rails scopes filter on the number of associated classes for a given field

But in this example, there is no such attribute as proof_ids in Week since the ids are stored with the proofs. This does not work for example:

Week.where(:proof_ids.gt => 0)

How is this query possible? Conceptually simple but I can't figure out how to do this with mongo or mongoid.

Similarly, I'd like to order by the number of proofs for example like:

Week.desc(:proofs.size)

But this also does not work.

I do realize that a counter-cache is an option to both my specific questions but I'd also like to be able to do the query.

Thanks in advance for any help.

like image 203
eagspoo Avatar asked Nov 22 '11 07:11

eagspoo


2 Answers

With rails (and without counter_cache), you could do:

class Week < ActiveRecord::Base
  has_many :proofs

  def self.by_proofs_size
    sort_by { |week| week.proofs.size }
  end

  def self.with_at_least_n_proofs(n = 1)
    select { |week| week.proofs.size >= n }
  end
end

Even though each of those operations produces 2 queries, this is far from ideal.

The pair of queries is repeated (=> 4 queries for each operation) with scopes (bug?):

scope :with_at_least_n_proofs, -> (n = 1) { select { |w| w.proofs.size >= n } }
scope :by_proofs_size, -> { sort_by { |w| w.proofs.size } }

The ideal is probably to use counter_cache

scope :with_at_least_n_proofs, -> (n = 1) { where('proofs_count >= ?', n) }
scope :by_proofs_size, -> { order(proofs_count: :desc) }
like image 79
Damien Avatar answered Oct 22 '22 07:10

Damien


I don't know if this is the best solution, as it maps it through a array, but this does the job: (the other solutions mentioned here gives me exceptions)

class Week < ActiveRecord::Base

  scope :has_proofs, -> { any_in(:_id => includes(:proofs).select{ |w| w.proofs.size > 0 }.map{ |r| r.id }) }

end
like image 4
patrick Avatar answered Oct 22 '22 09:10

patrick