Last updated by gorshing 7 years ago

Searchable Plugin - Controller and view

Searchable Plugin comes with a controller SearchableController and view searchable/index.gsp . Try these with your application (like this) - they can help to test queries and you can probably copy some of the code.

You could use the SearchableController as it comes, but you will probably want a different URL and HTML results page.

To change the URL add entries to your grails-app/conf/UrlMappings.groovy .

To change the view, you can simply keep the plugin's controller and copy myapp/plugins/searchable-x.x/grails-app/views/searchable/index.gsp to myapp/grails-app/views/searchable/index.gsp where it will override the plugin's version.

Or of course you can create your own dedicated search controller and view.

Under the covers

Here's the implementation of the search action from SearchableController :

import org.compass.core.engine.SearchEngineQueryParseException

// ...

class SearchableController { def searchableService

/** * Index page with search form and results */ def index = { if (!params.q?.trim()) { return [:] } try { return [searchResult:, params)] } catch (SearchEngineQueryParseException ex) { return [parseException: true] } }

// … }

Notice that params.q is the search query string and params is also given to the search method as the second argument. This is a Map of options and are things like page size, start result number, etc.

Any String arguments are parsed, so you can use request parameters directly, even when they have string values. For example, params may be [escape: "true", offset: "20", q: "toast"] , but you won't get a ClassCastException.

Pagination of search results in the view

Search results can be paginated using Grails' standard <g:paginate /> tag.

Here it is in action in the Searchable Plugin's own search results page, grails-app/views/searchable/index.gsp . The searchResult is an object returned by either SearchableService#search or DomainClass#search :

<g:if test="${haveResults}"> <!-- or you could use test="${searchResult?.results}" -->
    <g:set var="totalPages" value="${Math.ceil( / searchResult.max)}" />
    <g:if test="${totalPages == 1}">
        <span class="currentStep">1</span>
        <g:paginate controller="searchable" action="index" params="[q: params.q]" 
                    total="${}" prev="&lt; previous" next="next &gt;"/>

And here's some CSS to style the generated HTML:

.paging a.step {
    padding: 0 .3em;

.paging span.currentStep { font-weight: bold; }

Suggested queries

You can easily highlight the difference between the original and a suggested query with the following in a GSP:

<%@ page import="org.codehaus.groovy.grails.plugins.searchable.util.StringQueryUtils" %>

<p>Did you mean <g:link controller="searchable" action="index" params="[q: searchResult.suggestedQuery]"> ${StringQueryUtils.highlightTermDiffs(params.q.trim(), searchResult.suggestedQuery)} </g:link>? </p>

Which results in something like:

Did you mean _space_ invader?

See this in action in the plugin's view.