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: