En

JazzTeam Software Development Company

Agile Java Development

Создание собственного фильтра в Hibernate Search

Hibernate Search - инструмент полнотекстового поиска, является ответвлением небезызвестной библиотеки Hibernate, предназначенной для решения задач объектно-реляционного отображения, интегрированной с инструментом полнотекстового поиска Apache Lucene.

Часто встает вопрос о том, чтобы реализовать не просто полнотекстовый поиск, а так называемый “расширенный поиск”, который позволит помимо полнотекстового поиска добавить какие-то дополнительные условия поиска.

В данной статье мы реализуем небольшой пример, имитирующий хранилище книг (библиотеку), сделаем полнотекстовый поиск по книгам, а также добавим возможность искать книги с определенным рейтингом.

Для того чтобы добавить к полнотекстовому поиску дополнительные условия поиска, в нашем случае условие, которое найдет книги только с определенным рейтингом, используется концепция фильтров.

Для начала создадим тестовую конфигурацию проекта, подробности создания проекта и подключения Hibernate Search в данной статье рассмотрены не будут, полную инструкцию, как это сделать, можно найти на сайте Hibernate Search.

Для хранения информации о книге реализуем в систему модель Book, со следующим списком полей (полный вариант можно посмотреть по ссылке Book.java):

В данной модели нас интересует поле rating, которое непосредственно хранит рейтинг книги.

Для того чтобы помимо полнотекстового поиска выполнить поиск по конкретному значению рейтинга реализуем фильтр BookRatingFilter (BookRating.java). Для этого необходимо выполнить наследование от абстрактного класса org.apache.lucene.search.Filter и реализовать метод getDocIdSet(IndexReader reader), что, собственно, мы и сделаем:

На первом шаге фильтрации мы должны получить список значений поля rating из кэша:

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

где fieldName - поле, значения которого мы хотим получить из кэша, в нашем случае это rating.

Далее получим DocId всех документов, которые были найдены в результате полнотекстового поиска по Lucene Index:

  1. DocIdSet docs = previousFilter.getDocIdSet(indexReader)

Выполним фильтрацию по полученным документам, вернем объект FilteredDocIdSet, в методе match которого непосредственно и происходит фильтрация, данный метод будет вызван для каждого DocId, если метод вернет - true, то запись попадает в финальный результат и книга будет присутствовать в результатах поисковой выборки, иначе - нет.

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

Для того чтобы выполнить полнотекстовый поиск по нашему книжному хранилищу добавим класс BookSearchService (BookSearchService.java), который будет содержать основную бизнес-логику поиска с применением ранее написанного фильтра:

Сделаем небольшой модульный тест BookSearchServiceTest (BookSearchServiceTest.java) для того, чтобы проверить корректность работы ранее реализованного фильтра. Для начала сохраним несколько тестовых записей книг в БД:

После чего используя сервис BookSearchService выполним поиск книг с учетом рейтинга:

Фильтры можно объединять в цепочки, комбинировать, применять в различном порядке, что непосредственно будет влиять на результат поисковой выборки.

Исходники тестового проекта можно найти в репозитории на Github.

Ссылки с актуальными вопросами по Hibernate Search на stackoverflow

  1. http://stackoverflow.com/questions/5893872/hibernate-search-filter-out-object-with-the-highest-value
  2. http://stackoverflow.com/questions/25093682/using-a-lucene-filter-in-hibernate-search
  3. http://stackoverflow.com/questions/18124103/how-to-create-filter-for-hibernate-lucene-search -как создать фильтр
  4. http://stackoverflow.com/questions/10332784/advanced-search-using-hibernate-search/10418685#10418685 - создать advanced search

, ,

Leave a Reply

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