Introduction

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 adding some extra search conditions apart from of full-text search.

In this article we implement a small example, which simulates the book’s 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 filter’s 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 the 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:

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:

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.

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

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

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

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

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

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