Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
zoras opened this issue Aug 27, 2018 · 9 comments
Open

Comments

@zoras
Copy link

zoras commented Aug 27, 2018

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:

@KevinBongart
Copy link

Completely agree! Wanna open a PR?

@zoras
Copy link
Author

zoras commented Aug 28, 2018

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
Copy link
Author

zoras commented Aug 30, 2018

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.

@KevinBongart
Copy link

KevinBongart commented Aug 30, 2018

@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.)

@3limin4t0r
Copy link
Contributor

3limin4t0r commented Oct 1, 2018

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?.

@akimd
Copy link

akimd commented Nov 12, 2018

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?.

@pirj
Copy link
Member

pirj commented Feb 21, 2021

@3limin4t0r Would you like to send a PR?

@pirj pirj closed this as completed Feb 21, 2021
@pirj pirj reopened this Feb 21, 2021
@3limin4t0r
Copy link
Contributor

3limin4t0r commented Mar 9, 2021

@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.

@pirj
Copy link
Member

pirj commented Mar 9, 2021

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants