Так как проект на Java — было очень соблазнительно использовать для этого Lucene.
В гугле я сразу нашел замечательный модуль для Playframework-а под названием Search, также был найден модуль Elastic Search, который тоже использует Lucene, но он требует установки отдельного сервера, и потому был отметен. Модуль Search понравился мне своей простотой — все «навороты» в нем инкапсулированы, так что пользоваться им очень легко.
С установкой модуля, как и всегда в Play-e, проблем не возникло, команда play install search отработала на «ура» и выкачала модуль из репозитория.
Добавив module.search=${play.path}/modules/search-2.0 в application.conf я уже мог использовать его в приложении.
Следуя краткому руководству, я добавил к сущности Entry, по которой собственно и следовало осуществлять поиск, аннотацию @Indexed, а полю description — аннотацию @Field.
Написав в контроллере примерно следующий код:
public static void search(String phrase, int page) { int pageSize = PAGE_SIZE; Query query = Search.search("description:" + phrase, Entry.class); List<Entry> entries = query.page(page*pageSize, pageSize).fetch(); long totalCount = query.count(); render(entries, totalCount, page, pageSize, phrase); }
Я уже был готов делать первые тесты и наращивать функционал, но тут начались проблемы…
Поиск не работал, то есть метод count() возвращал 0, а список entries был пуст. Я пытался осуществлять поиск и на русском и на английском, и вызывал Search.rebuildAllIndexes(), и много чего еще пытался, но результат был неизменен.
Благо модуль в play-e скачивается вместе с исходниками и можно было задебажить. Непродолжительный дебаг показал, что в индекс не кладется поле description. Я залез чуть глубже и увидел, что в поисках аннотации @Field у поля сущности используется метод object.getClass().getFields(), но стоп, метод этот возвращает только public поля, а у меня в сущности поля как и положено имеют protected доступ, и автору модуля следовало использовать метод getDeclaredFields().
Тут я сделаю некоторое отступление: мне совершенно не хотелось пересобирать модуль и менять в нем код, как минимум по тому, что я бы потерял возможность делать play install search на «боевом» сервере, а пришлось бы руками подкладывать пересобраный модуль. Написать баг-репорт или предложить патч, было не быстрой затеей, а функционал нужен был уже сейчас.
В общем было принято решение сделать поле description public-ом, и написать todo, до момента исправления этого бага в модуле. И, о чудо, поиск заработал!
Следующей проблемой, о которой я уже догадывался прочитав tutorial, было то, что в модуле была «киллер фича» — автоматическое обновление индекса(и добавление новых записей) при CRUD операциях с сущностью — мне это было не нужно. Дело в том, что записи, которые добавляются в систему сначала проходят премодерацию и мне совершенно не нужно, чтобы еще «неотмодерированные» записи попадали в поиск.
Я хочу сам вызывать Search.index(entry) после модерации. Я очень надеялся на то, что я найду в исходниках проверку на какую-нибудь строчку в конфиге: делать автоматическое обновление индекса или нет, но я наткнулся на это в коде SearchPlugin:
@Override public void onEvent(String message, Object context) { if (!message.startsWith("JPASupport")) return; if (message.equals("JPASupport.objectPersisted") || message.equals("JPASupport.objectUpdated")) { Search.index (context); } else if (message.equals("JPASupport.objectDeleted")) { Search.unIndex(context); } }
Отключить это было никак нельзя, и вроде бы пора уже думать о том, что надо слать патч и пинать, чтобы быстрее выпустили версию с фиксом, но тут родилась гениальная идея: а ведь мне по сути все равно будет это модулем или просто набором библиотек, которые я положу в папку lib.
Копание в коде показало, что при старте приложения нужно выполнить:
Search.init(); FileExtractor.init();
А при остановке:
try { Search.shutdown(); } catch (Exception e) { throw new UnexpectedException (e); }
Это легко делается с помощью Job-ов с аннотациями @OnApplicationStart/Stop.
Следующим действием было «отучить» play думать, что это плагин. Playframework находит модули в classpath-е с помощью файлика play.plugins, собственно обычным архиватором этот файлик был удален из jar файла, и все завертелось-закрутилось.
Надеюсь мой опыт будет полезен и поможет сохранить время людям, которые использует Playframework в своей работе.
PS: Раз уж мне пришлось класть модуль в папку lib, я уж заодно и пересобрал его поправив баг с public полями. 🙂
ссылка на оригинал статьи http://habrahabr.ru/post/174545/
Добавить комментарий