I'm building a RESTful interface on a Grails 2.1.1 application. How should I implement search operations? I don't want to repeat huge amounts of code, which my current thinking would require.
The server structure is quite normal Grails-MVC: domain classes represent data, controllers offer the interface and services have the business logic. I use command objects for data binding in controllers but not on service methods. The client is a web UI. My goal is to have search URLs like this:
/cars/?q=generic+query+from+all+fields
/cars/?color=red&year=2011
(I'm aware of the debate on the RESTfulness of this kind of URLs with query strings: RESTful URL design for search. While I think this is the best model for my purpose, I'm open to alternatives if they make the API and the implementation better.)
As you can see from the code examples below my problem is with the second kind of URL, the field-specific search. In order to implement this kind of search operation for several domain classes with lots of fields my method signatures would explode.
There probably is a "Groovy way" to do this but I'm still a bit of a n00b in finer Groovy tricks :)
Domain:
class Car {
    String color
    int year
}
Controller:
class CarsController {
    def carService
    def list(ListCommand cmd) {
        def result
        if (cmd.q) {
            result = carService.search(cmd.q, cmd.max, cmd.offset, cmd.order, cmd.sort)
        }
        else {
            result = carService.search(cmd.color, cmd.year, cmd.max, cmd.offset, cmd.order, cmd.sort)
        }
        render result as JSON
    }
    class ListCommand {
        Integer max
        Integer offset
        String order
        String sort
        String q
        String color // I don't want this field in command
        int year // I don't want this field in command
        static constraints = {
            // all nullable
        }
    }
    // show(), save(), update(), delete() and their commands clipped
}
Service:
class CarService {
    List<Car> search(q, max=10, offset=0, order="asc", sort="id") {
        // ...
    }
    List<Car> search(color, year, max=10, offset=0, order="asc", sort="id") {
        // ...
    }
}
UrlMappings:
class UrlMappings {
    static mappings = {
        name restEntityList: "/$controller"(parseRequest: true) {
            action = [GET: "list", POST: "save"]
        }
        name restEntity: "/$controller/$id"(parseRequest: true) {
            action = [GET: "show", PUT: "update", POST: "update", DELETE: "delete"]
        }
    }
}
 
     
    