How to Build Your Own Custom Search in Ruby on Rails (With 5 Lines of Code)
Tags: rails, ruby, webdev, programming
Published: 2021-10-28 14:27:01

Search is a pretty easy feature to add to a Rails application.

You have a few options that make it straightforward to set up. You can go the route of installing gems or using services to handle it. Or you can just write a few lines of Ruby and customize it yourself. Today, we'll do the latter.

Here's how to build your own search functionality with Ruby on Rails.

---

## Querying the Database

First, our goal is to pull certain records from the database that match the user's search input.

Ruby on Rails doesn't actually have a module for search specifically. Active Record gives you a nice ORM to query data but doesn't have any helper methods to handle search. So we actually need to write a tiny bit of raw SQL and put that into Active Record's `where` method to make this work.

It should look something like this:

```ruby
BlogPost.where("title LIKE ?", "%#{search_term}%")
```

**Note:** you have to pass the search term as an <a href="https://testsuite.io/prevent-sql-injection-in-ruby-on-rails#array-conditions" target="_blank">array condition</a> like this to avoid <a href="https://testsuite.io/how-sql-injection-works" target="_blank">SQL injection attacks</a>.

This will fetch all BlogPost records with a title field that contains our search term.

The `LIKE` operator checks to see if a pattern exists, or in our case if a substring exists.

If the title field contains the substring, it evaluates to true and our where condition is met. So we return that given record in our query.

The `%` if you're curious represents any number of characters. This means our substring can be anywhere in the middle of the title field. Feel free to remove them if it better suits your use case.

If our term is "react", this query will result in SQL that looks something like this:

```sql
SELECT * FROM blog_posts WHERE title LIKE '%react%'
```

### Making Search Case-Insensitive to Ignore Capitalization

The last part of our query is optional, but the current implementation is case-sensitive.

This probably won't get us all the results we are expecting. But fear not, it's just as easy to ignore capitalization.

Here's an example:

```ruby
BlogPost.where("lower(title) LIKE ?", "%#{search_term.downcase}%")
```

The `lower()` method converts the field on the database side to lowercase.

Calling `search_term.downcase` converts our provided search term to lowercase also.

And there we have case insensitive search, where no matter the capitalization, we'll convert everything to lowercase and just look for the matching substring.

The SQL will look like this:

```sql
SELECT "blog_posts".* FROM "blog_posts" WHERE (LOWER(title) LIKE '%react%')
```

---

## Setting Up the Controller

We know the query we want to run to fetch our search results, now we just need the controller to do 2 things:
- tell us the search term
- return the results to the user

### Getting the Search Term

I like to keep my routing simple so I usually use query params or build a route specifically for this.

I'll set up a new route in my `config/routes.rb` file that looks something like this:

```ruby
get '/search/:search_term', to: 'testsuite#search`
```

This means route search traffic to my TestsuiteController's `search` method. 

Also, the `:search_term` in my route means the controller will be able to access that part of the URL by calling `params[:search_term]`.

My controller will need a short method to hook everything up:

```ruby
def search
  term = params[:search_term]
  @results = BlogPost.where("lower(title) LIKE ?", "%#{search_term.downcase}%")
end
```

This endpoint will return the results to our view, where you can access them through the `@results` variable.

And boom! When we go to `https://example.com/search/react` we navigate to the search method on the controller, pluck the search term from the URL and pass it to our query.

That's search in pretty much 5 lines of code.