Build a Simple Search with the simple_form Gem in Rails 5

Searching in Rails 5 is one of those things that always starts out simple “oh, I’ll just add a search bar to the site” and eventually ends up in a slew of PostgreSQL queries, tears, and eventually.. defeat. A search can be a basic filter of your model, but creating a search that is efficient, fast, and actually gets the results you want to show is much more complex. Think of how you felt last time you searched for something on Bing instead of Google. A good algorithm can make all the difference.


I’ve seen many search tutorials for Rails 5 that use form_tag, HTML forms, or form_for, but there are not as many resources for simple_form, even though simple_form has become the gold standard of Rails forms. Besides, both form_for and form_tag are on their way to be deprecated after Rails 5.2 and replaced by form_with. Here’s a quick overview of how you can add basic search functionality to your app using the simple_form gem!

Let’s get started!

1. Setup your Rails App

I’m creating a basic rails application, without Webpacker, but using PostgreSQL as my database, because it will allow me to do case-insensitive search later on.

rails new simple_search --database=postgresql 
rails db:create

p.s. Remember to create the database after you make your rails app because postgres doesn’t create a db by default

#Gemfile
gem 'simple_form'
gem 'bootstrap', '~> 4.2.1'
bundle install

p.s. Don’t forget the extra steps to setup Bootstrap & configure SimpleForm!

2. Create the model

For this case, I’m going to create a simple one-model app to search through a list of cocktails that I added to my database via a JSON endpoint (thanks to github.com/teijo for the db!). I’ll go through the data import quickly. If you want a full article on seeding from a third party database, leave a message below!

First create the model with three attributes:

rails g model cocktail name glass preparation:text
rails db:migrate

Then add the seeds:

#db/seeds.rb
require 'json'
require 'open-uri'
url = "https://raw.githubusercontent.com/maltyeva/iba-cocktails/master/recipes.json"
Cocktail.delete_all if Rails.env.development?
cocktails = JSON.parse(open(url).read)
cocktails.each do |cocktail|
Cocktail.create!(name: cocktail["name"], glass: cocktail["glass"], preparation: cocktail["preparation"])
end

3. Create the controller & views

Next we’re adding a controller with a single action, to show a list of all the cocktails. Since our app has one page, I’m going to root the application to the cocktails index. There is no /cocktails path in the application.

First, create the controller:

#terminal 
rails g controller cocktails index
#app/controllers/cocktails_controller.rb
class CocktailsController < ApplicationController
def index
@cocktails = Cocktail.all
end
end

Then, add a home route for the index:

#config/routes.rb 
root to: "cocktails#index"

Lastly, let’s add a simple front-end for the cocktail:

#app/views/cocktails/index.html.erb
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-8 offset-sm-2">
<h1>Cocktails</h1>
<% @cocktails.each do |cocktail| %>
<h3><%= cocktail.name %></h3>
<p><%= cocktail.preparation %></p>
<% end %>
</div>
</div>
</div>

4. Add the search form

Now it’s time to add the search functionality. I love simple_form because of.. well… its simplicity, because you can make a form in just four lines. Check out the full documentation here.

#app/views/cocktails/index.html.erb
<%= simple_form_for :search, url: root_path, method: "GET", html: { class: 'form-inline' } do |f| %>
<%= f.input :name, input_html: {value: ""} %>
<%= f.submit "Search", class: "btn btn-primary" %>
<% end %>

There’s a few things to watch out for here.

  1. The first thing is the :search . Typically, we create the form for an instance variable when manipulating a model (i.e. @cocktail) ). If I try to call the parameter @search, the application will tell me that the variable is undefined. By setting it as a symbol with the colon before :search, I can use it as a key in my params hash.
  2. The url of the search form tells us on which page the results will be displayed. Here I specified it as the root_path. The application only has one page (the root page of the cocktails index), so I need to tell my form to show the results on that page.
  3. By default, a simple_form has the method POST. Since our search is a GET request, we need to explicitly specify that in the first line.
  4. The input :name is going to be a key inside of my params hash. The value is the keyword the user searches for. In order to reset the form on page refresh, I am giving it a default value of blank.

5. Query your search in the controller

We’ll start with a super simple search — an exact keyword. This is a basic query, but not really useful, because it won’t find Margarita if my search phrase is margarita, or any part of the word.

But for now, let’s start with the basics:

class CocktailsController < ApplicationController
def index
@cocktails = Cocktail.all
@search = params["search"]
if @search.present?
@name = @search["name"]
@cocktails = Cocktail.where(name: @name)
end
end
end
The crappy search, in action

6. Let’s make this search better!

The first thing I want to do is make my search case-insensitive, so Margarita and margarita return the same result.

Here, my simple active record query won’t do and I’ll need to add SQL. One of the reasons I started the app with PostgreSQL as my db adapter is to use ILIKE, which will give me the case-insensitive match:

@cocktails = Cocktail.where("name ILIKE ?", @name)

What about a partial search? What if we want Margarita to return all margarita recipes like Passionfruit Margarita or Strawberry Margarita? We’ll need to add wildcard rules to make that work. Note that to add the % wildcard symbol, I need to interpolate my variable as a string.

@cocktails = Cocktail.where("name ILIKE ?", "%#{@name}%")
Tada! The working search

8. That’s it!

We’re done with our basic search. There’s lots of things I can add to this app, like searching through multiple columns, multiple tables, saving my search in the session, or using scopes to make the search more efficient. Let me know below if you’d like an article on advanced search!

GitHub Repository here.

Heroku Demo here.

Further Resources:

https://rubyplus.com/articles/3381-Simple-Search-Form-in-Rails-5

https://medium.freecodecamp.org/how-to-add-a-powerful-search-engine-to-your-rails-backend-57bced889032

Huge thank you to @dp_engel for the editing help~ 🤓

More Rails:

Simplify your front-end with BeautifyRuby and Rails 5

More Articles:

Time Management Techniques for Digital Nomads