Simple search in Rails applications

Almost every application does need search feature. Even if not direct feature You need some efficient way to narrow data selection. And I do like to have clean and compact code :) so I have spent some time to create my own search schema which I’d like to share with You.

First – what I’m talking about? Let’s assume You need to create some kind of report and has to provide several options to narrow down scope of entries. Since Active Record requires You to create part of SQL query in :conditions it is easy to create some not nice looking code with string joining, wondering if You should add AND operator and so on.

In most cases You don’t need full blown search capabilities (like with Ferret) and You want to keep Your dependencies list as short as possible.

How to search?

Right tools when You need to see some details
Right tools when You need to see some details (c) Wrote

I came up with following code:

conditions = ["1=1"]
cond_data = []
includes = [:association_used_for_display]


unless params[:condition_1].blank?
  conditions << "field_1 = ?"
  cond_data << params[:condition_1]
end

unless params[:condition_2].blank?
  conditions << "other_table.field_2 = ?"
  cond_data << params[:condition_2]
  includes << :some_association #for other_table
end

unless params[:condition_3].blank?
  conditions << "one_more_table.field_3 like ?"
  cond_data << "#{params[:field_3]}%"
  includes << :one_more_association #to get one_more_table
end

  
@results = Model.find(
  :all,
  :conditions => [ conditions.join(" and "), *cond_data ],
  :include => includes)

The idea is to keep array conditions with entries to create :conditions option and cond_data array to keep arguments to substitute all question marks.

With includes we can add associations we need to load when criteria need it. We can preset some associations to force load some data always needed for view creation (association_used_for_display in this example).

Using this schema allows easilly add new criteria – it just another unless statement.

So what do You think about it?

I’m curious what do You think about this approach – or You do have some own solutions? I’m waiting for Your comments!


Comments

10 responses to “Simple search in Rails applications”

  1. I used this solution for RoR 1.2.x. In solution you present I have problems with dates ex: transactions between two dates. For Ror 2.x I prefer to use special model for search eg. Search and named_scopes. I plan to describe it in my blog soon.

  2. I was playing with such code many times and was writing some methods to easily add conditions, no mather if conditions is passed as string or as hash.

    But now these days are gone – as Seban said, now the rails way to do it are named_scopes. Look at Ryan Bates’ scope builder: http://github.com/ryanb/scope-builder/tree/master – it makes it even prettier :)

  3. ilan berci Avatar
    ilan berci

    first of all, you are opening up yourself to sql injection attacks as you are not sanitizing the params before placing them in the sql.

    Secondly, as already mentioned, named scopes are the way to go here as they can be concactenated.

    named_scope(:older_than, do |age|
    {
    :conditions => [“users.age > :age”, {:age => age}]
    }
    end)

    named_scope(:taller_than, do |height|
    {
    :conditions => [“users.height > :height”, {:height => height}]
    }
    end)

    User.older_than(35).taller_than(4)

    ilan

  4. @ilan
    First of all – where do You see vulnerability to SQL injection? All parameters to query go through Rails sanitizing with ? placeholder

    Second – named_scope is available in Rails 2.1. For older versions You need to deal with search on Your own. And I have to deal on daily basis with versions ‘before’ 2.1.

  5. ilan berci Avatar
    ilan berci

    @Witold,
    I didn’t see any place in your original text where you stipulated the constraint that you were working with rails versions prior to 2.1. If this is the case, then I would just yank out the 2.1 stuff in named scopes and stick it in your 1.2/2.0 rails libs. It would be a far better approach than reinventing the wheel..

    As for injecting raw values into strings, yes you are correct (I see now that you are passing it in as a conditions) but it is still recommended to use the substitution method as it is far more maintainable since rails will do the type conversion for you.. and they had this way before 1.2..

    ilan

  6. I only notice, that Thoughtbot wrote some plugin: http://giantrobots.thoughtbot.com/2008/10/14/life-in-the-fast-lane . Quite nice search

  7. @ilan
    Could You be more elaborate what do You mean by ‘substitution method’? Do You think about % method for string? Or something different?

  8. @Witold

    Named scopes for rails < 2.0: http://www.rubyflow.com/items/991

    Cheers :)

  9. If you’re working with Rails pre 2.1 you can use has_finder plugin. Has_finder in fact became named_scope when it was merged into 2.1.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.