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?

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!
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.
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 :)
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
@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.
@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
I only notice, that Thoughtbot wrote some plugin: http://giantrobots.thoughtbot.com/2008/10/14/life-in-the-fast-lane . Quite nice search
@ilan
Could You be more elaborate what do You mean by ‘substitution method’? Do You think about % method for string? Or something different?
I like searchgasm.
@Witold
Named scopes for rails < 2.0: http://www.rubyflow.com/items/991
Cheers :)
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.