No commit activity in last 3 years
No release in over 3 years
A gem that provides helper to achieve association counting without hassle. It provides you readonly data inside your ActiveRecord model without executing n+1 queries!
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 5.2.3
~> 2.0.2
~> 12.3.3
~> 3.8.0
 Project Readme

Scope SQL Counter

An ActiveRecord extension that helps you count association using SQL.

Since the association counting was computed within a single query, it's at least better than doing n+1 queries. The main idea is that you don't need to use counter cache library that migrate new columns, use 3rd party app and stuff.

rubygems

Installation

Add this line to your application's Gemfile:

gem 'scope_sql_counter'

Usage

Unfortunately, this gem heavily relies on ActiveRecord. Well, since most rails app use it, you are probably safe? Also, please make sure that all your associations have indices in order to maximize the speed of your query.

Add the scopes

So let's say your User model have a has_many :blogs association. Use the ActiveRecord extension method to generate the scope:

class User < ActiveRecord::Base
  has_many :blogs, dependent: :destroy

                    # scope name      # association name
  scope_sql_counter :with_blog_count, :blogs
end

This will create a scope User.with_blog_count on your model. And if you call it:

User.with_blog_count
=> User Load (0.8ms)
   SELECT  users.*, ( SELECT COUNT(blogs.id) FROM blogs WHERE blogs.user_id = users.id ) AS blogs_count
     FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]

As you can see, when the query executes, it sets an alias AS blogs_count. This alias should be available as a readonly attribute on your ActiveRecord model instance. For example:

users = User.with_blog_count
users.each do |user|
  user.blogs_count # => 8 // no further queries
end

Multiple scopes

There are times you may want to fetch multiple counter on your associations. You can achieve this by doing:

class User < ActiveRecord::Base
  has_many :blogs
  has_many :comments

  scope :with_multiple_count, -> {
    select(ScopeSqlCounter.new(context: self, association_key: :blogs).call)
      .select(ScopeSqlCounter.new(context: self, association_key: :comments).call)
  }
end

# But.. it doesn't look good? Don't worry! We can make it cleaner:

class User < ActiveRecord::Base
 scope_sql_counter :with_blog_count, :blogs
 scope_sql_counter :with_comment_count, :comments

 scope :with_multiple_count, -> { with_blog_count.with_comment_count }
end

Additional configurations

count_alias: Sets the alias name for the counter instead of the default

conditions: Adds more condition on your scope counter instead of plain association call

  1. count_alias . For example:
scope_sql_counter :with_blog_count, :blogs, count_alias: :posts_count
users = User.with_blog_count
=> User Load (0.8ms)
   SELECT  users.*, ( SELECT COUNT(blogs.id) FROM blogs WHERE blogs.user_id = users.id ) AS posts_count
     FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]

users.each do |user|
  user.posts_count # => 0
end
  1. conditions . For example:
scope_sql_counter :with_published_blog_count, :blogs,
                  conditions: 'blogs.published_at IS NOT NULL',
                  count_alias: :published_blog_count
users = User.with_published_blog_count
=> User Load (0.8ms)
   SELECT  users.*, ( SELECT COUNT(blogs.id) FROM blogs WHERE blogs.user_id = users.id
                        AND blogs.published_at IS NOT NULL) AS published_blog_count
     FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]

users.each do |user|
  user.published_blog_count # => 0
end

has_many :through and has_and_belongs_to_many

Dont' worry! It fully supports many to many relationships just fine!

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Add unit test
  4. Commit your changes (git commit -am 'Add some feature')
  5. Push to the branch (git push origin my-new-feature)
  6. Create new Pull Request

MIT

scope_sql_counter © 2019+, Harvey Ico. Released under the MIT License.