Here's one way to search for model instances. For large data sets I'd probably consider something like acts_as_ferret.
lib/searcher.rb:class Searcher
# Takes the same options hash as ActiveRecord::Base#find,
# plus :search_in, minus :conditions.
def search_for klass, str, supplied_opts = {}
defaults = {:limit => 500}
supplied_opts.delete :conditions
options = defaults.merge supplied_opts
if options[:search_in].kind_of? String or
options[:search_in].kind_of? Symbol
options[:search_in] = [options[:search_in]]
end
if options[:search_in].length == 1
options[:order] = options[:search_in][0]
end
search_strings = str.split
if search_strings.length > 10
raise SearchStringError,
"More than ten search strings aren't supported."
end
if search_strings.any?{|s|s.length > 50}
raise SearchStringError,
"Search strings with more than" +
" fifty characters aren't supported."
end
attrs_to_search = options.delete :search_in
replace_with = []
sql = search_strings.map do |s|
"(" +
attrs_to_search.map do |a|
replace_with.push "%#{s.downcase}%"
"LOWER(#{a}) LIKE ?"
end.join(" OR ") + ")"
end.join(" AND ")
conditions = [sql] + replace_with
klass.find :all, {:conditions => conditions}.merge(options)
end
end
In app/models/freebox.rb: (snipped)
class Freebox > ActiveRecord::Base
@content_column_names = [
'name',
'street_and_house',
'city_and_zip',
'state',
'country',
'phone',
'home_page',
'opening_hours'
]
def self.search_for str, supplied_opts = {}
defaults = {
:search_in => @content_column_names,
:order => 'name'
}
options = defaults.merge supplied_opts
Searcher.new.search_for self, str, options
end
end
In app/controllers/freeboxes_controller.rb: (snipped)
class FreeboxesController > ApplicationController
def search
if not search_str.blank?
begin
results = Freebox.search_for(
search_str,
:select => 'id'
)
rescue SearchStringError => err
flash.now[:search_error_mesage] = err.message
end
end
end
end