J3RN
J3RN

Reputation: 1843

Differences between `any?` and `exists?` in Ruby on Rails?

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

Answers (4)

thisismydesign
thisismydesign

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

Adam
Adam

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

Sebasti&#225;n Palma
Sebasti&#225;n Palma

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

Josh Brody
Josh Brody

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

Related Questions