Более новая версия доступна в разделе документации.

Предисловие

Данный документ является руководством по применению подсистемы полнотекстового поиска (full text search, FTS) платформы CUBA.

Целевая аудитория

Данное руководство предназначено для разработчиков, создающих на платформе CUBA приложения с возможностью полнотекстового поиска. Предполагается, что читатель ознакомлен с Руководством по разработке приложений, доступным по адресу www.cuba-platform.ru/manual.

Дополнительные материалы

Настоящее Руководство, а также другая документация по платформе CUBA доступны по адресу www.cuba-platform.ru/manual.

Подсистема полнотекстового поиска CUBA основана на фреймворке Apache Lucene, поэтому знакомство с его устройством будет полезным. См. lucene.apache.org/core.

Обратная связь

Если у Вас имеются предложения по улучшению данного руководства, обратитесь пожалуйста в службу поддержки по адресу www.cuba-platform.ru/support/topics.

При обнаружении ошибки в документации укажите, пожалуйста, номер главы и приведите небольшой участок окружающего текста для облегчения поиска.

1. Обзор подсистемы FTS

Полнотекстовый поиск (full text search, FTS) платформы CUBA предоставляет возможность неструктурированного поиска по значениям атрибутов сущностей и по содержимому загруженных файлов.

Особенностью реализации полнотекстового поиска CUBA является его ориентация на использование в бизнес-приложениях со сложными моделями данных. В частности, в результатах поиска отображаются не только сущности, напрямую содержащие в некотором атрибуте искомую строку, но и связанные сущности, при отображении которых используется этот атрибут. Например, если сущность Order (заказ) содержит ссылку на Customer (покупателя), и строка поиска содержит название покупателя, то в результатах поиска будет отображен и найденный покупатель, и заказ, на него ссылающийся. Это поведение является логичным для пользователя, который обычно видит название покупателя в экране редактирования заказа.

Результаты поиска фильтруются с учетом ограничений, накладываемых подсистемой безопасности платформы. То есть, если группа доступа текущего пользователя не разрешает загрузку некоторых экземпляров сущностей, они не будут отображатся в результатах поиска.

Подсистема полнотекстового поиска содержит два взаимосвязанных механизма: индексирование и собственно поиск.

1.1. Индексирование

Если в приложении подключен компонент fts и включено свойство fts.enabled, то при каждом сохранении в базу данных сущности, подлежащей индексированию, ее идентификатор записывается в очередь на индексацию - таблицу SYS_FTS_QUEUE.

Чтобы процесс индексирования запускался автоматически в фоновом режиме, необходимо создать и активировать назначенное задание. Отдельный асинхронный процесс периодически извлекает идентификаторы изменившихся сущностей из очереди, загружает экземпляры сущностей и индексирует их. Индексация производится с помощью библиотеки Apache Lucene. Документ Lucene содержит следующие поля:

  • Имя сущности и идентификатор экземпляра.

  • Поле all - конкатенация индексируемых атрибутов сущности, но только локальных и типа FileDescriptor. Если атрибут имеет тип FileDescriptor, то индексируется содержимое соответствующего файла. Локальные атрибуты могут быть одного из следующих типов: строка, число, дата, перечисление.

  • Поле links - конкатенация идентификаторов сущностей, которые содержатся в ссылочных индексируемых атрибутах.

Под индексируемыми атрибутами понимаются атрибуты данной сущности и, возможно, связанных с ней сущностей, объявленные в дескрипторе FTS.

Индекс хранится в файловой системе, по умолчанию в подкаталоге ftsindex рабочего каталога приложения (задаваемого свойством cuba.dataDir), в стандартном варианте развертывания это tomcat/work/app-core/ftsindex. Расположение индекса можно изменить с помощью свойства fts.indexDir.

Поиск ведется по следующим правилам:

  • если искомая строка обрамлена кавычками, то ищется соответствующая фраза - набор таких же слов в том же порядке, игнорируя знаки пунктуации;

  • если искомая строка начинается с символа "*", то производится поиск по вхождению строки в любой части слова индексированных данных;

  • в противном случае поиск производится по совпадению искомой строки с началом слов индексированных данных.

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

