How to Build Your Own Custom Search in Ruby on Rails (With 5 Lines of Code)
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:
BlogPost.where("title LIKE ?", "%#{search_term}%")
Note: you have to pass the search term as an array condition like this to avoid SQL injection attacks.
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:
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:
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:
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:
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:
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.