#111 Advanced Search Form (revised)

It is often best to use a GET request when submitting a search form, however if it is an advanced search form with a lot of fields then this may not be ideal. Here I show how to create a search resource to handle this.

Way back in episode 37 we showed how you can add a simple search form to a page. This form allows users to type in a simple query into a text field and get a list of the matching products back.

The search form performs a GET request that calls the ProductsController’s index action, passing a search parameter to it. For most scenarios this is the best way to add searching to an application. Even if you want to have multiple search fields using a GET request for the search is a good approach.

There may be times though when you want to add more advanced searching to an application and present a complicated form to the user. In these cases there may be too many parameters and too much data to send in a GET request. An example of such a form is the advanced search page on the vBulletin forums. This form has a over a dozen different fields for submitting various search parameters, probably more than you’d want to submit via a GET request.

So how should we handle this kind of situation in a Rails application? The key is not to treat search as a simple request but as a separate resource that has a model and which is backed by a database.

Using a Search Model

To add advanced searching to our simple e-commerce application we’ll need to add a Search model and give it attributes to match the fields we want to have in our new advanced search form. We’re going to allow users to search by keyword, category and minimum and maximum price.

This form is fairly simple. It uses form_for on our @search object and has a text field for the keywords, a dropdown for the category, and two more text fields for the minimum and maximum price fields. We can now click on the advanced search link and view our new form.

We’ll need a create action to handle the user submitting the form. This will create a new Search record then redirect to its show page to show the results so we’ll write the show action now as well.

Our template uses a new products method on the Search model (we’ll write this shortly) that returns a list of the matching products and renders it. This renders a partial for each Product and we’ll write that partial now.

We’ve cached the results of our search in an instance variable so that if products is called multiple times the search won’t be performed more than once. The actual work has been delegated to a private method called find_products. The logic in this method is fairly specific to our application but it should be easy to modify for other searches. In this case it gets the products by name then adds where clauses for the name, category_id, min_price and max_price, if that parameter has been supplied by the user. Finally it returns the list of matching products.

We could, if we wanted to, make the form even fancier and give the user the option to specify the order in which the results should be presented and the maximum number of results that should be returned.

We can try out our form now. We’ll search for products called “catan” in the “Toys & Games” category with a minimum price of $10.

This brings back the two matching products.

Clearing Old Searches

One thing to be aware of when using this approach is that every advanced search made stores a record in the database. This means that the searches table can become very large so it’s a good idea to regularly clear out old searches. We can easily create a rake task to do this.

We need to make this task dependent on the Rails environment which we do by passing in the :environment option. We then delete any searches made over a month ago.

Whenever this rake task is run it will remove old searches but ideally we want it to run automatically via a cron job. The best way to do this is to use the Whenever gem to set this up as a daily task. This gem was covered back in episode 164.

That is for our episode on creating an advanced search form. One advantage of this approach is that as the search is stored in the database it’s easy to allow users to save searches and give them links to search they’ve made.