Алгоритм поиска состоит из двух этапов:

  • Cначала искомая строка ищется в поле all документов Lucene. Найденные сущности добавляются в список результатов.

  • Если что-то найдено на первом этапе, то идентификаторы найденных сущностей ищутся в поле links документов Lucene. Найденные на втором этапе сущности также добавляются в список результатов.

Warning

Если строка поиска состоит из нескольких слов (и не обрамлена кавычками), то будет произведен поиск всех слов по отдельности по условию ИЛИ. То есть в результаты поиска попадут сущности, содержащие хотя бы одно из введенных слов.

1.3. Пример индексирования и поиска

Рассмотрим приведенный выше простейший пример со связанными сущностями Order и Customer.

Example1Classes

В данном случае, если все атрибуты объектов являются индексируемыми, при индексации двух связанных экземпляров Order и Customer будут созданы два документа Lucene примерно следующего содержания:

id: Order.id = "b671dbfc-c431-4586-adcc-fe8b84ca9617"
all: Order.number + Order.date + Order.amount = "001^2013-11-14^1000"
links: Customer.id = "f18e32bb-32c7-477a-980f-06e9cc4e7f40"
id: Customer.id = "f18e32bb-32c7-477a-980f-06e9cc4e7f40"
all: Customer.name + Customer.email = "John Doe^john.doe@mail.com"

Теперь предположим, что ищется строка "john":

  • Сначала производится поиск в полях all всех документов. Будет найден Customer, и добавлен в результаты поиска.

  • Затем будет произведен поиск идентификатора найденного покупателя в полях links всех документов. Будет найден Order, и он также будет добавлен в результаты поиска.

2. Быстрый старт

В данной главе мы рассмотрим применение подсистемы полнотекстового поиска в приложении-примере Библиотека, который может быть загружен с помощью CUBA Studio.

Разобьем задачу на следующие этапы:

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

  2. Настроим конфигурационный файл FTS для работы с сущностями модели данных примера Библиотека.

  3. Для иллюстрации возможностей поиска по загруженным файлам используем сущность BookPublication и функциональность загрузки файлов, описанную в разделе Загрузка файла в хранилища в Руководстве по разработке приложений.

2.1. Настройка проекта

  1. Запустите CUBA Studio, перейдите в окно Open project > Samples и загрузите проект Library.

  2. Откройте проект Library в Studio.

  3. Откройте окно свойств проекта Project properties → Edit и в списке App components включите компонент fts, затем сохраните изменения. Studio предложит пересоздать скрипты Gradle - согласитесь.

  4. Запустите Run → Deploy. На этом этапе будет произведена сборка приложения и оно будет развернуто на сервере Tomcat в подкаталоге build/tomcat.

  5. Создайте базу данных приложения: Run → Create database.

  6. Запустите сервер приложения: Run → Start application server.

  7. Откройте веб-интерфейс приложения по адресу http://localhost:8080/app. Войдите в систему с именем admin и паролем admin.

  8. Для того, чтобы включить функциональность полнотекстового поиска, в главном меню приложения откройте Administration → Application properties, найдите и откройте список fts в таблице свойств, двойным щелчком откройте атрибут fts.enabled и выберите true в поле флажок Current value.

    fts enabled true

После выполнения вышеописанных действий функциональность полнотекстового поиска подключена к приложению и готова к работе. Если выйти из системы и снова выполнить логин, в правой части верхней панели главного окна приложения появится поле поиска. Кроме того, полнотекстовый поиск может использоваться в UI-компоненте Filter.

Однако поиск не будет давать результатов, так как никакие данные еще не проиндексированы.

Для однократного запуска индексации текущего состояния базы данных (а точнее, сущностей, описанных в конфигурационном файле FTS по умолчанию), откройте в главном меню Administration → JMX Console, найдите JMX-бин app-core.fts:type=FtsManager и вызовите последовательно сначала метод reindexAll(), а затем processQueue().

