Материал по работе с Apache Lucene и созданию простейшего нечёткого поиска

Пост расcчитан на начинающих, на людей незнакомых с технологией Apache Lucene. В нем нет материала о том, как устроен Apache Lucene внутри, какие алгоритмы, структуры данных и методы использовались для создания фреймворка. Пост является обучающим материалом-тизером, написанным для того, чтобы показать, как организовать простейший нечёткий поиск по тексту. В качестве материала для обучения предоставлен код на github, сам пост в качестве документации и немного данных для тестирования поисковых запросов.

697aa0ce29874fa4aae65be227ea4507.png


Введение


Подробно о библиотеке Apache Lucene написано здесь и здесь. В статье будут встречаться такие термины как: запрос, индексация, анализатор, нечеткие совпадения, токены, документы. Советую сначала прочитать вот эту статью. В ней эти термины описывают в контексте фреймворка Elasticsearch, который базируется на библиотеках Apache Lucene. Поэтому базовая терминология и определения совпадают.

Инструментарий


В статье описывается использование Apache Lucene 5.4.1. Исходный код доступен на github, в репозитории есть небольшой набор данных для тестирования. По сути статья является подробной документацией к коду в репозитории. Начать «играть» с проектом можно с запуска тестов в классе BasicSearchExamplesTest.

Создание индексов


Проиндексировать документы можно с помощью класса MessageIndexer. В нём есть метод index:

    public void index(final Boolean create, List documents) throws IOException {
        final Analyzer analyzer = new RussianAnalyzer();
        index(create, documents, analyzer);
    }

Он принимает на вход переменную create и documents. Переменная create отвечает за поведение индексатора. Если она равна true, то индексатор будет создавать новый индекс даже если индекс уже существовал. Если false, то индекс будет обновляться.
Переменная documents это список объектов Document. Document это объект индексации и поиска. Он представляет собой набор полей, каждое поле имеет имя и текстовое значение. Для того чтобы получить список документов создан класс MessageToDocument. Его задача создавать Document используя два строковых поля: body и title.

    public static Document createWith(final String titleStr, final String bodyStr) {
        final Document document = new Document();

        final FieldType textIndexedType = new FieldType();
        textIndexedType.setStored(true);
        textIndexedType.setIndexOptions(IndexOptions.DOCS);
        textIndexedType.setTokenized(true);

        //index title
        Field title = new Field("title", titleStr, textIndexedType);
        //index body
        Field body = new Field("body", bodyStr, textIndexedType);

        document.add(title);
        document.add(body);
        return document;
    }

Обратите внимание что метод index по умолчанию использует RussianAnalyzer, доступный в библиотеке lucene-analyzers-common.

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

Поиск


Для демонстрации базовых возможностей поиска создан класс BasicSearchExamples. В нём реализованы два метода поиска: простой поиск по токенам и нечеткий поиск. За простой поиск отвечают методы searchIndexWithTermQuery () и searchInBody (), за нечеткий поиск метод fuzzySearch ().

В Lucene существует много способов создать запрос, но для простоты методы обычного поиска реализованы только с помощью классов QueryParser и TermQuery. Методы нечеткого поиска используют FuzzyQuery, которая зависит от одного важного параметра: maxEdits. Этот параметр отвечает за нечеткость поиска, подробности здесь. Погрузиться в многообразие способов сделать запрос можно здесь.

Для того чтобы поиграть с поиском перейдите к классу BasicSearchExamplesTest

Задание


Чтобы играть с проектом было не скучно попробуйте выполнить несколько заданий:

  • Сделайте интерактивный консольный поиск. Поиск должен показывать выдачу и спрашивать следующий запрос.
  • Сейчас поиск работает только с полем body. Сделайте так, чтобы поиск работал по полям title и body одновременно.
  • Подсчитайте количество проиндексированных слов (токенов)
  • Расширьте модель Message, добавьте в неё регион (region) и дату создания сообщения (creationDate). Не забудьте добавить новые поля для индексации в классе MessageToDocument. Добавьте новые способы поиска с фильтром по региону и дате
  • Посмотрите на класс запросов MoreLikeThisQuery. Попробуйте сгруппировать все документы по похожести используя значение score.
  • Скачайте вот этот файл, в нем около 5000 различных сообщений. Проверьте как работает группировка, новые запросы и фильтры.

Заключение


Преимущество Apache Lucene в его простоте, высокой скорости работы и низких требованиях к ресурсам. Недостаток в отсутствии хорошей документации, особенно на русском языке. Проект очень быстро развивается, поэтому книги, туториалы и Q/A, которыми забит интернет, давно потеряли актуальность. К примеру, у меня ушло 4–5 дней только на то, чтобы понять, как вытащить векторную модель TF-IDF из индексов Lucene. Надеюсь что этот пост привлечет внимание специалистов к этой проблеме недостатка информации.

Для тех же кто хочет погрузиться в мир Apache Lucene советую взглянуть на документацию Elasticsearch. Многие вещи там очень хорошо описаны, со ссылками на авторитетные источники и с примерами.

© Habrahabr.ru