# How do we 'Search' in a RESTful world?

**URL:** <https://www.ruby-forum.com/t/how-do-we-search-in-a-restful-world/103941>\
**Category:** Rails\
**Created:** [May 11, 2007, 8:25pm UTC](https://www.ruby-forum.com/t/how-do-we-search-in-a-restful-world/103941 "2007-05-11T20:25:12Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![bravedave](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/46a35a/32.png) [@bravedave](https://www.ruby-forum.com/u/bravedave)\
**Post date:** [May 11, 2007, 8:25pm UTC](https://www.ruby-forum.com/t/how-do-we-search-in-a-restful-world/103941/1 "2007-05-11T20:25:12Z")

</div>

Dear RubyReportist’s  
I’ve been beating up the Rails web forums for the last week trying to  
understand how ‘mature’ Rails web developers’ elect to allow their  
users to ‘mince’ the data.  
We all know that a normal INDEX action in Rails usually does a  
Table.find(:all) and there we show the user a list (page by page) of  
the ‘whole show’.  
Now, what happen’s when the user wants to just see a part of it, or  
sort by a field, or select a result set a group of separate fields?  
I’ve seen code like this:  
def self.search(town, beds, min\_price, max\_price)  
conditions = []  
conditions \<\< [‘town = ?’, town] if town  
conditions \<\< [‘beds = ?’, beds] if beds  
conditions \<\< [‘price \>= ?’, min\_price] if min\_price  
conditions \<\< [‘price \<= ?’, max\_price] if max\_price  
find(:all, :conditions =\> [conditions.transpose.first.join(’ and '),  
\*conditions.transpose.last])  
But not found anyone that can tie it all together. Are there any  
people in this group that would be willing to consult to show me a  
simple example of how this could be done?  
David K.  
removed\_email\_address@domain.invalid  
775-885-9125

---

<div class="post-metadata">

**Author:** ![bravedave](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/46a35a/32.png) [@bravedave](https://www.ruby-forum.com/u/bravedave)\
**Post date:** [May 11, 2007, 9:29pm UTC](https://www.ruby-forum.com/t/how-do-we-search-in-a-restful-world/103941/2 "2007-05-11T21:29:18Z")

</div>

On 5/11/07, BraveDave [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> I’ve seen code like this:  
> simple example of how this could be done?  
> David,

First off, I like your approach here, I may implement it myself. But  
to answer your question as in how I’ve done it. I needed to be able to  
search within associated tables of has\_one, has\_many and HABTM all  
within the same search. I also needed to pass additional filters, that  
changed the semantics of the search. I have a metaprogramming based  
approach to this. First, I’ve created a master search class that  
defines some class level methods and some instance methods like  
to\_conditions, to\_includes, to\_querystring etc. Each form’s controller  
derives a subclass from that, assigning fields matching those on the  
form.

E.g. In my controller:

class HotelController  
class HotelSearcher \< MasterSearcher  
attr\_accessor :town, :beds, :price # these are accessable to the  
form  
end

def index  
hotel\_searcher = HotelSearcher.new  
end  
def search  
hotel\_searcher = HotelSearcher.new(params[:hotel\_searcher])  
Hotels.find, :all, :conditions =\> hotel\_seacher.to\_conditions #  
here be majik  
end

In the view, just do a form\_for(@hotel\_searcher) or use a FormBuilder,  
etc.

To get fancy:  
class HotelSearcher \< MasterSearcher  
attr\_accessor :town, :beds, :price  
fk\_accessor :state, :id, :alias =\> :state\_id  
ignored :filter, :default =\> ‘all’  
end

The fk\_accessor uses the model and the field to search on. An optional  
alias can be used for things like id’s in select lists where the  
options are from a lookup table. (They are all called ‘id’ and you can  
only have one of the same name.) There is an additional #to\_includes  
that gathers the correct include =\> [] array.

The ignored works for form fields that should not participate in the  
conditions clause, but modify the filter in some way (like order or  
something else.) You can customize the to\_conditions by  
def to\_conditions  
super do |conditions|  
# your code goes here  
end  
end

HTH,  
Ed

> David K.  
> removed\_email\_address@domain.invalid  
> 775-885-9125
> 
> >

–  
Ed Howland

> **[Green Programming](http://greenprogrammer.blogspot.com)**
>
> Environmentally safe code

---

<div class="post-metadata">

**Author:** ![bravedave](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/46a35a/32.png) [@bravedave](https://www.ruby-forum.com/u/bravedave)\
**Post date:** [May 12, 2007, 5:19pm UTC](https://www.ruby-forum.com/t/how-do-we-search-in-a-restful-world/103941/3 "2007-05-12T17:19:56Z")

</div>

Ed,  
I feel like I’ve got a tiger by tail here, and am struggling to keep  
up! I’ve read this post many times and wonder if you’d help me sort it  
out?

> First, I’ve created a master search class that defines some class level methods and some instance methods like to\_conditions, to\_includes, to\_querystring etc. Each form’s controller  
> derives a subclass from that, assigning fields matching those on the  
> form.

What does this ‘search class’ inherit from? Is it like a ‘login’ table  
that has no AR backbone? Did you put it in the app/models folder with  
a name like mother\_of\_all\_query\_parameters.rb? What blows me away is  
that you use the ‘form\_for’ helper and (from what I’ve read) this  
supports ONE AR model table?

So, if you’ll let me try to tell the story here…it goes like this:  
Your ‘MasterSearcher’ class is designed to hold stuff like  
query :conditions :filters :sort\_by, etc. and is uniquely configured  
for each ‘usage’ by adding the correct attr\_accessor fields that are  
appropriate for the model or combination of models being searched.  
When the user clicks on SEARCH the search.controller.index action  
fires and presents the index.rhtml where the user fills in search  
criteria using the form\_for helper. When the user hits the SUBMIT  
button it fires the SEARCH action that then delivers the search.rhtml  
to show the user the ‘goodies’ they wanted. I can’t see where the  
@hotel is that you pass to ‘feed’ this view?

Could you combine your SEARCH action logic into your INDEX logic  
something like this;  
def index  
if the params[:hotel\_searcher]) are nil  
MasterTable.find(:all)  
else  
do that voodoo that you do so well  
end  
end  
I’m so new and impressionable to Rails, that I’m striving to be a  
‘good boy’ and only use the 7 CRUD actions.  
Thank you,  
David

---

<div class="post-metadata">

**Author:** ![bravedave](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/46a35a/32.png) [@bravedave](https://www.ruby-forum.com/u/bravedave)\
**Post date:** [May 14, 2007, 3:13pm UTC](https://www.ruby-forum.com/t/how-do-we-search-in-a-restful-world/103941/4 "2007-05-14T15:13:14Z")

</div>

I’m not sure that this answers your question at all, but there is a nice  
plugin that helps with searching AR records without the fulltext  
searching.  
EZWhere by Ezra, You can start looking at it on his blog at  
[http://brainspl.at/articles/2006/10/03/nested-joins-and-ez-where-update](http://brainspl.at/articles/2006/10/03/nested-joins-and-ez-where-update)

HTH  
Daniel

---

<div class="post-metadata">

**Author:** ![bravedave](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/46a35a/32.png) [@bravedave](https://www.ruby-forum.com/u/bravedave)\
**Post date:** [May 14, 2007, 2:40pm UTC](https://www.ruby-forum.com/t/how-do-we-search-in-a-restful-world/103941/5 "2007-05-14T14:40:08Z")

</div>

On 5/12/07, BraveDave [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> that has no AR backbone? Did you put it in the app/models folder with  
> a name like mother\_of\_all\_query\_parameters.rb? What blows me away is  
> that you use the ‘form\_for’ helper and (from what I’ve read) this  
> supports ONE AR model table?

I actually put it in a module in app/helpers called SearchHelper and a  
class called SearchForm. So in reality my code in the controller looks  
more like this:  
class HotelSearcher \< SearchHelper::SearchForm.  
I did it this way to be able to run RSpec tests (which has a  
convenient spec:helpers Rake task. Also, it is visible to the  
controller and the view.

The form\_for doesn’t care about AR. The object just has to be  
initialized in the controller and respond to method calls for the  
fields. That’s why I used att\_accessor to create them in the class def  
in the controller.

> So, if you’ll let me try to tell the story here…it goes like this:  
> Your ‘MasterSearcher’ class is designed to hold stuff like  
> query :conditions :filters :sort\_by, etc. and is uniquely configured  
> for each ‘usage’ by adding the correct attr\_accessor fields that are  
> appropriate for the model or combination of models being searched.

It actually doesn’t hold the sort by but you could do that. The reason  
I didn’t was because I am using the OpenRico DB grid and it handles  
(column) sorting via JavaScript and Ajax call backs. I just get the  
conditions from the derived SearchForm and combine it with the stuff  
from OpenRico.

> When the user clicks on SEARCH the search.controller.index action  
> fires and presents the index.rhtml where the user fills in search  
> criteria using the form\_for helper. When the user hits the SUBMIT  
> button it fires the SEARCH action that then delivers the search.rhtml  
> to show the user the ‘goodies’ they wanted. I can’t see where the  
> @hotel is that you pass to ‘feed’ this view?

You are right, my example code was missing that. Should have been:  
def search  
hotel\_searcher = HotelSearcher.new(params[:hotel\_searcher])  
@hotels = Hotels.find, :all, :conditions =\>  
hotel\_seacher.to\_conditions #  
end

> Could you combine your SEARCH action logic into your INDEX logic  
> something like this;  
> def index  
> if the params[:hotel\_searcher]) are nil  
> MasterTable.find(:all)  
> else  
> do that voodoo that you do so well  
> end  
> end

Yes, that is possible, but not very Railish or RESTful. You should let  
the router make decisions about which actions to call. Almost all the  
Rails code I’ve seen has had a form in the index that POSTs to another  
action. Especially if you do Ajax forms.

> I’m so new and impressionable to Rails, that I’m striving to be a  
> ‘good boy’ and only use the 7 CRUD actions.

My approach is really REST agnostic. If by 7 you mean (index, new,  
create, show, edit, update, destroy) only index displays a list of  
records. Search isn’t usually one of these CRUD things, but something  
nearly every app I’ve written has needed.

In REST you have these two actions:  
GET /articles  
and  
GET /article/123  
The first responds with a list, the second with a view of one  
particular element of that list. Note the the index action has no  
filter. It is not very modern UI (you’d get back all of the articles  
of which there could be thousands or more.) How do you filter? How do  
you page the results?

I think Searching and REST are out of each other’s scope. Or it seems  
that way to me.

Alternatively, you could use PragDave’s RADAR approach to architect the  
app.

[http://pragdave.pragprog.com/pragdave/2007/03/the\_radar\_archi.html](http://pragdave.pragprog.com/pragdave/2007/03/the_radar_archi.html)

While not a solution to the search issue, I’ve heard of using this to  
implement a search in the presentation layer that talks to the back  
end via REST. Can’t find the URL for it.

> Thank you,  
> David

Sure.

Hth,  
Ed

> > > I’ve seen code like this:  
> > > simple example of how this could be done?  
> > > to\_conditions, to\_includes, to\_querystring etc. Each form’s controller  
> > > def index  
> > > To get fancy:  
> > > that gathers the correct include =\> [] array.  
> > > HTH,
> 
> >

–  
Ed Howland

> **[Green Programming](http://greenprogrammer.blogspot.com)**
>
> Environmentally safe code

---

<div class="post-metadata">

**Author:** ![bravedave](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/46a35a/32.png) [@bravedave](https://www.ruby-forum.com/u/bravedave)\
**Post date:** [May 14, 2007, 3:34pm UTC](https://www.ruby-forum.com/t/how-do-we-search-in-a-restful-world/103941/6 "2007-05-14T15:34:18Z")

</div>

On 5/14/07, Daniel N [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> I’m not sure that this answers your question at all, but there is a nice  
> plugin that helps with searching AR records without the fulltext searching.  
> EZWhere by Ezra, You can start looking at it on his blog at  
> [Ruby on Rails Blog / What is Ruby on Rails for?](http://brainspl.at/articles/2006/10/03/nested-joins-and-ez-where-update)
> 
> HTH  
> Daniel

This is a nice conditions generator and it handles the nested joins  
that many I’ve seen lacked. I like the embedding of order by in the  
syntax.

I was trying to solve the first half of the problem (for which this  
plugin solves the second 1/2 nicely.) IOW, how do you go from a search  
form to a conditions array? Esp. when you have multiple join tables  
that participate in the search.

In many cases, if you have a single model to search, you can create it  
in the search index action  
@model = MyModel.new  
Then use form\_for on @model

and then again in the SUBMIT action  
@model = MyModel.new(params[:model]  
… somehow create the conditions array from the fields in the model.  
… perhaps via EXWhere above.

But how do you make this DRY? It seems that each form is going to have  
to be handled independently, and this make sense, but I wanted to  
convert from any form into any conditions array and do it OAOO.

My approach to form processing could use an EZWhere plugin internally,  
because I am using metaprogramming to generate the fields.

Ed

> > > out?  
> > > that you use the ‘form\_for’ helper and (from what I’ve read) this
> > 
> > > to show the user the ‘goodies’ they wanted. I can’t see where the  
> > > something like this;  
> > > Rails code I’ve seen has had a form in the index that POSTs to another  
> > > In REST you have these two actions:  
> > > that way to me.
> > 
> > > > > Dear RubyReportist’s  
> > > > > def self.search(town, beds, min\_price, max\_price)  
> > > > > simple example of how this could be done?  
> > > > > to\_conditions, to\_includes, to\_querystring etc. Each form’s controller
> 
> etc.
> 
> > > > options are from a lookup table. (They are all called ‘id’ and you can  
> > > > end  
> > > > quoted text -  
> > > > [http://greenprogrammer.blogspot.com](http://greenprogrammer.blogspot.com)
> > 
> > > >

–  
Ed Howland

> **[Green Programming](http://greenprogrammer.blogspot.com)**
>
> Environmentally safe code