jmx fts setup

После этого поиск например строки "adm" должен выдавать следующие результаты:

2.1 project setup

2.2. Настройка вызова процесса индексирования

Для периодического вызова процесса индексирования удобно воспользоваться механизмом назначенных заданий платформы (см. Руководство по разработке приложений → Назначенные задания CUBA).

Сначала необходимо активировать весь механизм запуска задач. Добавьте в файл app.properties модуля core проекта приложения следующее свойство:

cuba.schedulingActive = true

Перезапустите сервер приложения, войдите в систему пользователем admin, откройте экран JMX Console, найдите и откройте JMX-бин app-core.cuba:type=Scheduling и убедитесь, что атрибут Active имеет значение true.

Далее откройте экран Administration → Scheduled Tasks, нажмите Create и задайте следующие значения атрибутов новой задачи:

  • Defined by: Bean

  • Bean name: cuba_FtsManager

  • Method name: processQueue()

  • Singleton: true

  • Period, sec: 30

Сохраните задачу, выделите ее в таблице и нажмите Activate. С этого момента каждые 30 секунд будет вызываться процесс индексирования измененных сущностей.

Warning

Автоматическое индексирование не затрагивает сущности, созданные до его запуска. Для постановки таких сущностей в очередь на индексацию воспользуйтесь методами reindexAll() или asyncReindexAll() JMX-бина app-core.fts:type=FtsManager. Подробнее см. Запуск и настройка переиндексации сущностей.

2.3. Настройка файла конфигурации

При добавлении базового проекта fts в модуле core создаётся новый файл fts.xml. Проверьте его содержимое и при необходимости отредактируйте, включив в него те сущности, которые должны участвовать в полнотекстовом поиске.

<fts-config>
    <entities>

        <entity class="com.sample.library.entity.Author">
            <include re=".*"/>
        </entity>

        <entity class="com.sample.library.entity.Book">
            <include re=".*"/>
        </entity>

        <entity class="com.sample.library.entity.BookInstance">
            <include re=".*"/>
        </entity>

        <entity class="com.sample.library.entity.BookPublication">
            <include re=".*"/>
        </entity>

        <entity class="com.sample.library.entity.LibraryDepartment">
            <include re=".*"/>
        </entity>

        <entity class="com.sample.library.entity.LiteratureType">
            <include re=".*"/>
        </entity>

        <entity class="com.sample.library.entity.Publisher">
            <include re=".*"/>
        </entity>

        <entity class="com.sample.library.entity.Town">
            <include re=".*"/>
        </entity>

    </entities>
</fts-config>

Это файл конфигурации FTS, в данном случае включающий в индексирование все сущности предметной области со всеми их атрибутами.

Добавьте в файл app.properties модуля core приложения следующее свойство:

cuba.ftsConfig = +com/sample/library/fts.xml

В результате индексироваться будут и сущности, определенные в платформе в файле cuba-fts.xml, и описанные в файле проекта fts.xml.

Перезапустите сервер приложения. На данном этапе полнотекстовый поиск должен работать по всем сущностям модели приложения, а также по сущностям подсистемы безопасности платформы: Role, Group, User.

2.4. Поиск по содержимому загруженных файлов

Теперь добавим функциональность загрузки файлов для каждой публикации и их отображение на экране списка сущности BookPublication.

Для начала необходимо внести изменения в BookPublication. Добавьте новый атрибут file, который будет являться ссылкой на сущность FileDescriptor с отношением много-к-одному. FileDescriptor - это описатель загруженного файла (не путать с java.io.FileDescriptor), позволяющий ссылаться на файл из объектов модели данных. При сохранении изменений выберите все экраны и представления, предложенные Studio, для добавления в них нового атрибута.

book publication new attribute

Сгенерируйте новые скрипты обновления БД, выполните команду обновления базы данных и перезапустите сервер приложения. При пересоздании базы данных полнотекстовый поиск по умолчанию отключается. Снова включите флажок Value для атрибута Enable в экране JMX Console, выполните индексацию всех файлов, выйдите из системы снова выполните логин.

