Search for Anything in Your Rails 6 Application

Search for Anything in Your Rails 6 Application

  • 44

Search for Anything in Your Rails 6 Application .With Rails 5+, ensuring the speed of a small-scale application is trivial. It’s easy to retrieve one object out of a small database using Active Record.

The key to building an application your users will love is simple — it must work well and it must be fast.

With Rails 5+, ensuring the speed of a small-scale application is trivial. It’s easy to retrieve one object out of a small database using Active Record. But how can an application handle scaling as the number of records grows into the hundreds of thousands?

The thing is — Ruby as a programming language is pretty slow. However, SQL queries are lightning-fast. Active Record bridges that gap by letting a developer query a database in Ruby to execute a search in SQL.

Active Record is one of the things that makes Rails such a powerful framework, because it allows a developer to write SQL without writing SQL. It’s part of the Rails magic and it is wonderful.

Until, that is, you begin looking at Active Record documentation and feel like you are back at square one.

In every single Ruby on Rails coding interview I’ve ever done, the first question has always been about Active Record. Active Record is one of the things that makes Rails such a powerful framework, because it allows a developer to write SQL without writing SQL.

So, here is a breakdown of how it happens:

  • Rails converts the Active Record request into an equivalent SQL query.
  • The SQL query retrieves the corresponding results from the database.
  • Rails instantiates the equivalent Ruby object of the appropriate model.
  • The applications run any relevant callbacks like after_find or after_initialize.

An Easy Place to Start Is Finding One Record in the Database

find() or find_by()

Because the ID is a primary key, the find method will always return one record, and if the record does not exist, it will return nil.

#returns the product with the id of 3
Product.find(3)
#SQL query
SELECT * FROM products WHERE (products.id = 3) LIMIT 1

To find multiple records, you can pass an array of IDs.

#returns an array of products containing IDs 1,3, and 5
Product.find([1,3,5])
#SQL query
SELECT * FROM products WHERE (products.id IN (1,3,5))

find_by is a bit more intricate. It allows you to search for a record using any attribute and will return the first matching record in the database.

#finds the first product matching the name Awesome Product
Product.find_by(name: "Awesome Product")
#finds the first product matching the author Maria 
Product.find_by(author: "Maria")
#finds the first product matching the name and author criteria
Product.find_by(name: "Awesome Product", author: "Maria")

Unlike the find method, find_by will not return nil if it doesn’t find a matching record, making it a safer option to use, to avoid running into an exception error.

How To Find Multiple Records

where

The where method is a good place to start. It returns an array of records that matches the condition.

Product.where(archived: true)
OR 
Product.where('archived' => true)
#SQL query 
SELECT * FROM products WHERE (products.archived = true)

You need to be careful when using the where method with a query that comes from params because it can often be a target for SQL injections. You must escape the string to avoid this type of attack.

DON'T DO THIS
Book.where("author = #{params[:author]}")
DO THIS
Book.where("author = ?", params[:author])

where.not

To get the inverse of the where search, use the where.not method to select objects from the database that do not match the given criteria.

Product.where.not(archived: true)
OR
Product.where.not('archived' => true)
#SQL query 
SELECT * FROM products WHERE (products.archived != true)

You can also use false in the above statements to get the opposite, but the where.not is a neat trick for more complex queries.

Search through multiple tables

Use a where query to search through multiple tables if they are connected via a has_many/belongs_to association.

Note that when using the joins statement, it refers to the table name of the association and not the model name. Use this when you are searching through the parent association.

Book.where(author: params[:author])
Author.joins(:books).where(books: { author: params[:author]})

Avoid N+1 queries

When searching through multiple records, it’s easy for Rails to add more and more queries with each SQL execution. Avoid the n+1 trap by eager-loading the associations.

Book.includes(:comments).where(comments: { visible: true })

That’s It!

These are the fundamentals of searching in a Rails application. Active Record is lightning fast, but there are still limits to what it can do.

I recommend using a gem like pg_search. There is more to filtering in Rails, including limit, ordering, grouping, etc. Let me know if you’d like an article on it in the future!