Say you have a blog and you want the user to be able to search your posts, you will need to add methods to both your posts controller and post model, and create a corresponding search form.
1. Posts Controller
In your posts_controller.rb file, add the following to your index method:
def index @posts = Post.all if params[:search] @posts = Post.search(params[:search]).order("created_at DESC") else @posts = Post.all.order('created_at DESC') end end
2. Post Model
In your post.rb file, add the following method:
def self.search(search) where("name LIKE ?", "%#{search}%") where("content LIKE ?", "%#{search}%") end
You can choose the fields you’d like your search form to query. In the above example, the search term will be run against both the name field and the content field of each post. If the search term appears in either of those fields, there will be a match.
Note: this will work in development with SQLite. If you switch to PostgreSQL in production, you may need to change LIKE to ILIKE.
3. Search Form
In your views/posts/index.html.erb (or wherever you’d like to place the search form), add the following simple form:
<%= form_tag(posts_path, :method => "get", id: "search-form") do %> <%= text_field_tag :search, params[:search], placeholder: "Search Posts" %> <%= submit_tag "Search" %> <% end %>
4. Render Results
In addition to rendering search results, it’s nice to let the user know when there are no matching results. You can accomplish this by adding the following under the above form:
<% if @posts.present? %> <%= render @posts %> <% else %> <p>There are no posts containing the term(s) <%= params[:search] %>.</p> <% end %>
In the above method, you are instructing your program to display the posts if there are matching posts (the above code calls a partial _post.html.erb which actually renders the results), or let the user know there are no posts containing the searched terms.
- PM Career Story - April 28, 2022
- How to Transition into Product Management - December 26, 2017
- What I’ve Learned in My First Few Months as a Product Manager - October 14, 2015
Cayo Medeiros (yogodoshi) says
Hi Karen, nice tutorial!
Just a single observation: there are two form tags on your views/posts/index.html.erb code, you can remove the tag leaving only the helper, right? =)
Koren Leslie Cohen says
Good catch – totally unnecessary! (Just removed)
Cayo Medeiros (yogodoshi) says
*koren, sorry! =s
Can you please edit my mistake on the previous comment? =(
Koren Leslie Cohen says
No worries! 😉
Keith says
The search method should concatenate the results or use an ‘or’ query. In Rails 4.2, it will only return results from the last where statement. Matches in the first statement are not returned.
Koren Leslie Cohen says
Thanks, Keith! This is just a simple search, but I’m sure readers will find this useful when creating their own searches.
Richard says
some reason I had to do this to get it working, anyways thanks for the help
where(“content LIKE ? OR name LIKE ?”, “%#{search}%”, “%#{search}%”)
Sharon says
Thanks for this Koren! Also thanks Richard for your solution – I was very puzzled as to why only one of my where statements would work, and varying the order of the where statements changed which one would work.
Koren Leslie Cohen says
Glad you got it working!
Alex says
For some reason when I paste the Render Results, it gives me this error.
“SQLite3::SQLException: no such column: content: SELECT “posts”.* FROM “posts” WHERE (content LIKE ‘%ok%’) ORDER BY created_at DESC
There are no posts containing the term(s) .”
And it highlights the “if posts.present?” part
Sérgio Toledo says
Hi,
Nice tutorial!
I think you don’t need line 2 “@posts = Post.all” in your posts_controller.rb.
Regards.
Ott says
Thanks for highlighting the list() meohtd. I had been pulling my hair trying to access parameters sent from jquery having names of the form “parameterName[]”. The list() meohtd turned out to be what the doctor ordered.
Edwin says
Yup, completely unnecessary to perform a full search for all Posts before checking if the search parameter is present. It ends up looking for posts twice whether search is present or not.
oliver yoo says
Hi, Koren!!
I’m very grateful for your tutorial!
Thanks!
Eduardo says
Koren, very good. But, in the controller, maybe the first ‘@posts = Post.all’ could be removed, isn’t it?
Brandon says
Good stuff, though I believe that the method should be “where(“name || content LIKE ?”, “%#{search}%”)”, or else the method is just going to return results for the content search?
Jonni says
Wow, most useful. Can’t rembmeer how many times I’ve written something like this to handle checkbox/multiple select logic. Thank you very much and keep up the good work. 🙂
dyo says
Hi Koren how about if I have category_name from another table: category, how I write this on the model
def self.search(search)
where(“name LIKE ?”, “%#{search}%”)
where(“category_name LIKE ?”, “%#{search}%”) ==>>
end
Eddie Campain says
This is a very helpful post! I have a question though. How would I make the search bar work if I am looking for attributes across multiple models? Your example is dealing only with the Post model, however, how would you be able to have the search bar work for another model as well, say for example I had a User model and I wanted the search bar to be able to also handle searches for user names ? Thanks 🙂
Rebecca says
Did anyone ever figure this out? I’m also curios about the solution.
Jim says
For any other copy-paste demons out there, when copying Brandon’s “where(“name || content LIKE ?”, “%#{search}%”)”the double-quotes turned into double apostrophes or whatever so look out for that. Fixed that and now I can search on either name or zip in my rails app.I could not get Richard’s selector to work.
Bikram says
Awesome tutorial .:-)
Can we get the same tutorial on live search.
Recep İnanç says
Hi Karen,
I like your tutorials! But wouldn’t this be open to sql injections?
fcoders says
hi
can i make a search to search for tracking number to track orders just like ups
Joshua Bartlett says
Hi Koren! Thanks for this (and for your work at DSC, y’all have the best UX Design I’ve ever encountered, I’ll be a member forever)! Here’s an odd bug I’m encountering:
I’ve retooled this for my list of customers. If I pass through a list of parameters, ie email, phone, lname, fname, etc., and a user is missing data for any of those parameters, it prevents them from being returned. So, for example, I have a customer named John Doe. If John Doe’s profile on the site is missing an email address, then a search for John or Doe won’t return him if one of the parameters is email.
Here’s the code:
models/customer.rb
def self.search(search)
where(“phone || lastName || email LIKE ?”, “%#{search}%”)
end
controllers/customer_controller.rb:
def index
if params[:search]
@customers = Customer.search(params[:search]).order(“created_at DESC”)
else
@customers = Customer.all
end
end
Any thoughts?
kat says
You could make methods for phone, lastName, and email that check for the presence of those attributes. If they’re not present, return a dummy string instead.
This probably won’t work exactly as written but I hope it illustrates the idea.
“`
models/customer.rb
def self.search(search)
where(“phone || lastName || email LIKE ?”, “%#{search}%”)
end
private
def email
if email.present?
email
else
‘fakeemail@fake.com’
end
end
//could maybe be refactored to this? not sure…
def email
email ||= ‘fakeemail@fake.com’
end
“`
Antonio nicasio says
thank you very helpful
Kasper says
Perfect! Thank you for providing this.
Chee Ng says
Very good and thank you!
David says
returning ‘Post.all’ won’t be a good idea for most real world apps.
Simon says
In the controller, you can drop the line 2. For the model, just reuse this code:
class Note < ApplicationRecord
def self.search(search)
where("content LIKE ? OR title LIKE ?", "%#{search}%", "%#{search}%")
end
end
By the way, thanks for the post and to the commenters. Got my search engine to work. +1