From November 2008 issue
One of the most powerful Grails plugins is also one of the easiest to use. The Searchable plugin brings the power of the Compass search engine into Grails in a very Groovy way. If you haven’t tried the Searchable plugin yet, it’s time to stop making excuses and stop wasting time building complex search forms for your projects. Let’s get started.
The first step to using any plugin is to install it. From the root of your Grails project execute the following command:
grails install-plugin searchable
Now the fun begins. To make a domain class searchable, just add the
static searchable
flag to the class. Here’s our Book
class, for example:
class Book {
static Searchable = true
String title
Integer pages
Author author
String toString(){title}
}
We’ve already generated the scaffolding for this class, so now let’s add a
search
action to our BookController
like this:
def search = {
def bookList = Book.search(params.
query).results
render(view:’list’, model:[bookList:
bookList])
}
In order to call our new
search
action we will need a form, so we’ll add one to the generated list view. Put the following block of code right before the opening <table>
tag in list.gsp:
<div class=”nav”>
<g:form name=”search” action=’search’>
Search:
<input type=”text” name=”query” value=”${query}”/>
<input type=”submit” value=”Search”/>
</g:form>
</div>
Now if we run our application - with a little data thrown in via the bootstrap - we’ll get something like this:
We can now search for any string value across any of the properties of our Book instances from a single input field. We can search the String properties as well as the Integers. For example, if we had a book called “200 Reasons to Use Grails,” and we searched for the value 200, we would find
that book along with any books that had exactly 200 pages.
Although the default behavior is to search across all the properties, we can search a specific property by prefacing the search value with propertyName: (eg: author:Rocher).
I should point out that the code we have right now will blow up if the user doesn’t enter any search value before submitting the form. Don’t worry - this is easy to fix. The dynamic
search
method that is added to our Book
class by the Searchable plugin returns an instance of the SearchResult
class. This class contains a property called ‘results
’, which is an ArrayList
. This is why we can so easily return it in the model to our list view. So if there is no search value we can just call Book.list()
and return that instead. Let’s modify our search
action to look more like this:
def search = {
def bookList
if (params.query)
bookList = Book.search(params.query).results
else
bookList = Book.list()
render(view:’list’, model:[bookList:bookList])
}
We’re not using any additional parameters here, but the
search()
method takes all the same parameters that the list()
method takes - which is important when it comes to sorting and pagination. In order to make pagination work with the Searchable plugin, we need to make a few simple changes. First we’ll need to pass a couple extra pieces of information to the view. The SearchResult
returned by the search()
method will be used to determine the total number of objects found, and the query
parameter will be passed back to the view so that it can be reused when loading subsequent pages. So now our search
action will look like this:
def search = {
def bookList
if (params.query){
def searchResult = Book.search(params.query)
bookList = searchResult.results
}
else
bookList = Book.list()
render(view:’list’, model:[bookList:bookList, searchResult:searchResult, query:params.query])
}
Now we will modify list.gsp, but since we are using the same view for both the list and search actions, we will put our changes within a g:if tag. If we are viewing search results we will modify the g:paginate tag to make use of the new values we passed in on the model. Otherwise, we will use the original g:paginate tag.
<g:if test=”${searchResult}”>
<g:paginate total=”${searchResult.total}” params=”[query:query]” />
</g:if>
<g:else>
<g:paginate total=”${Book.count()}”/>
</g:else>
In keeping with the Grails way, the Searchable plugin gives you sensible defaults for most settings but allows for much more customization if needed. To add this, just install the SearchableConfiguration.groovy file by executing the following command:
grails install-searchable-config
You can get more details about what you can do with this file as well as excellent documentation on the Searchable plugin page at grails.org (see references). Hopefully this tutorial will give you enough to get you going. Once you get started, more questions will undoubtedly arise, so I’ve included a list of references below.
The Searchable plugin makes including powerful full text searching so easy to do that there’s really no reason to not take advantage of it. So dig in and have fun!
References:
http://grails.org/Searchable+Plugin
http://www.grails.org/Searchable+Plugin+-+Configuration
http://www.grails.org/Searchable+Plugin+-+Searching
http://www.compass-project.org/
http://lucene.apache.org/