ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Using the Lucene Query Parser Without Lucene
Pages: 1, 2, 3, 4

Hibernate Query Builder

Now we can develop the query builder. As mentioned before, this example is only using TermQuery for the sake of a manageable example--it is very easy to translate it into the Hibernate criteria.

Here is the implementation:

import org.apache.lucene.index.Term;
import org.apache.lucene.search.TermQuery;
import org.hibernate.Criteria;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;

public class HibernateQueryBuilder implements IQueryBuilder {

    private final Criteria criteria;

    public HibernateQueryBuilder(Criteria criteria) {
        this.criteria = criteria;
    }

    public void termQuery(TermQuery query){
        Term term = query.getTerm();
        String field = term.field();
        Criterion crit;
        crit = Restrictions.eq(field, term.text());
        this.criteria.add(crit);
    }
}

The Hibernate query is based on Criteria. This is why we require a Criteria instance to create HibernateQueryBuilder. Please check the Hibernate documentation for details on how Criteria works.

Building restrictions based on TermQuery is very easy now with the pieces we have built. Sample query "name:John" is expressed in HQL (Hibernate Query Language) as Friend.name = "John". That is all. Now our simple tool handles any TermQuery and builds proper Hibernate criteria. Things gets more complicated if you want to support other query types available in Lucene API.

Lights, Camera, Action!

Let's put this all together into an example that uses the code developed so far:

import java.util.List;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.Query;
import org.hibernate.Criteria;

public class Test {

    public static void main(String[] args) throws Exception {
        // Setup Hibernate
        Criteria criteria=...;

        // Parse the query
        Analyzer analyzer = new WhitespaceAnalyzer();
        QueryParser luceneParser = new QueryParser("name", analyzer);
        String queryString = "name:John";
        Query luceneQuery = luceneParser.parse(queryString);

        // Build HQL criteria based on Lucene query.
        IQueryBuilder queryBuilder=new HibernateQueryBuilder(criteria);
        QueryInterpreter qi=new QueryInterpreter(queryBuilder);
        // Add criteria
        qi.parse(luceneQuery);

        // Retrieve list of objects that fulfill criteria
        List list = criteria.list();            
        // ... 
    }
}

The preceding code is missing Criteria creation, but that is well described in the Hibernate manual and I'll leave it as an exercise for the reader.

The example is straightforward. First we need the Lucene query parser and analyzer to "read" the query. WhitespaceAnalyzer is one of many implementations. QueryParser uses Analyzer to properly understand the query string. With Query in hand, we create a new HibernateQueryBuilder. The builder requires valid Criteria to work. Then we construct the QueryInterpreter. All we have to do now is to use QueryInterpreter to add the criteria. This is what qi.parse(luceneQuery) does. The last line gives us a list of objects conforming to search constraints.

Conclusion

I hope you see how easy it is to use the Lucene query parser, outside of the scope of the rest of Lucene, and together with an existing search solution in a legacy application. The ideas in this article were born during the development of a real application, and have been tested in a production environment.

In our environment, we have found great flexibility in expressing search criteria, simplicity in implementation, and an added benefit of being able to cache search results. We have created a demo application to let you play with the ideas in this article and take them further. The demo is built with Lucene Query API, Hibernate, and the HSQLDB database.

What Next?

Thanks to the decoupled design, this same technique can be used for other data stores. Why not adapt your existing search to LDAP or the Java Persistence API? (See Figure 5.)

Future implementations
Figure 5. Future implementations

Development work for this is already in progress. You are welcome to join this growing project and help improve the project.

Resources

Marcin Maciukiewicz is an experienced architect and developer. He has worked on a wide range of projects, from small web applications to high-availability enterprise solutions.

Daniel Owsiański is an independent IT consultant and author of webankieta.pl, a leading Polish online survey system.


Return to ONJava.com.