Так как мы добавили новый атрибут, в таблице публикаций на экране списка сущности BookPublication теперь появился новый пустой столбец: File. Чтобы его заполнить, откройте экран редактирования строки таблицы, с помощью нового поля File загрузите текстовый файл в систему и нажмите OK. По умолчанию CUBA поддерживает следующие форматы файлов: RTF, TXT, DOC, DOCX, XLS, XSLX, ODT, ODS и PDF.

book publication file is not

Новые файлы теперь отображаются в таблице. Внешний вид таблицы можно отредактировать.

book publication files uploaded

Чтобы переиндексировать имеющиеся в базе данных сущности и файлы в соответствии с новой конфигурацией поиска, откройте в экране JMX Console JMX-бин app-core.fts:type=FtsManager и вызовите последовательно сначала метод reindexAll(), а затем processQueue(). Все вновь добавляемые и изменяемые данные будут индексироваться автоматически, с задержкой, определяемой интервалом вызова назначенного задания, т.е. не более 30 секунд.

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

book publication fts result

Более подробную информацию о FileStorageAPI и FileDescriptor вы можете найти в соответствующих разделах основного руководства.

2.5. Запуск и настройка переиндексации сущностей

Если полнотекстовый поиск был подключен в момент, когда в систему уже внесены какие-либо данные, то эти данные нужно проиндексировать. Добавление записей в очередь на индексацию осуществляется с помощью методов JMX-бина app-core.fts:type=FtsManager. Удобный способ вызвать метод JMX-бина это воспользоваться экраном JMX Console пункта меню Администрирование.

JMX-бин app-core.fts:type=FtsManager предоставляет два метода для постановки сущностей в очередь на индексацию:

  • reindexAll() - синхронно добавляет все сущности, описанные в файле конфигурации FTS, в очередь на индексацию. При больших объемах данных этот процесс может занять длительное время, и в этом случае рекомендуется воспользоваться методом asyncReindexAll().

  • asyncReindexAll() - сущности добавляются в очередь на индексацию пакетами с помощью метода FtsManager.reindexNextBatch(). Размер пакета задается конфигурационным параметром fts.reindexBatchSize. Метод FtsManager.reindexNextBatch() должен вызываться механизмом назначенных заданий или с помощью планировщика Spring. Пока формирование очереди не завершено, индексация не производится.

Приложение A: Файл конфигурации FTS

Файл конфигурации полнотекстового поиска представляет собой XML-файл, как правило располагающийся в каталоге src модуля core и содержащий описание индексируемых сущностей и их атрибутов.

Файл конфигурации FTS задается в свойстве приложения cuba.ftsConfig.

Рассмотрим структуру файла.

fts-config - корневой элемент.

Элементы fts-config:

  • entities - список сущностей, подлежащих индексированию и поиску.

    Элементы entities:

    • entity - описание индексируемой сущности.

      Атрибуты entity:

      • class - Java класс сущности.

      • show - должна ли данная сущность показываться в результатах поиска самостоятельно. Значение false используется для сущностей-связей, которые не интересны пользователю сами по себе, но нужны, например, для связи загруженных файлов и сущностей предметной области. По умолчанию true.

        Элементы entity:

      • include - включить атрибут или несколько атрибутов сущности в индекс.

        Атрибуты include:

        • re - регулярное выражение для отбора атрибутов по имени.

        • name - имя атрибута. Может быть путем (через точку) по ссылочным атрибутам. Тип не проверяется, однако если имя является путем, то конечный атрибут должен быть сущностью, а не простым типом (атрибут простого типа не имеет здесь смысла, он должен индексироваться в своей сущности).

      • exclude - исключить ранее включенный атрибут. Возможные атрибуты такие же, как в элементе include.

      • searchables - Groovy-скрипт для добавления в очередь на индексирование произвольных сущностей, связанных с измененной.

        Например, когда изменяется (добавляется, удаляется) экземпляр CardAttachment, мы должны также переиндексировать связанный с ним экземпляр Card, так как сам собой Card в очередь не встанет, ибо не менялся.

        При запуске в скрипт передаются следующие переменные:

        • searchables - список сущностей, который нужно пополнять.

        • entity - текущий экземпляр сущности, помещаемый в очередь автоматически.

          Пример скрипта:

          <entity class="com.haulmont.workflow.core.entity.CardAttachment" show="false">
              ...
              <searchables>
                  searchables.add(entity.card)
              </searchables>
          </entity>
      • searchableIf - Groovy-скрипт для ограничения помещения в очередь некоторых экземпляров индексируемой сущности.

        Например, может быть не нужно индексировать старые версии документов.

        При запуске в скрипт передается переменная entity - текущий экземпляр сущности. Скрипт должен вернуть булевское значение - true для того чтобы индексировать текущий экземпляр, false чтобы игнорировать его.

        Пример скрипта:

        <entity class="com.haulmont.docflow.core.entity.Contract">
            ...
            <searchableIf>
                entity.versionOf == null
            </searchableIf>
        </entity>

