Code Monkey home page Code Monkey logo

scope_sql_counter's Introduction

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.

scope_sql_counter's People

Contributors

harveyico avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.