rails-style-guide icon indicating copy to clipboard operation
rails-style-guide copied to clipboard

Faster Rails: Use `exists` instead of `present?` to check if a Record Exists

Open zoras opened this issue 7 years ago • 9 comments

Prefer exists?instead of present? for checking record according to this blog post.

... exists?, is even more optimized, and it should be your first choice when checking the existence of a record. It uses the SELECT 1 ... LIMIT 1 approach, which is very fast.

Also, prefer any? and empty? when checking for the existence of an association record without any scope.

... any? and empty? will also produce a very optimized query that uses SELECT 1 FROM ... LIMIT 1 form, but any? will not hit the database again if the records are already loaded into memory.

This makes any? faster by one whole database call when the records are already loaded into memory:

zoras avatar Aug 27 '18 06:08 zoras

Completely agree! Wanna open a PR?

KevinBongart avatar Aug 28 '18 11:08 KevinBongart

Ya sure! Can you guide me where I've to make the changes? Didn't know that https://github.com/bbatsov/rubocop & https://github.com/bbatsov/ruby-style-guide are now under https://github.com/rubocop-hq. When did this happen? Any blog posts?

zoras avatar Aug 28 '18 12:08 zoras

Here's another one from a performance blog post

... if we are performing an existence check on records that we need as part of the application logic then we should use blank? which results in hitting the database once for the select query. But if we opted for exists? we will end up by hitting the database twice, the first query to perform the check and the second one to fetch the data.

Use blank? if you need to use the records in the application logic. Prefer exists? if you care only about the existence check.

zoras avatar Aug 30 '18 06:08 zoras

@zoras I think the ActiveRecord Queries section of the README would be a good place to add a bullet point about exists? vs. present? and any? vs. empty?. Click here to edit the file directly, which will result in a pull request with your changes.

(The Rubocop repo is more about the tool that can detect rule violations and enforce some of the rules, and the Ruby Style Guide is specific to Ruby whereas the Rails Style Guide includes the ActiveRecord framework.)

KevinBongart avatar Aug 30 '18 12:08 KevinBongart

Quickly summarizing the methods.

Check for one or more records:

  • present? will load all the records (if not already loaded) and check if there is at least one record present.
  • exists? will always query the database to check if at least one record is present.
  • any? behaves like present? if records are already loaded and behaves like exists? if the records aren't loaded.

Check for no records:

  • blank? will load all the records (if not already loaded) and check if there are no records present.
  • empty?, none? behaves like blank? if records are already loaded and returns the inverse of exists? if records aren't loaded.

Misc:

  • count will always query the database with a COUNT query to check the amount of records.
  • length will load all the records (if not already loaded) and returns the collection size.
  • size behaves like length if records are already loaded and behaves like count if the records aren't loaded.
  • one? uses size to check the record count, returns true if there is exactly one record present.
  • many? uses size to check the record count, returns true if there is more than one record.

Note: The methods above are assumed without block. When providing a block to any? or none? the method is passed forward to Enumerable. Changing the behaviour to match present? and blank?.

3limin4t0r avatar Oct 01 '18 09:10 3limin4t0r

Somewhat unrelated note: it is unfortunate that the method is named exists?, which is in contradiction with the conventions of Ruby, and adds mental burden given that Rubocop complains about File.exists?, deprecated in favor of File.exist?.

akimd avatar Nov 12 '18 14:11 akimd

@3limin4t0r Would you like to send a PR?

pirj avatar Feb 21 '21 03:02 pirj

@pirj I wouldn't know what to really say in a "style guide" about this. I feel like this is not as much about style, since one method is not clearly better then the other. This is more about selecting the right tool for the job. Knowing what tools you have available to you is always a good practice.

Although now I think about it, there is a case to be made to recommend any?, none? (empty?) and size as generally good. You only use exists? and count if you specifically want to check the database, while the collection is loaded. And the use of present?, blank? and length as a slight optimisation if you need to check existence/count before actually using the record.

I might try to write a PR, and see if I can give some well formulated general advise.

3limin4t0r avatar Mar 09 '21 12:03 3limin4t0r

Sounds about right, thank you. Looking forward to it.

pirj avatar Mar 09 '21 15:03 pirj