rails: AR::Relation::Calculations.count breaks relations with group and select

See runnable code example in gist.

Goal: get a count of rows from a query that includes pseudo columns and grouping.

When counting with grouping in the relation, ActiveRecord tries to return group counts instead of a total count and drops the SELECT on the floor. If there are pseudo-columns in the SELECT for grouping, this breaks the whole query.

Also, if it even worked, this would return a hash instead of a Fixnum. There doesn’t seem to be a way to get the total of returned rows, ignoring the grouping.

  • count should be usable to return a numeric count even when grouping is needed for the query.
  • relation.count and relation.all.count should have the same semantics.
  • count should work directly on any relation, so the relation can still be used as a chainable scope.

Suggestion for Rails 4: The Calculations#count method is too ambiguous and assumes too much. Change Calculations#count method to only return groups if a :group option is passed directly to the count method, i.e. do not trigger grouped counts based on other group_values in the relation. That, or split #count and #count_grouped into separate methods. This might make it easier to do the right thing with the query builder.

A simple first step might be to flag the relation so that it conditionally delegates .count to .all.count when the above situation occurs. Thoughts?

About this issue

  • Original URL
  • State: closed
  • Created 12 years ago
  • Comments: 21 (19 by maintainers)

Most upvoted comments

I’m also having this problem when paginating the results of a query with ARel in a .group call (supposedly, Kaminari.paginate calls count under the hood).

Here’s some interesting facts:

On Rails 3.2.2

> User.group(User.arel_table[:id]).size
# NoMethodError: undefined method `to_sym' for #<Arel::Attributes::Attribute:0xb0007c0>
from /active_record/relation/calculations.rb:248:in `execute_grouped_calculation'
> User.group('users.id').size
#  SELECT COUNT(*) AS count_all, users.id AS users_id FROM `users` GROUP BY users.id
=> {1=>1}

On Rails 3.2.7

> User.group(User.arel_table[:id]).size
# SELECT COUNT(*) AS count_all, `users`.`id` AS struct_arel_attributes_attribute_relation_arel_table_0xadfe044_name_users_engine_user_id_integer_name_string_created_at_datetime_updated_at_datetime_columns_nil_aliases_table_alias_nil_primary_key_nil_name_id FROM `users` GROUP BY `users`.`id`
=> {1=>1}

Notice the funny column name in the last query. Looks definitely like somewhere obj.to_s.underscore is called on an object which doesn’t expect it.

As a workaround, I separately store the scope for pagination(without grouping), and chain a .group call only when fetching the data itself. But I suppose, pretty many people use pagination so this is a common use case and should be addressed.