Ruby on Rails - ActiveRecord::Relation count method is wrong?
see this Rails 3: Difference between Relation.count and Relation.all.count
In short Rails ignores the select
columns (if more than one) when you apply count to the query. This is because
SQL's COUNT allows only one or less columns as parameters.
From Mailbox
code
scope :participant, lambda {|participant|
select('DISTINCT conversations.*').
where('notifications.type'=> Message.name).
order("conversations.updated_at DESC").
joins(:receipts).merge(Receipt.recipient(participant))
}
self.mailbox.conversations.count
ignores the select('DISTINCT conversations.*')
and counts the join
table with receipts
, essentially counting number of receipts
with duplicate conversations
in it.
On the other hand, self.mailbox.conversations.all.count
first gets the records applying the select
, which gets unique conversations
and then counts it.
self.mailbox.conversations.all == self.mailbox.conversations
since both of them query the db with the select.
To solve your problem you can use sending_user.mailbox.conversations.all.count
or sending_user.mailbox.conversations.group('conversations.id').length
Is overriding an ActiveRecord relation's count() method okay?
Edit:
I checked the code of will_paginate, seems like it is not using count
method of AR relation, but i found that you can provide option total_entries
for paginate
@kids = @parent.kids.for_chatting.paginate(
page: params[:page],
total_entries: parent.chatty_kids_count
)
This is not working
You can use wrapper for collection like here
https://github.com/kaminari/kaminari/pull/818#issuecomment-252788488,
just override count method.class RelationWrapper < SimpleDelegator
def initialize(relation, total_count)
super(relation)
@total_count = total_count
end
def count
@total_count
end
end
# in a controller:
relation = RelationWrapper.new(@parent.kids.for_chatting, parent.chatty_kids_count)
Rails5.2 - Active Record .select() causing error when .count is called
User.all.select('users.*,"one" as one').count(:all)
and
User.all.select('users.*,"one" as one').count(:id)
Both work because it specifies what the SQL code should count on.
Rails 3: Difference between Relation.count and Relation.all.count
Okay, thanks to tadman for pushing me in the right direction.
I digged somewhat deeper (especially in the log files) and what i found is a little bit weird.
The problem was caused by the number of selected columns. If one selects only one column and counts the result
my_meal.noodles.select("distinct color").count
ActiveRecord creates the following SQL statement:
SELECT COUNT(distinct color) AS count_id FROM "NOODLES" WHERE ("NOODLES".meal_id = 295)
In case one selects two or more columns and applies count
to it
my_meal.noodles.select("distinct color, shape").count
ActiveRecord forgets about that select clause and creates:
SELECT COUNT(*) AS count_id FROM "NOODLES" WHERE ("NOODLES".meal_id = 295)
This may be right, since (SQL's) COUNT
allows only one or less columns as parameters. Add a group
before the count
and anything is fine:
my_meal.noodles.select("distinct color, shape").group("color, shape").count
SELECT COUNT(*) AS count_all, color, shape AS color_shape FROM "NOODLES" WHERE ("NOODLES".meal_id = 295) GROUP BY color, shape
Apart from this AS color_shape
it is exact what i expected. BUT... only it returns this:
>> my_meal.noodles.select("distinct color, shape").group("color, shape").count
=> {star=>309, circle=>111, spaghetti=>189, square=>194, triangle=>179, bowtie=>301, shell=>93, letter=>230}
>> my_meal.noodles.select("distinct color, shape").group("color, shape").count.class
=> ActiveSupport::OrderedHash
This weird return value is (apart from order which depends on the DB) identical with the result and return value of
my_meal.noodles.group("shape").count
Conclusion:
As pointed out here there is still a gap between relations (may they be mathematical or arel relations) and ActiveRecord::Relations.
I can see the advantages of pressing the result in the patterns of a model as often as possible (at least in the context of a Rails app).
However real relations are not the result of the combination of several operations but the result of the concatenation of those operations.
In general the chainability of ActiveRecord::Relations is a great thing but there are some design decisions i cannot follow.
If you can't depend on the assurance that each action returns a new relation to work with, it looses much of its inuitional appeal.
As for the solution of my problem, i will use the above-mentioned group
solution and some kind of dirty workaround for the count operation:
my_meal.noodles.select("distinct color, shape").group("color, shape").all.count
This compresses the results to an acceptable minimum before pulling them out of the database and creating expensive objects just to count them. Alternatively one could use a handwritten SQL query, but why have a Rails and do not use it, huh? ;-)
Thanks for your help,
Tim
Count Method on Undefined Active Record Array @objects.count
There are many ways to write something like this.
Something simple would be:
<% if @objects %>
<%= @objects.count %>
<% else %>
nothing found
<% end %>
If you get into a slightly more complex conditional I would suggest moving the logic into a helper and call it from the view. ex:
<%= count_for(@object) %>
query , can not select column count
Have you tried calling the count
method on one of the returned Tag
objects? Just because inspect
doesn't mention the count
doesn't mean that it isn't there. The inspect
output:
[#<Tag id: 401, name: "different">, ... , #<Tag id: 4, name: "family">]
will only include things that the Tag
class knows about and Tag
will only know about the columns in the tags
table: you only have id
and name
in the table so that's all you see.
If you do this:
tags = Tag.joins(:quote_tags).group('quote_tags.tag_id').order('count desc').select('count(tags.id) AS count, tags.id, tags.name')
and then look at the count
s:
tags.map(&:count)
You'll see the array of counts that you're expecting.
Why does Relation.size sometimes return a Hash in Rails 4
size
on an ActiveRecord::Relation
object translates to count
, because the former tries to get the count of the Relation
. But when you call count
on a grouped Relation
object, you receive a hash.
The keys of this hash are the grouped column's values; the values of this hash are the respective counts.
AssessmentResponse.group(:client_id).count # this will return a Hash
AssessmentResponse.group(:client_id).size # this will also return a Hash
This is true for the following methods: count
, sum
, average
, maximum
, and minimum
.
If you want to check for rows being present or not, simply use exists?
i.e. do the following:
AssessmentResponse.group(:client_id).exists?
Instead of this:
AssessmentResponse.group(:client_id).count.zero?
Related Topics
Ruby on Rails: Radio Buttons for Collection Select
How to Pass Content to Jekyll Default Converter After Custom Conversion
Single Table Inheritance to Refer to a Child Class with Its Own Fields
How to Convert 1 to "First", 2 to "Second", and So On, in Ruby
Given a Url, How to Get Just the Domain
Ruby Equivalent for Python's For/Else
What Does a Single Splat/Asterisk in a Ruby Argument List Mean
How to Copy an Object with Presigned Url
Is It a Good Idea to Pass a Huge String as an Argument to Sidekiq Worker
Unescaping Characters in a String with Ruby
How to Install "Readline" for Rails Console
Ruby 'Require' Call Fails on Custom Code
Controller Method #Show Getting Called