Reputation: 1843
In Ruby on Rails, there appear to be two methods to check whether a collection has any elements in it.
Namely, they are ActiveRecord::FinderMethods’ exists? and ActiveRecord::Relation’s any?. Running these in a generic query (Foo.first.bars.exists?
and Foo.first.bars.any?
) generated equivalent SQL. Is there any reason to use one over the other?
Upvotes: 24
Views: 27161
Reputation: 25102
I ran into a practical issue: exists?
forces a DB query while any?
doesn't.
user = User.new
user.skills = [Skill.new]
user.skills.any?
# => true
user.skills.exists?
# => false
Consider having factories and a before_create
hook:
class User < ActiveRecord::Base
has_many :skills
before_create :ensure_skills
def ensure_skills
# Don't want users without skills
errors.add(:skills, :invalid) if !skills.exists?
end
end
FactoryBot.define do
factory :user do
skills { [association(:skill)] }
end
end
create(:user)
will fail, because at the time of before_create
skills are not yet persisted. Using .any?
will solve this.
Upvotes: 3
Reputation: 91
The answers here are all based on very outdated versions. This commit from 2016 / ActiveRecord 5.1 changes empty?
, which is called by any?
when no block is passed, to call exists?
when not preloaded. So in vaguely-modern Rails, the only difference when no block is passed is a few extra method calls and negations, and ignoring preloaded results.
Upvotes: 9
Reputation: 33430
The use of ActiveRecord#any?
is reduced over ActiveRecord#exists?
. With any? you can check, in the case of passing a block, if certain elements in that array matches the criteria. Similar to the Enumerable#any?
but don't confuse them.
The ActiveRecord#any?
implements the Enumerable#any?
inside the logic of its definition, by converting the Relation accessed to an array in case a block has been passed to it and yields and access the block parameters to implement in a "hand-made" way a "Ruby" any?
method.
The handy else added is intended to return the negation of empty? applied to the Relation. That's why you can check in both ways if a model has or no records in it, like:
User.count # 0
User.any? # false
# SELECT 1 AS one FROM "users" LIMIT ? [["LIMIT", 1]]
User.exists? # false
# SELECT 1 AS one FROM "users" LIMIT ? [["LIMIT", 1]]
You could also check in the "any?" way, if some record attribute has a specific value:
Foo.any? { |foo| foo.title == 'foo' } # SELECT "posts".* FROM "posts"
Or to save "efficiency" by using exists? and improve your query and lines of code:
Foo.exists?(title: 'foo') # SELECT 1 AS one FROM "posts" WHERE "posts"."title" = ? LIMIT ? [["title", "foo"], ["LIMIT", 1]]
ActiveRecord#exists?
offers many implementations and is intended to work in a SQL level, rather than any?
, that anyways will convert the Relation what you're working with in an array if you don't pass a block.
Upvotes: 10
Reputation: 5363
#any
and #exists?
are very different beasts but query similarly.
Mainly, #any?
accepts a block — and with this block it retrieves the records in the relation, calls #to_a
, calls the block, and then hits it with Enumerable#any?
. Without a block, it's the equivalent to !empty?
and counts the records of the relation.
#exists?
always queries the database and never relies on preloaded records, and sets a LIMIT of 1. It's much more performant vs #any?
. #exists?
also accepts an options param as conditions to apply as you can see in the docs.
Upvotes: 38