Last active
September 5, 2020 13:36
-
-
Save gpaddis/ceab19bd7a0fc5e5cc53ee329ffda73f to your computer and use it in GitHub Desktop.
WithCounts - dynamic parameterized scope to add count fields for has_many relations.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
# A dynamic parameterized scope to add a count field. Works with | |
# direct and polymorphic has_many relations. | |
# | |
# Example: | |
# > post = Post.with_counts(:comments, :likes).first | |
# > post.comments_count # contains the count of associated comments | |
# > post.likes_count # contains the count of associated likes | |
# | |
# See: https://medium.com/@eric.programmer/the-sql-alternative-to-counter-caches-59e2098b7d7 | |
module WithCounts | |
extend ActiveSupport::Concern | |
included do | |
scope :with_counts, lambda { |*relations| | |
select (["#{table_name}.*"] + count_queries(relations)).join(',') | |
} | |
# Just an alias for the scope above. | |
scope :with_count, ->(relations) { with_counts(relations) } | |
end | |
class_methods do | |
# Generate the count queries for the given relations according to their type. | |
# | |
# @param [Array] relations | |
# @return [Array] | |
def count_queries(relations) | |
Array(relations).map do |relation| | |
<<~SQL | |
( | |
SELECT COUNT(#{relation}.id) FROM #{relation} | |
#{where_query(relation)} | |
) AS #{relation}_count | |
SQL | |
end | |
end | |
private | |
# Generate a where query part according to the relation type. | |
# | |
# @param [Symbol] relation | |
# @return [String] | |
def where_query(relation) | |
if polymorphic?(relation) | |
polymorphic_as = reflect_on_association(relation).options[:as] | |
<<~SQL | |
WHERE #{polymorphic_as}_id = #{table_name}.id | |
AND #{polymorphic_as}_type = '#{name}' | |
SQL | |
else | |
"WHERE #{name.underscore.to_sym}_id = #{table_name}.id" | |
end | |
end | |
# Find out if a relation is polymorphic. | |
# | |
# @param [Symbol] relation | |
# @return [Boolean] | |
def polymorphic?(relation) | |
reflect_on_association(relation).options.key?(:as) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment