Creating a search

Hi there,

I spent a long time on the following "problem" and cant' figure it out on my own:

I need to have a search function and thought I could do it like this:

- the search page has a form which takes the search criteria (people *only* fill in the criteria they want to use for the search) - upon submission of the form, the search criteria goes into params[:search] (i.e. params[:search][:firstname], params[:search][:lastname], params[:search][:gender], etc.) - the controller takes the values from params[:search] and should generate/write the appropriate "conditions" for the search statement "User.find(:all, conditions => {...})" - But since submitting the search page/form with only *some* (not all) fields filled in (or check boxes checked, or radio buttons clicked, etc.) would generate some "empty" values in the key/value pairs in "params[:search]", the generated search statement would be wrong because for example a condition saying " 'gender' => nil/empty " could be added. - So, only key/value pairs which have a value that's not empty should generate the "condition" code.

Question: Where is the mistake in my code? If I do it like below, I get the error: "odd number list for Hash" (at line: see the comment in the code).

The code until now looks like this:

def search     @searchresults = ''     if request.post?         @searchresults = User.find(:all, :conditions => {             #the following should write the conditions like this: 'gender' => params[:search][:gender], etc...             params[:search].reject {|key, val| val.nil?}.map { |k, v| \"#{k.inspect} => #{v.inspect}\" }.join(\", \") # <= ERROR for this line         })     end end

Any help on this is really appreciated - thanks a lot! Tom

Tom Ha wrote:

def search     @searchresults = ''     if request.post?         @searchresults = User.find(:all, :conditions => {             #the following should write the conditions like this: 'gender' => params[:search][:gender], etc...             params[:search].reject {|key, val| val.nil?}.map { |k, v| \"#{k.inspect} => #{v.inspect}\" }.join(\", \") # <= ERROR for this line         })     end end

Any help on this is really appreciated - thanks a lot! Tom

def search   default_search_options = {:gender => "male or female", :car => "ferrari", :house => "< 1000000", :taste => "salty"}

  # now you have sensible defaults with user specified overrides.   # Rails does this itself in more places than I can count   search_criteria = default_search_options.merge params[:search]

  # more code here

end

hth

p.s. If the form you are developing needs to include Ferrari, can I join your company? :slight_smile:

ilan

Hmmm, I still don't seem to be able to make it work like this:

  def search       @searchresults = ''       if request.post?       default_search_options = {:gender => "female", :city => "Mycity"}       search_criteria = default_search_options.merge!(params[:search])           @searchresults = User.find(:all, :conditions => {               'gender' => search_criteria[:gender],               'city' => search_criteria[:city]           })       end   end

-> When I just click the "search" button (the "empty" form is submitted), the result shown is: all females in Mycity (correct) -> But when I click the radio button "male" and then submit the form, the result shown is still: all females in Mycity (wrong) and not "all males in Mycity" (correct) -> The data in "params[:search]" doesn't seem to merge into the hash "default_search_options", although the data definitely exists in the hash "params[:search]". -> Where's the mistake...?

The next question would concern the line: default_search_options = {:gender => "female", :city => "Mycity"} -> If I got you right, your idea is to say: the default_search_options defines the *entire* data in the db (meaning i.e. "males and femals in all cities") and default_search_options is then partially overridden by the params[:search] conditions (only by the ones that are provided). But how would I code "males and femals in all cities" in the default_search_options = {...}? As far as I know it's not possible to say: :gender => 'male', :gender => 'female', :city => 'Mycity', :city => 'Yourcity', :city => 'Whatevercity' - right? (and it would be kind of "not very practical"...)

Can you help me out?

Tom Ha wrote:

to say: :gender => 'male', :gender => 'female', :city => 'Mycity', :city => 'Yourcity', :city => 'Whatevercity' - right? (and it would be kind of "not very practical"...)

Can you help me out?

On Mar 8, 9:52 pm, Ilan Berci <rails-mailing-l...@andreas-s.net>

What if the user doesn't care about the gender of the search.. then you would want to include both genders and the query would go something like this: User.find(:all, :conditions => ["gender in '?', params[:gender].join(",")) where the default would be :gender => ['1', '0'] where 1 maps to female and 0 maps to male..

As for the merge not working, I don't understand why, you will need to debug.

1) sudo gem install ruby-debug 2) start mongrel as: script/server webrick -u 3) Insert a debugger statement at the top of the controller method 4) step through the program to see where the merge is failing..

p.s. since you are assigning the merge to a temporary.. you just need to call merge() and not merge!()

hth

looking forward to hearing more about your progress..

ilan

I'd suggest something quite different. Ilan's approach will work but it provides a default search rather than an open search crafted by the user. I do not get the sense that this is what you're after. Try something like this:

def search   search_criteria = params[:search].delete_if{|key, val| val.blank?}   if search_criteria.empty?     flash[:notice] = "Please enter search criteria"     @searchresults = ""   else     @searchresults = User.find(:all, :conditions=>search_critera, :order=>:login)   end

  respond_to do |wants|     ...   end end

The first line gets rid of any key in the :search hash that as a nil or blank so search_criteria ends up as a hash consisting only of keys with values. (Note: you could supply a different test in the delete_if block if blank is a valid search criteria). If your search only needs to exact matches (ie., no 'like' conditions in the sql for partial matches) then you can pass that hash to the find method without modification. The other advantage that it provides you, as shown, is that you can choose to handle the edge case where the user does not supply any search criteria. Returning _all_ users could kill your app (think big, millions of users! :wink: