← Home

empty?, blank?, any?, exists? methods of Ruby on Rails ActiveRecord

Ruby on Rails ActiveRecord provides a few methods for checking if a relation returns zero or more records.

empty? and blank?

Let's have a closer look at empty? and blank? methods to better understand what exactly they do and when they should be used. The docs say blank? returns true if relation is blank and empty? returns true if there are no records. Let's check the queries those methods generate in the Rails console to see what that means exactly:

User.where(admin: true).empty?
 (0.6ms)  SELECT COUNT(*) FROM `users` WHERE `users`.`admin` = 1
=> false
User.where(admin: true).blank?
  User Load (0.9ms)  SELECT `users`.* FROM `users` WHERE `users`.`admin` = 1
=> false

In this example empty? retrieves just the number of records that meet the specified condition, while blank? retrieves all those records. That means that empty? is faster than blank? in this example.

What if a relation is already loaded and we check if it is blank/empty.

admins = User.where(admin: true).load
User Load (1.0ms)  SELECT `users`.* FROM `users` WHERE `users`.`admin` = 1
admin.blank?
=> false
admin.empty?
=> false

In this example both methods rely on preloaded records in the relation rather than on the database queries. That means they should have similar performance.

Among these two methods empty? is a better choice to check if a not preloaded relation is not empty. If a relation is preloaded, empty? and blank? would be equally good choice.

any? and exists?

These methods use very different approaches to check if there are any records in a relation.

any? accept a block as a parameter. With a block it retrieves the records in the relation (unless they were preloaded), represents them as an array and then calls Enumerable#any? on it. Without a block this method is equivalent to !empty? and either retrieves the count of the records in the relation from the database or relies on the preloaded records.

exists? always queries the database and never relies on preloaded records:

User.where(admin: true).exists?
`User Exists (1.9ms)  SELECT  1 AS one FROM `users` WHERE `users`.`admin` = 1 LIMIT 1`

Only one record is retrieved and that makes this method fast compared to any? without a block. exists? also accepts various parameters and uses them to construct the SQL query.

If the block with for any? can be represented as a condition for exists? and the relation is not preloaded then exists? would show better performance. For example,

User.any? { |u| u.admin? }
User Load (2.7ms)  SELECT `users`.* FROM `users`
User.exists?(admin: true)
User Exists (0.4ms)  SELECT  1 AS one FROM `users` WHERE `users`.`admin` = 1 LIMIT 1

Summary

Let's look at the four methods again to find out what would be the fastest way of checking that, for example, there are no users with admin column set to true in users table?

User.where(admin: true).blank? would retrieve all the records that have admin equal to true and count them.

User.where(admin: true).empty? would retrieve the count of records that have admin equal to true.

!User.where(admin: true).any? would be exactly the same as the previous option.

!User.any? { |u| u.admin? } would retrieve all the records from users table and then check if at least one of them has admin? equal to true.

!User.exists?(admin: true) or !User.where(admin: true).exists? would retrieve the first record with admin set to true. That makes this approach fastest among those five.