logo

Using searchable across domain classes

logo

Mike has done some great sleuthing on how to use Grails uber-cool searchable plugin to search a domain class based on properties of a nested class.

This is exactly what I’ve been looking for! Gravl supports multiple blogs per install, and when you are viewing my blog, I want those seaches to be constrained to fulltext search of just *my* blog, not all the data in the blog db.

My data model is 1:M from Blog to BlogEntry with a bidirectional link for navigation… So to make it all searchable I’ve got…

class Blog {
  static hasMany = [ blogEntries : BlogEntry, tags : Tag, blogProperties : BlogProperty ]

  static searchable = true

  // ...
}

class BlogEntry {

  static hasMany = [ comments : Comment, tags : Tag ]
  static belongsTo = [ Blog ]

  // we only index title and body, but index all fields in the parent "blog"
  static searchable = {
        title()
        body()
        blog(component: true)
  }

  Blog blog
  //...
}

So by marking blog(component: true) we’re telling Compass to store Blogs fields in the index with the BlogEntry. Why do we want to do that? So we can search BlogEntry and impose criteria related to Blog. In my case, I want to do “find me all BlogEntry objects which contain the text ‘blah’ but limit the hits to those objects that have a blog.blogid of ‘glen’”. The code is simpler than the explanation! Here’s my whole search controller code which powers my search box:

def search = {

        def query = params.query
        def blogid = params.blog

        def results = BlogEntry.search(query + " +blogid:${blogid}", params)

        return [ results: results, query: query ]
 }

And you’re got yourself blog-specific searching… Note that “blog.blogid” becomes just “blogid” in terms of field searching (ie. the component objects fields are collapsed into the parent).

Thanks Mike! Great pickup!

9 Responses to “Using searchable across domain classes”

  1. Dierk König says:

    How cool. I very much like the ‘searchable DSL’ !

  2. Chris says:

    Can you please describe how you made the searchable plugin work under RC4 and later? I did

    grails create-app bookstore
    grails install-plugin searchable

    grails create-domain-class Book

    added some properties and static searchable = true

    grails generate-all Book

    and

    grails run-app

    and get some weird error messages about URL controllers missing an action

    which version of grails did you use to make it work?

  3. Glen Smith says:

    Hi Chris.

    I was using it under RC3, so I can’t speak for RC4.

    The only issue I’ve had with it was a NullPointerException on startup for which you need a “patch”:http://jira.codehaus.org/browse/GRAILSPLUGINS-220 .

    Other than that had no issues.

  4. Maurice Nicholson says:

    Hey Glen,

    nice article :-)

    May I suggest writing the query with a builder closure like:

    BlogEntry.search params, {
    queryString(query)
    must(term(“blogid”, blogid))
    }

    This way you separate the user query from your own internal query, so you can monkey with it, eg:

    BlogEntry.search(params) {
    queryString(query, [escape: true, defaultAndOperator: true])
    must(term(“blogid”, blogid))
    }

    which escapes any dodgy characters and makes all terms in the query string required (this is just my example of course – it depends in your requirements as to how you treat user input!).

    Second it allows you to bypass the query string parser for the blogid. Otherwise, if blogid was a word or words, and it’s put into the text query, it would be parsed and when Lucene actually does the query it might not be the query you intended, since it has stripped junk characters removed common words (and/or) etc!

    I would typically map such properties as “un_tokenized”, meaning when they are saved to the index, they are not broken up into tokens (parsed) but instead are stored as complete verbatim text strings (preserving case and all). Then when you use “term” in the query builder, you can pass in the *exact* value you expect and your search will only match those exact values in the index.

    See the Mapping section of the docs for how to achieve this.

    As for the issues you and others have experienced here, I think there all fixed, but please raise issues if not :-)

  5. Glen Smith says:

    Awesome Maurice. Thanks for the tip. So good to have you back in the community!

  6. Maurice Nicholson says:

    Aw, thanks Glen :blush: :-)

    I’m enjoying reading your Gravl stories. It’s a great name too :-)

    Oh BTW I tried saving this comment without first entering my name and hit save and nothing happened. Then I looked in firebug and saw:

    • Property [author] of class [class CommentCommand] cannot be blank

    Aw, thanks Glen :blush: :-)

    I‘m enjoying reading your Gravl stories. It‘s a great name too :-)

    on

    Mon, 3 Mar 2008 22:08

    but I didn’t see the error displayed ;-)

  7. Mustafa Sener says:

    Hi Glen,
    I tried this example with two other domain classes which are

    class Book {
    Author author;
    String name;
    static searchable = {
    name()
    author(component: true)
    }
    static belongsTo = [Author];
    }

    class Author {
    static searchable = true
    String name;
    String surname;
    static hasMany = [books:Book]
    }

    and I performed following queries

    Book.search(“authorid:1″);
    Book.search(“authorname:Glen”);

    both of these queries does not return anything.

    am I doing any mistakes?

    Thanks…

  8. botna says:

    hi Glen
    I want to use searchable in a many to many relationship
    But I failed
    Would you please tell me how to use that if you have any experiance about that
    Thanks a lot

  9. Ali says:

    Hello,

    I have book hasMany author and author belongsTo company

    I want to search the books for the company x
    Book.search {
    …..
    }

    what i do ?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

logo
logo
Powered by WordPress | Designed by Elegant Themes