Full Text Search in Grails

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Full Text Search in Grails

Fabio Quimbay
Hi,

Currently I have an application with Grails 2.3.7 and I need to use a full text search functionality. 

I was reading some documentation about this engines and plugins and I see implementations like: Searchable Plugin, Hibernate Search Plugin, Solr Plugin, SolrJ, Elastic Search, etc; but I'm not pretty sure of which of them I can use to start my work; at the beginning I was planning to work with Solr but this plugin (Solr Plugin) is some old (more or less 3 years ago) and I don't know if there is another better or more recent implementation.

I appreciate any advice, that let me starting with full text search in Grails.

Thank you !!

Regards.

--
Fabio Quimbay
Reply | Threaded
Open this post in threaded view
|

Re: Full Text Search in Grails

xunitc
Hi,

Here is my solution:
I use elasticsearch.

// a factory, which from internet.
package org.myapp;
// some import
public class ElasticsearchNodeFactoryBean implements
FactoryBean<GNode>, InitializingBean, DisposableBean {

    protected final Log logger = LogFactory.getLog(getClass());

    private Map<String, String> settings;

    private GNode node;

    public void setSettings(final Map<String, String> settings) {
        this.settings = settings;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        internalCreateNode();
    }

    private void internalCreateNode() {
        final GNodeBuilder nodeBuilder = nodeBuilder();
        if (null != settings) {
            nodeBuilder.getSettings().put(settings);
        }
        node = nodeBuilder.node();
    }

    @Override
    public void destroy() throws Exception {
        try {
            node.close();
        } catch (final Exception e) {
            logger.error("Error closing Elasticsearch node: ", e);
        }
    }

    @Override
    public GNode getObject() throws Exception {
        return node;
    }

    @Override
    public Class<GNode> getObjectType() {
        return GNode.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
// resource.groovy
beans = {
  esNode(org.myapp.ElasticsearchNodeFactoryBean) {
    settings = ["node.client": true/*,"cluster.name","mycluster"*/]
  }
}

After this, the "myInstnceClass" of type can be the
instance.getClass(), but must no "." in it, so you can try to replace
it, I use this function :
indexName(domain) {
    return domain.name.toLowerCase().replaceAll('\\.', '_');
}

// search service.
class SearchService {
    def synchronized index(instance) {
        if (!instance) {
            return;
        }
        def array = instance.searchMap();
        def client = esNode.client;
        if (array && instance.id) {
            client.index {
                index "myAppName"
                type "myInstanceClass"
                id String.valueOf(instance.id)
                source {
                    array
                }
            }
        }
    }
    def search(domain, terms, from, size) {
        def types = [];
        if(domain instanceof List) {
            domain.each { it ->
                types.add("myInstanceClass");
            }
        } else {
            types.add("myInstanceClass");
        }
        def search =
esNode.client.prepareSearch(appName).setTypes(types as
String[]).setQuery(terms).setFrom(from).setSize(size).gexecute();
        def hits = search.response.hits;
        return [total: hits.totalHits, model: hits.hits?.collect{it.source}];
    }
}


//Domain Class

class Post {
    // some fields

    // this return a search map. you can define which will be searched.
    def searchMap() {
        def search = [:] as Map;
        search.id = this.id;
        search.name = this.name;
        search.content = this.content;
        search.dateCreated = this.dateCreated;
        search.hitCount = this.hitCount;
        search.commentCount = this.commentCount;
        def category = [:] as Map;
        category.id = this.category.id;
        category.name = this.category.name;
        search.category = category;
        def person = [:] as Map;
        person.id = this.person.id;
        person.username = this.person.username;
        search.person = person;
        search.tags = this.tags;
        return search;
    }
}

And when you save or update a domain instance, please use the
SearchService.index(instance);
When you will search, like :
// in a controller
def terms = new
QueryStringQueryBuilder(params.q).field("name").field("content").autoGeneratePhraseQueries(true);
def result = searchService.search(Post, terms, from, pageSize);
return [resultList: result.model, resultTotal: result.total];

Some manual work, it works good for me. Hope some help for you.

2014-04-08 6:46 GMT+08:00 Fabio Quimbay <[hidden email]>:

> Hi,
>
> Currently I have an application with Grails 2.3.7 and I need to use a full
> text search functionality.
>
> I was reading some documentation about this engines and plugins and I see
> implementations like: Searchable Plugin, Hibernate Search Plugin, Solr
> Plugin, SolrJ, Elastic Search, etc; but I'm not pretty sure of which of them
> I can use to start my work; at the beginning I was planning to work with
> Solr but this plugin (Solr Plugin) is some old (more or less 3 years ago)
> and I don't know if there is another better or more recent implementation.
>
> I appreciate any advice, that let me starting with full text search in
> Grails.
>
> Thank you !!
>
> Regards.
>
> --
> Fabio Quimbay



--
Xunitc.

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Xunitc
Reply | Threaded
Open this post in threaded view
|

Re: Full Text Search in Grails

Fabio Quimbay
Hi,

Thank you xunitc for your help. 

This is my first time working with full-text search, and I read a lot of things about 
ElasticSearch, and I see that is a great tool. Finally I configured the Solr Plugin 
inside Grails, and for now it works fine, I hope with more time continue my study of 
ElasticSearch, and with the example that you sent me, it will be more easy.

Thank you !!

Regards.

--
Fabio Quimbay


On Mon, Apr 7, 2014 at 7:42 PM, xunitc <[hidden email]> wrote:
Hi,

Here is my solution:
I use elasticsearch.

// a factory, which from internet.
package org.myapp;
// some import
public class ElasticsearchNodeFactoryBean implements
FactoryBean<GNode>, InitializingBean, DisposableBean {

    protected final Log logger = LogFactory.getLog(getClass());

    private Map<String, String> settings;

    private GNode node;

    public void setSettings(final Map<String, String> settings) {
        this.settings = settings;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        internalCreateNode();
    }

    private void internalCreateNode() {
        final GNodeBuilder nodeBuilder = nodeBuilder();
        if (null != settings) {
            nodeBuilder.getSettings().put(settings);
        }
        node = nodeBuilder.node();
    }

    @Override
    public void destroy() throws Exception {
        try {
            node.close();
        } catch (final Exception e) {
            logger.error("Error closing Elasticsearch node: ", e);
        }
    }

    @Override
    public GNode getObject() throws Exception {
        return node;
    }

    @Override
    public Class<GNode> getObjectType() {
        return GNode.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
// resource.groovy
beans = {
  esNode(org.myapp.ElasticsearchNodeFactoryBean) {
    settings = ["node.client": true/*,"cluster.name","mycluster"*/]
  }
}

After this, the "myInstnceClass" of type can be the
instance.getClass(), but must no "." in it, so you can try to replace
it, I use this function :
indexName(domain) {
    return domain.name.toLowerCase().replaceAll('\\.', '_');
}

// search service.
class SearchService {
    def synchronized index(instance) {
        if (!instance) {
            return;
        }
        def array = instance.searchMap();
        def client = esNode.client;
        if (array && instance.id) {
            client.index {
                index "myAppName"
                type "myInstanceClass"
                id String.valueOf(instance.id)
                source {
                    array
                }
            }
        }
    }
    def search(domain, terms, from, size) {
        def types = [];
        if(domain instanceof List) {
            domain.each { it ->
                types.add("myInstanceClass");
            }
        } else {
            types.add("myInstanceClass");
        }
        def search =
esNode.client.prepareSearch(appName).setTypes(types as
String[]).setQuery(terms).setFrom(from).setSize(size).gexecute();
        def hits = search.response.hits;
        return [total: hits.totalHits, model: hits.hits?.collect{it.source}];
    }
}


//Domain Class

class Post {
    // some fields

    // this return a search map. you can define which will be searched.
    def searchMap() {
        def search = [:] as Map;
        search.id = this.id;
        search.name = this.name;
        search.content = this.content;
        search.dateCreated = this.dateCreated;
        search.hitCount = this.hitCount;
        search.commentCount = this.commentCount;
        def category = [:] as Map;
        category.id = this.category.id;
        category.name = this.category.name;
        search.category = category;
        def person = [:] as Map;
        person.id = this.person.id;
        person.username = this.person.username;
        search.person = person;
        search.tags = this.tags;
        return search;
    }
}

And when you save or update a domain instance, please use the
SearchService.index(instance);
When you will search, like :
// in a controller
def terms = new
QueryStringQueryBuilder(params.q).field("name").field("content").autoGeneratePhraseQueries(true);
def result = searchService.search(Post, terms, from, pageSize);
return [resultList: result.model, resultTotal: result.total];

Some manual work, it works good for me. Hope some help for you.

2014-04-08 6:46 GMT+08:00 Fabio Quimbay <[hidden email]>:
> Hi,
>
> Currently I have an application with Grails 2.3.7 and I need to use a full
> text search functionality.
>
> I was reading some documentation about this engines and plugins and I see
> implementations like: Searchable Plugin, Hibernate Search Plugin, Solr
> Plugin, SolrJ, Elastic Search, etc; but I'm not pretty sure of which of them
> I can use to start my work; at the beginning I was planning to work with
> Solr but this plugin (Solr Plugin) is some old (more or less 3 years ago)
> and I don't know if there is another better or more recent implementation.
>
> I appreciate any advice, that let me starting with full text search in
> Grails.
>
> Thank you !!
>
> Regards.
>
> --
> Fabio Quimbay



--
Xunitc.

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email



Reply | Threaded
Open this post in threaded view
|

Re: Full Text Search in Grails

mstein
There is also an elasticsearch grails plugin available to facilitate its integration in a grails project, there : http://grails.org/plugin/elasticsearch
It *should* work out of the box after the installation, and it will use an embedded node by default for a development environment (so you don't have to launch a standalone elastic search instance for your tests).

Cheers,

Manuarii