Пример файла конфигурации FTS:

<fts-config>
    <entities>

        <entity class="com.sample.library.entity.Author">
            <include re=".*"/>
        </entity>

        <entity class="com.sample.library.entity.Book">
            <include re=".*"/>
        </entity>

        <entity class="com.sample.library.entity.BookInstance">
            <include re=".*"/>
        </entity>

        <entity class="com.sample.library.entity.BookPublication">
            <include re=".*"/>
        </entity>

        <entity class="com.sample.library.entity.Publisher">
            <include re=".*"/>
        </entity>

        <entity class="com.sample.library.entity.EBook">
            <include name="publication.book"/>
            <include name="attachments.file"/>
        </entity>

        <entity class="com.haulmont.workflow.core.entity.CardAttachment" show="false">
            <include re=".*"/>
            <exclude name="card"/>

            <searchables>
                searchables.add(entity.card)
            </searchables>
        </entity>

    </entities>
</fts-config>

Приложение B: Свойства приложения

В данном разделе перечислены свойства приложения, имеющие отношение к подсистеме полнотекстового поиска.

cuba.ftsConfig

Аддитивный конфигурационный параметр, задает файл конфигурации FTS проекта.

Файл загружается с помощью интерфейса Resources, поэтому может быть расположен в classpath или в конфигурационном каталоге.

Используется в блоке Middleware.

Пример:

cuba.ftsConfig = +com/company/sample/fts.xml

Все свойства, описанные ниже, являются параметрами времени выполнения, хранятся в базе данных и доступны в коде приложения через конфигурационный интерфейс FtsConfig.

fts.enabled

Флаг, разрешающий использование функциональности FTS в проекте.

Значение данного флага может быть оперативно изменено с помощью атрибута Enabled JMX-бина app-core.fts:type=FtsManager.

Значение по умолчанию: false

fts.indexDir

Абсолютный путь к каталогу для хранения индексных файлов. Если не установлен, используется подкаталог ftsindex рабочего каталога приложения (задаваемого свойством cuba.dataDir), в стандартном варианте развертывания это tomcat/work/app-core/ftsindex.

Значение по умолчанию: не установлено

fts.indexingBatchSize

Количество записей, извлекаемое из очереди на индексирование за один вызов метода processQueue().

Данное ограничение актуально для ситуации, когда в очереди на индексацию оказывается сразу очень большое число записей, например после выполнения метода reindexAll() JMX-бина app-core.fts:type=FtsManager. В этом случае индексация выполняется порциями, что занимает больше времени, но создает ограниченную и предсказуемую нагрузку на сервер.

Значение по умолчанию: 300

fts.reindexBatchSize

Количество записей, помещаемое в очередь на индексацию за один вызов метода reindexNextBatch().

Значение по умолчанию: 5000

fts.maxSearchResults

Максимальное количество результатов поиска.

Значение по умолчанию: 100

fts.searchResultsBatchSize

Количество элементов в одной порции результатов поиска. Для показа следующей порции пользователь должен нажать More на экране результатов.

Значение по умолчанию: 5

. . .