Last night we capped off a great weekend at RubyNation 2011 with a wrap-up dinner (thanks Dr. Nic!). I'm proud that so many CodeSherpas had a hand in putting together this conference over the years. Anyways, during the dinner a fellow attendee, Robert, asked me for my thoughts on RESTful search. "It's just the query string on the index, right?" That is a very interesting question, and as it turns out there's more than one way to do it...
We are all familiar with search and use Google every day. Perform a Google search and it gives you results. But "search" itself is a verb - an activity (bear with me if this seems a bit pedantic).
When using Google, we are really interested in web pages or sites related to our search term: a set of resources related to that term:
term ("fast cars") -> search (submit to Google) -> resource (a list of web pages related to fast cars)
We will use this as our guide to develop a RESTful search service. I will make use of some Rails conventions in my examples below, but the concepts here apply to any RESTful ideal.
Let's say I'm building a web site for a fantastic Mini dealership, and my site includes search for all cars on the lot. I will need to answer questions like:
Do you have any red & white Mini Coopers with superchargers?
Option 1: The query string!
Yes this is very obvious. GET => /mini_cooper_inventory Returns my entire inventory GET => /mini_cooper_inventory?color="red"&color="white" &supercharger="true" Returns the portion of my inventory matching the given query parameters
Option 2: A filter resource.
Aha! I have played a trick on you in Option 1. We were already using a filtered resource to see only those Coopers in our inventory. We excluded cars that have been already been sold, or those on order or in transit. To think of filters RESTfully: GET => /mini_coopers Returns all Mini Coopers that have anything to do with my dealership: ones that I have sold, ones that are in inventory, and ones on order. Because these are such commonly used resources, instead of doing a query every time I wanted to look at my inventory: GET => /mini_coopers?status="inventory" I have a dedicated another resource: GET => /mini_cooper_inventory In Rails you would perform the search in the #index method of this controller, and then narrow by any other query parameters. A note of interest, the mini_cooper_inventory is not backed by a model of it's own. REST is not CRUD, but that is a topic for another day.
Option 3: A search-results resource
This is probably useful when you have a really complex search that spans several resources. Let's say the IRS (yikes!) is askng me for all details on all sales made by Duzzy Cheetham or his family in the past two years. RESTfully speaking, I'm potentially dealing with any of a number of resources in my system: mini_coopers customers sales I could use one of the previous 2 options, and search for sales matching certain critera: GET => /sales?customer_last_name=Cheetham &customer_last_name=Swindell &date_range=2009-04to2011-04 &detailed_report=true But at this point they query string (Option 1) does not seem to be a good fit. I could create a search object: POST => /search But again, this is not really what I am after, unless I am giving users the ability to save searches. What I really want is some sort of customer report: GET => /customers/:duzzy_cheetham_id/customers_report I want to see all purchases made by Duzzy Cheetham and his family: GET => /all_purchases_made_by_duzzy_cheetham_and_his_family Neither of those requests are really reusable or useful outside of this one search. Let's try again. GET => /search_results/new This is the form for doing a complex search (familiar to those of you using Rails). Not bad. What's next? POST => /search_results This creates a new search results report that matches my search terms, including date range, customers, and whatever else I want to throw in. And if I want 2 types of searches, one that returns a list of results, and another that summarizes results in some type of report: POST => /search_results POST => /search_results_reports The name of the actual resource is up to you. The search submitted can even be given a name that allows users to save and re-run searches: POST => /search_results/:saved_search_id
Anyways, there are many options when it comes to organizing your applications to work RESTfully - for the way the web was designed. I hope this example gives some food for thought.