Check if record does NOT exist in Rails (from array of ids)?
Asked Answered
B

8

22

I can do this to check if a record(s) exists (say id "1" exists, but "2" and "3" don't):

Model.exists?(:id => [1, 2, 3]) #=> true

How do I do the opposite, so:

Model.not_exists?(:id => [1, 2, 3]) #=> true
Bluecollar answered 1/12, 2010 at 16:28 Comment(0)
M
3

Edit: Please don't use this method. Even if it works it's not optimal since it loads all records instead of only testing their existence. This is a better way of doing it.


If you only need search records through ID you can try this

class Model
  def self.not_exists?(ids)
    self.find(ids)
    false
  rescue
    true
  end
end

If any of the IDs does not exist the find method will raise a ActiveRecord::RecordNotFound exception that we simply catch and return true.

Marilou answered 1/12, 2010 at 17:11 Comment(1)
This should not be the accepted answer, rescue as its used is dangerous and find will load the records where you only care at the existence of a raw. Answer belong should be the one acceptedBarnstorm
P
35

just add a ! operator

!Model.exists?(:id => [1, 2, 3]) #=> true
Pommel answered 1/12, 2010 at 16:36 Comment(2)
I'm asking how to say if 2 and 3 don't exist, how to make it return false. !Model.exists?(:id => [1, 2, 3]) returns false if 1 exists, whereas I want Model.not_exists?(:id => [1, 2, 3]) to return true if any don't exist.Bluecollar
Model.find(ids_ary).count then rescue ActiveRecord::RecordNotFoundHydrostat
B
5

Use empty?, this is what you want. It uses count(*) vs select 1 as one.

> Rocketeer.where(:id => [1, 2, 3]).empty?
   (0.6ms)  SELECT COUNT(*) FROM "rocketeers" WHERE "rocketeers"."id" IN (1, 2, 3)
=> false

> Rocketeer.where(:id => [1, 2, 3]).any?
   (0.5ms)  SELECT COUNT(*) FROM "rocketeers" WHERE "rocketeers"."id" IN (1, 2, 3)
=> true

> Rocketeer.where(:id => [1, 2, 3]).exists?
  Rocketeer Exists (0.5ms)  SELECT  1 AS one FROM "rocketeers" WHERE "rocketeers"."id" IN (1, 2, 3) LIMIT 1
=> true
Brufsky answered 29/5, 2015 at 13:51 Comment(2)
Isn't SELECT 1 AS one ... LIMIT 1 more efficient than SELECT COUNT(*)?Riebling
limit 1 is equal or faster than count(*). Although generally it'll be only perceptible with a combination of table size and index keys. Also, favor use of empty? to save DB RTT, if you'll use matching records.Pye
M
3

Edit: Please don't use this method. Even if it works it's not optimal since it loads all records instead of only testing their existence. This is a better way of doing it.


If you only need search records through ID you can try this

class Model
  def self.not_exists?(ids)
    self.find(ids)
    false
  rescue
    true
  end
end

If any of the IDs does not exist the find method will raise a ActiveRecord::RecordNotFound exception that we simply catch and return true.

Marilou answered 1/12, 2010 at 17:11 Comment(1)
This should not be the accepted answer, rescue as its used is dangerous and find will load the records where you only care at the existence of a raw. Answer belong should be the one acceptedBarnstorm
M
2

A more Ruby-esque way of doing it would be to use unless with exists?. This way, you don't have to use !. I imagine your use case is something like this:

def my_method
    return unless Model.exists?(:id => [1, 2, 3])

    # do something
end

You can replace 1, 2, 3 with a variable (call it id or something) and even remove the array altogether if you'd like: .exists?(id: id)

Mangrum answered 1/3, 2018 at 0:44 Comment(0)
G
1
class Model
  def self.does_not_exist?(ids)
    Model.where(id: ids).count < ids.size
  end
end

Explanation: If (and only if) all the instances you're looking for exist, Model.where(id: ids).count is equal to ids.size.

However if there are one or more instances missing, the count will be lower, meaning that there's a record that does not exist.

Golf answered 22/4, 2015 at 8:29 Comment(2)
Maybe with an explanation it's more helpfulUpstart
It answers the question but is slower performance for a large number of rows. It does a COUNT in SQL which counts every row in a table. where(id: [1, 2, 3]).exists? on the other hand does a SELECT... WHERE... LIMIT 1 which will return first row it finds. See @Brufsky 's answer.Binni
R
0

Another simple way is to use the where method with an array of id's.

# If the count of the query is equal to the count of all of the id's then the statement will return false.
# Else it will return true if not all ids exists in the database.
Model.where(id: [1, 2, 3]).count < [1,2,3].count
Ribbentrop answered 5/5, 2015 at 23:48 Comment(0)
C
0

To ensure all ids exist, not just one the following works without have to rescue

ids = [1,2,3]
Model.where(id: ids).count != ids.length

The exists? method will return true if any of the ids match. The above will ensure all ids exist.

Charlsiecharlton answered 30/3, 2022 at 18:10 Comment(0)
T
0

.none? returns true if there are no records.

Model.none?(id: [1, 2, 3])
Tannen answered 23/7 at 0:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.