Active Record find vs. find_by vs. where
Tags: rails, activerecord, beginners
Published: 2020-04-04 05:55:40

Data is power, and being able to query a database is powerful. I’ve been using Rails this past year building web applications, mostly as an API for my backend that gets used by my frontend. Rails is great for my needs and I find Active Record to be mostly intuitive when I need to retrieve objects from my database.

I would describe Active Record as the intermediary between your database and your application; it does all of the heavy lifting with SQL and will perform queries on the database for you. All you have to do is know what you're looking for, which finder method to use, pass in the appropriate arguments, and voila, your data will be served. Okay, this may be an oversimplification. 

![data gif](https://media.giphy.com/media/4FQMuOKR6zQRO/giphy.gif)

Yet, there are times I find myself deciding on which finder method to use, such as `find_by`, `find`, or `where`, and the different tradeoffs/considerations of one over the other.

###Retrieving a Single Object
There are several finder methods that return a single instance of the model. One of the most common methods includes `find`.

####find
The `find` method retrieves an object that matches the specified primary key (generally the id).** If a **record is not found, it will raise an `ActiveRecord::RecordNotFound` exception.**
```ruby
Model.find(id)

# record found
User.find(4) # => #<User id: 4, name: "Batman">

# record not found
User.find(53) # ActiveRecord::RecordNotFound (Couldn't find
# User with 'id'=53)
```
You can also **use `find` to query for multiple objects** by passing in multiple key arguments or an array of primary keys. It will **return an array containing all of the matched records** for the passed-in keys. However, **`find` will raise an `ActiveRecord::RecordNotFound` exception if not all of the records are found**.
```ruby
Model.find(id, id*)
Model.find([id, id])

# records found
User.find(1, 4) # => [#<User id: 1, name: "Spongebob">, 
#<User id: 4, name: "Batman">]

# records not found
User.find(2, 5) # ActiveRecord::RecordNotFound 
# (Couldn't find all Users with 'id': (2, 5) 
# (found 1 results, but was looking for 2).)
```

####find_by
Another way to locate a single model instance is `find_by`. The **`find_by` method returns the first record that matches a given condition.** The condition is key value pair argument of what you’re looking for. Optionally, you can pass in several arguments as well. If a **record is not found, `nil` is returned.** Say you want to find a user that has a pet cat, your Active Record query would like would: 
```ruby
Model.find_by(key: value)

# record found & single argument
User.find_by(pet: 'cat') # => #<User id: 1, pet: “cat”, location: "SF">

# record found & multiple arguments
User.find_by(pet: 'cat', location: 'NY') # => #<User id: 5, pet: "cat", 
# location: "NY">

# record not found
User.find_by(pet: 'iguana') # => nil
```


###Retrieving Multiple Objects
There are several methods to retrieve multiple objects, but I am only look at using `where` in this post.

####where

**`where` is a finder method that returns a collection of Active Record objects represented as an `ActiveRecord::Relation`.** Using `where` allows you to specify conditions that will filter the records returned. The conditions can be in the form of a string, array, or hash. If **nothing is found, an empty `ActiveRecord::Relation` object is returned (` => #<ActiveRecord::Relation []> `.** 

Below are the three ways the `where` conditions can be specified:
####Pure String Conditions
```ruby
# record(s) found
Entry.where("user_id = '1'") # => #<ActiveRecord::Relation [#<Entry id: 1, 
# user_id: 1>, #<Entry id: 2, user_id: 1>]...>

# record not found
Entry.where("user_id = '5'") # => #<ActiveRecord::Relation []>
```
Be careful when using pure strings, as it can leave you vulnerable to SQL injection attacks. Something like `Entry.where("name LIKE '%#{params[:name]%'")` is ill-advised because of argument safety. Having the variable directly in the conditions string will pass the variable to the database as-is, potentially putting your database at risk from unscrupulous users who can do very bad things.

####Array Conditions
```ruby
# single argument
Entry.where("user_id = ?", params[:users]) # => #<ActiveRecord::Relation # 
# [#<Entry id: 1, user_id: 1, completed: false>, #<Entry id: 2, 
# user_id: 1, completed: true>...]>

# multiple arguments
Entry.where("user_id = ? AND completed = ?", params[:users], true) # => 
# #<ActiveRecord::Relation [#<Entry id: 2, user_id: 1, completed: true>, 
# #<Entry id: 4, user_id: 1, completed: true>...]>
```
If the argument will be dynamic and vary, Active Record will take the first argument as the conditions string and additional arguments will replace the question marks `(?)` in it.

####Hash Conditions
```ruby
# single argument
Entry.where(user_id: 1) # => #<ActiveRecord::Relation #<Entry id: 1, 
# user_id: 1, completed: false>, #<Entry id: 2, user_id: 1, completed: 
# true>...]>

#multiple argument
Entry.where(user_id: 1, completed: true) # => #<ActiveRecord::
# Relation #<Entry id: 2, user_id: 1, completed: true>, #<Entry id: 4, 
# user_id: 1, completed: true>]>
```
Using hash conditions will generally make your code more readable. You can pass in the keys and values of the attributes you want to query on. 

###Method Chaining
A feature of using `where` is the ability to method chain. Method chaining is only allowed in a statement if the previous method returns an `ActiveRecord::Relation`, like `where` and `all`. Methods that only return a single object have to be at the end of the statement. 
```ruby
Entry.where(user_id: 1).find_by(completed: false) # => #<ActiveRecord::
# Relation #<Entry id: 1, user_id: 1, completed: false>, #<Entry id: 3, 
# user_id: 1, completed: false>...]>
```

This is just a small sampling of the power of Active Record. As you build your Active Record queries and decide which finder method to use, it will vary based on your needs. It also helps to keep in mind what each method returns, how it handles a match not found, and how many records you want. Accessing your data does not have to be a SQL-induced headache, and we have Active Record to thank for that.


Resources
[Rails Guides: Active Record Query Interface](https://guides.rubyonrails.org/active_record_querying.html#retrieving-objects-from-the-database)