rails-style-guide
rails-style-guide copied to clipboard
Faster Rails: Use `exists` instead of `present?` to check if a Record Exists
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 theSELECT 1 ... LIMIT 1approach, which is very fast.
Also, prefer any? and empty? when checking for the existence of an association record without any scope.
...
any?andempty?will also produce a very optimized query that usesSELECT 1 FROM ... LIMIT 1form, butany?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:
Completely agree! Wanna open a PR?
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?
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 forexists?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. Preferexists?if you care only about the existence check.
@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.)
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 likepresent?if records are already loaded and behaves likeexists?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 likeblank?if records are already loaded and returns the inverse ofexists?if records aren't loaded.
Misc:
countwill always query the database with a COUNT query to check the amount of records.lengthwill load all the records (if not already loaded) and returns the collection size.sizebehaves likelengthif records are already loaded and behaves likecountif the records aren't loaded.one?usessizeto check the record count, returnstrueif there is exactly one record present.many?usessizeto check the record count, returnstrueif 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?.
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?.
@3limin4t0r Would you like to send a PR?
@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.
Sounds about right, thank you. Looking forward to it.