# Creating a search

**URL:** <https://www.ruby-forum.com/t/creating-a-search/131366>\
**Category:** Rails\
**Created:** [March 8, 2008, 9:41pm UTC](https://www.ruby-forum.com/t/creating-a-search/131366 "2008-03-08T21:41:37Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![tharrisx](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/e19adc/32.png) [@tharrisx](https://www.ruby-forum.com/u/tharrisx)\
**Post date:** [March 8, 2008, 9:41pm UTC](https://www.ruby-forum.com/t/creating-a-search/131366/1 "2008-03-08T21:41:37Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![tharrisx](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/e19adc/32.png) [@tharrisx](https://www.ruby-forum.com/u/tharrisx)\
**Post date:** [March 8, 2008, 9:52pm UTC](https://www.ruby-forum.com/t/creating-a-search/131366/2 "2008-03-08T21:52:26Z")

</div>

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? 🙂

ilan

---

<div class="post-metadata">

**Author:** ![tharrisx](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/e19adc/32.png) [@tharrisx](https://www.ruby-forum.com/u/tharrisx)\
**Post date:** [March 8, 2008, 11:53pm UTC](https://www.ruby-forum.com/t/creating-a-search/131366/3 "2008-03-08T23:53:44Z")

</div>

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?

On Mar 8, 9:52 pm, Ilan B. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)

---

<div class="post-metadata">

**Author:** ![tharrisx](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/e19adc/32.png) [@tharrisx](https://www.ruby-forum.com/u/tharrisx)\
**Post date:** [March 10, 2008, 3:15am UTC](https://www.ruby-forum.com/t/creating-a-search/131366/4 "2008-03-10T03:15:25Z")

</div>

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 B. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)

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

---

<div class="post-metadata">

**Author:** ![tharrisx](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/e19adc/32.png) [@tharrisx](https://www.ruby-forum.com/u/tharrisx)\
**Post date:** [March 10, 2008, 1:31pm UTC](https://www.ruby-forum.com/t/creating-a-search/131366/5 "2008-03-10T13:31:16Z")

</div>

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! 😉
