Overview
Dase gem provides includes_count_of
method on a relation, which works similar to ActiveRecord's preload
method and solves N+1 query problem when counting records in has_many
ActiveRecord associations.
Usage
and this Rails model definition
class Author
has_many :articles
end
you can now write this:
authors = Author.includes_count_of(:articles)
billy = authors.first # => #<Author name: 'Billy'>
billy.articles_count # => 2
with conditions on associated records (only articles in year 2012)
Author.includes_count_of(:articles, where: {year: 2012} )
using lambda syntax (in v4.1 and greater)
Author.includes_count_of(:articles, -> { where(year: 2012) } )
with renamed counter method
Author.includes_count_of(:articles, -> { where(year: 2012) }, as: :number_of_articles_in_2012)
with multiple associations counted at once
Author.includes_count_of(:articles, :photos, :tweets)
Installation
Rails version | Add this to Gemfile |
---|---|
3.2.x | gem 'dase', '~> 3.2.0' |
4.0.x | ----- N/A ----- |
4.1.x | gem 'dase', '~> 4.1.0' |
4.2.x | gem 'dase', '~> 4.2.0' |
5.1.x | gem 'dase', '~> 5.1.0' |
Under the hood
When a relation is "materialized", we run a custom preloader which calculates the hash of counters in a single SQL query like this:
counters_hash = Article.where(:year => 2012).count(:group => :author_id)
then we add counters to the parent records like this:
define_method(:articles_count) { counters_hash[author.id] || 0 }
Alternative approaches
Dase calculates counters dynamically every time you make an SQL query. It makes an extra SQL query for each association processed. These alternatives may be more efficient:
- Cache column in the DB - see counter_culture gem, or counter_cache in Rails Guides.
- Using subquery in SELECT clause, or JOIN+SELECT+GROUP approach, as explained in that video
Name origin
The gem is named by the german mathematician Johann Dase, who was a mental calculator and could add and multiply numbers very quickly.
Contributing
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request