Tuesday, July 31, 2007

RESTful search across multiple models

A guy I know from the Rails mailing list called me this morning. He’s working on some kind of forum application, I think, and is thinking hard about how to do search in a RESTful way.

Normally, I just use the index action to search across a single resource, but in his case he wants to search Posts, which are nested inside Topics, which are nested inside Forums.

Liquid error: No such file or directory – posix_spawnp

He wants to search Posts across all Forums and Topics. One option would be to make PostsController serve double-duty

Liquid error: No such file or directory – posix_spawnp

and make it figure out whether or not it’s called in a scoped way or an unscoped way.

Liquid error: No such file or directory – posix_spawnp

In this case, I suggested he go a different way. Even though he says he only wants to search Posts, I feel pretty sure he wants to search Topics (by title) and Forums (by name), too.

And even if he doesn’t, I do. I’m about to add a general-purpose, cross-resource search to SOLIS, the registration and conference management system I’m writing for SUUSI and this seemed like an ideal way to help out a friend, make a much-needed blog post, and get some code done in the meantime.

Before we get started, though…this is one way to do RESTful search. It’s not the only way. It is probably not the best way. I’m not sure it’s a good way, since I just thought of it on the porch this morning and haven’t used it yet. I’m pretty sure it’s an OK way, though, and a good place to start thinking about it. YMMV.

OK, that’s out of the way.

I already know what I want: a singleton resource called /search. So let’s add it to the routes

Liquid error: No such file or directory – posix_spawnp

I can never remember whether singleton resources want singular (SearchController) or plural (SearchesController) names. I’m pretty sure this has changed in edge, and possibly changed back. So, I add the resource and load it up, and see what Rails tells me. When I load /search, I get ‘uninitialized constant SearchController’. Cool, it did what I wanted, and used singular. (I just checked, and on edge it uses plural. Bleh. I guess I can see why, but…anyway.) Now we need that controller.

Liquid error: No such file or directory – posix_spawnp

Because it’s a singleton resource, the actions are different; requesting /search calls the show action.

Now, showing a search with no query string doesn’t make sense. /search/new is an ugly URL. I want to search as a GET not a POST, so you can bookmark it. So, here’s what I ended up with.

Liquid error: No such file or directory – posix_spawnp

Liquid error: No such file or directory – posix_spawnp

Obviously you’ll format your search results more prettily, with links and such. And you’ll need search() methods on all the classes you’re going to search. Or, make a Searcher model and let it do all the work.

Discussion

Follow me on Twitter. I don't have comments enabled, because I remember when we didn't have blog comments, and we did just fine, thank you very much.