size of array returned by named_scope with :group/:select distinct

Here's the example: $ ruby script/generate scaffold thing name:string class Thing < ActiveRecord::Base   named_scope :distinct, :group => :name end

:select => "DISTINCT things.*" would work in the same way.

Start up script/console:

Thing.new(:name => "a").save!

=> true

Thing.new(:name => "a").save!

=> true

Thing.all

=> [#<Thing id: 1, name: "a", created_at: "2009-10-16 02:34:28", updated_at: "2009-10-16 02:34:28">, #<Thing id: 2, name: "a", created_at: "2009-10-16 02:34:28", updated_at: "2009-10-16 02:34:28">]

Thing.distinct

=> [#<Thing id: 2, name: "a", created_at: "2009-10-16 02:34:28", updated_at: "2009-10-16 02:34:28">]

Thing.distinct.size

=> 2

Thing.distinct.all.size

=> 1

As you can see, Thing.distinct.size is mis-reporting the size of the result. I can't figure out what's causing this, or why adding .all fixes it. Running the query straight into the database predictably returns just one record:

SELECT * FROM "things" GROUP BY name;

        id = 2       name = a created_at = 2009-10-16 02:34:28 updated_at = 2009-10-16 02:34:28

Am I missing something obvious?

I just stumbled upon the same problem... From what I can tell, ActiveRecord doesn't add the GROUP BY clause when it's doing a COUNT. Take a look in your log. You should see two nearly identical queries, but one will be a SELECT with the GROUP BY and one will be a COUNT without the GROUP BY.

Something like this:

SELECT things.* FROM things GROUP BY name

and then

SELECT COUNT(*) AS count_all FROM things; # NO GROUP BY!

The thing is, GROUP BY with an aggregate function will return the number of items in each group. So if you have 3 Things, "a", "a", and "b", adding a GROUP BY at the end of the COUNT query will give you something like this:

That makes sense - I was thinking of what named_scope returns as an Array, when really it's ActiveRecord::NamedScope::Scope. I see now in the source that it just delegates Array methods to find(:all), which is why you can do things like Thing.distinct.each and why Thing.distinct.to_s returns an Array's string notation. I'm not sure why it has to define a size method, that seems like something it could just hand off to find(:all) too.

I'll have to think about that subquery.

I found that calling named_scope.all.size resolves this issue. It's not perfect, but it does the job.