Custom Filter in Hibernate Search

    Hibernate Search, as a full-text search tool, is an offshoot of well-known Hibernate library, that is designed for solving problems of object-relational mapping, and is integrated with full-text search tool for Apache Lucene.

    The frequent question is how to implement not just a full-text search, but the so-called "advanced search", which will allow to add some extra search conditions apart from of full-text search.

    In this article we implement a small example, which simulates the books repository (library), we do a full-text search on books, as well as adding the ability to search for books with a certain rating.

    The filters concept is used to add extra search conditions to the full-text search, in our case it’s a condition that finds only books with the certain rating.

    First, let’s create a project test configuration. The details of project creating and Hibernate Search connecting are considered in this article while the full instruction on how to do this can be found in Hibernate Search website.

    The Book model with the following list of fields (the full model version can be viewed at Book.java link) was realised in the system for the storage of information about a book:

    In this model, we are interested in rating field, which directly stores the book rating.

    To search for a specific rating value apart from full-text search BookRatingFilter filter (BookRating.java) was realized. To do this, you should perform inherit from the abstract org.apache.lucene.search.Filter class and implement getDocIdSet(IndexReader reader) method.

    Let’s do it:

    On the first filtration step we should get a list of rating field values from the cache:

    1. final String[] allIndexedStrings = FieldCache.DEFAULT.getStrings(indexReader, fieldName)

    where fieldName is a field, which values we want to get out of the cache, in our case it is rating.

    Further we get DocId of all the documents that have been found as a result of a full-text search by Lucene Index:

    1. DocIdSet docs = previousFilter.getDocIdSet(indexReader)

    Let’s apply filter to the received documents and return FilteredDocIdSet object, directly in which match method the filtering takes place. This method is called for each DocId, if the method returns “true”, the record enters the final result and the book will be present in the search of sampling results, or it will not be otherwise.

    1. return new FilteredDocIdSet(docs) {
    2. @Override
    3. protected boolean match(int documentIndex) {
    4. double rating = Double.parseDouble(allIndexedStrings[documentIndex]);
    5. return rating >= lowerRatingBound && rating <= upperRatingBound;
    6. }
    7. };

    For the full-text search in our book-store we add BookSearchService class (BookSearchService.java), which will contain the main search business logic using previously written filter:

    Now let’s make a small modular BookSearchServiceTest (BookSearchServiceTest.java) test to check the correct operation of the previously implemented filter. At first we save a few test records of the books in the database:

    Then using BookSearchService service we search for the books based on the rating:

    Filters can be chained together, combined, used in a different order, that directly affects the search result of sampling. Source code of test project can be found in the repository on Github.

    Links with relevant questions about Hibernate Search on stackoverflow

    http://stackoverflow.com/questions/5893872/hibernate-search-filter-out-object-with-the-highest-value

    http://stackoverflow.com/questions/25093682/using-a-lucene-filter-in-hibernate-search

    http://stackoverflow.com/questions/18124103/how-to-create-filter-for-hibernate-lucene-search - how to create a filter

    http://stackoverflow.com/questions/10332784/advanced-search-using-hibernate-search/10418685#10418685 - how to create advanced search