Skip to content

Yet Another N+1 COUNT Query Killer for ActiveRecord

License

Notifications You must be signed in to change notification settings

Nowsta/activerecord-precounter

 
 

Repository files navigation

ActiveRecord::Precounter Build Status

Yet Another N+1 COUNT Query Killer for ActiveRecord, counter_cache alternative.
ActiveRecord::Precounter allows you to cache count of associated records by eager loading.

This is another version of activerecord-precount, which is not elegant but designed to have no monkey-patch to ActiveRecord internal APIs for maintainability.

Synopsis

N+1 count query

Sometimes you may see many count queries for one association. You can use counter_cache to solve it, but you need to ALTER table and concern about dead lock to use counter_cache.

tweets = Tweet.all
tweets.each do |tweet|
  p tweet.favorites.count
end
# SELECT `tweets`.* FROM `tweets`
# SELECT COUNT(*) FROM `favorites` WHERE `favorites`.`tweet_id` = 1
# SELECT COUNT(*) FROM `favorites` WHERE `favorites`.`tweet_id` = 2
# SELECT COUNT(*) FROM `favorites` WHERE `favorites`.`tweet_id` = 3
# SELECT COUNT(*) FROM `favorites` WHERE `favorites`.`tweet_id` = 4
# SELECT COUNT(*) FROM `favorites` WHERE `favorites`.`tweet_id` = 5

Count eager loading

precount

With activerecord-precounter gem installed, you can use ActiveRecord::Precounter#precount method to eagerly load counts of associated records. Like preload, it loads counts by multiple queries

tweets = Tweet.all
ActiveRecord::Precounter.new(tweets).precount(:favorites)
tweets.each do |tweet|
  p tweet.favorites_count
end
# SELECT `tweets`.* FROM `tweets`
# SELECT COUNT(`favorites`.`tweet_id`), `favorites`.`tweet_id` FROM `favorites` WHERE `favorites`.`tweet_id` IN (1, 2, 3, 4, 5) GROUP BY `favorites`.`tweet_id`

Installation

Add this line to your application's Gemfile:

gem 'activerecord-precounter'

Limitation

Target has_many association must have inversed belongs_to. i.e. ActiveRecord::Precounter.new(tweets).precount(:favorites) needs both Tweet.has_many(:favorites) and Favorite.belongs_to(:tweet).

Unlike activerecord-precount, the cache store is not ActiveRecord association and it does not utilize ActiveRecord preloader. Thus you can't use preload to eager load counts for nested associations. And currently there's no JOIN support.

License

MIT License

About

Yet Another N+1 COUNT Query Killer for ActiveRecord

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Ruby 96.4%
  • Logos 2.5%
  • Shell 1.1%