Active Record has_one association for an aggregation

I really want to use a has_one association for an aggregation, so that I can use this association together with preload (or ar_lazy_preload) to easily avoid N+1 queries.

Suppose I have a comments_count method like this:

class Post < Active::Record
  has_many :comments

  def comments_count
    comments.count
  end
end

How can I convert this to has_one? Is anyone using associations for this use case?

Here are the other options I considered:

  • Make the aggregation a view-backed model (but lots of overhead for each association)
  • Run the aggregation completely separately (but this needs passing an aggregation results hash around with the list of records, or manually setting an attribute on each record)

I am not sure if I got you correctly.

For what I understood so far, is that you want to count the number of comments for each post, avoiding N+1 problem. If that is the case, I think you can use the Counter Cache to do that:

See: Active Record Associations — Ruby on Rails Guides

Basically you will have:

class Post < ApplicationRecord
  has_many :comments
end
class Comment < ApplicationRecord
  belongs_to :post, counter_cache: true
end

With this, you need to add comments_count in the Post model (posts table)

Rails will deal with keeping this column up-to-date. Then you can just call:

Post.first.comments_count
2 Likes

Neat, I didn’t know there was a such a feature.

But I’d love to hear what folks use as their generalized approach to this. Something that you’d use for any aggregation operation (sum, distinct, group by, etc.) and that supports where conditions.