Wednesday, November 25, 2009

GroovyMag Plugin Corner: Searchable

Over the next few months, I will be reprinting some of the Plugin Corners that I've written for GroovyMag. Hopefully this will give you a taste of what you can find in GroovyMag each month (and keep my blog from being so dead). Enjoy!

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/

Wednesday, November 18, 2009

Slaying Hallway Zombies

For the past few months, I have been working on a project for a division of Boeing. It was a bit of a culture shock, going from a small, dynamic, family feeling company like Contegix to one of the largest companies in the world. But it's a Grails project, and the prospect of helping all of those oppressed Boeing Java devs get into Grails is well worth the cross-cultural experience. :-)

So, what does that have to do with zombies? Well, when working for a huge company, one often ends up in a huge building. And in huge buildings, we often find very long hallways. The building I work in has hallways that are several hundred feet long (almost 150 meters). Something strange happens to (otherwise friendly) people when they walk down long hallways. They become hallway zombies. Hallway zombies aren't like other zombies — they aren't decaying (usually), and they don't try eat your brains. But if you’re not careful, they will rob your joy. There’s something unnatural about walking directly past another person and not even acknowledging that they exist. But if you don’t fight it, it will start to become natural to you. And then you will have become a hallway zombie yourself!

Hallway zombies can be defeated, though, and fortunately you don't even have to decapitate them. (How would that look on a reference letter?) Some hallway zombies can be defeated with a simple smile. Some may take repeated smiles. The first smiles may seem to bounce right off of them, but if you keep it up, you will start to break through. Others are tougher and may require more drastic action, such as a full round-house “Good morning!”

Those are the basic weapons for fighting hallway zombies — and fight you must, or your fate is sealed. But these weapons are much more effective with a good technique. If you spot a hallway zombie coming towards you a couple hundred feet away and you start smiling at them right away, they will be able to defend against it. It's best to catch them by surprise. Let them think you’re one of them. Look straight ahead, or down at the floor. (Don't be tempted to take furtive glances into open office doors or down a row of cubicles. That's a dead give-away.) Then, when you are within 15 to 20 feet of passing, strike quickly with your best smile and even a nod of the head. If you're pulling out the big guns, you can start a bit sooner, just in case you see an opening for a “How’s it going?” to follow up on your “Good morning!”

I’m not yet convinced that any hallway zombie is beyond reach, but there are a few here that are proving quite challenging. I’m working on some new attack strategies, just in case. On the other hand, there have been some successes that are quite encouraging. Slaying hallway zombies is hard work, but it’s very rewarding. So I keep fighting.

You may not work in this type of environment, but chances are that you will find yourself somewhere, at some point in time, where you are faced with a hallway zombie. Don’t panic. Steady your nerves, hold your ground, and… Smile!