Предисловие
Данный документ является руководством по применению подсистемы полнотекстового поиска (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.
1.2. Поиск
Поиск ведется по следующим правилам:
-
если искомая строка обрамлена кавычками, то ищется соответствующая фраза - набор таких же слов в том же порядке, игнорируя знаки пунктуации;
-
если искомая строка начинается с символа "*", то производится поиск по вхождению строки в любой части слова индексированных данных;
-
в противном случае поиск производится по совпадению искомой строки с началом слов индексированных данных.
Для русского и английского языка поиск производится с учетом морфологии.
Алгоритм поиска состоит из двух этапов:
-
Cначала искомая строка ищется в поле
all
документов Lucene. Найденные сущности добавляются в список результатов. -
Если что-то найдено на первом этапе, то идентификаторы найденных сущностей ищутся в поле
links
документов Lucene. Найденные на втором этапе сущности также добавляются в список результатов.
Warning
|
Если строка поиска состоит из нескольких слов (и не обрамлена кавычками), то будет произведен поиск всех слов по отдельности по условию ИЛИ. То есть в результаты поиска попадут сущности, содержащие хотя бы одно из введенных слов. |
1.3. Пример индексирования и поиска
Рассмотрим приведенный выше простейший пример со связанными сущностями Order
и Customer
.
В данном случае, если все атрибуты объектов являются индексируемыми, при индексации двух связанных экземпляров 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.
Разобьем задачу на следующие этапы:
-
Подключим функциональность поиска к проекту, настроим вызов процесса индексирования и убедимся в его работоспособности.
-
Настроим конфигурационный файл FTS для работы с сущностями модели данных примера Библиотека.
-
Для иллюстрации возможностей поиска по загруженным файлам используем сущность
BookPublication
и функциональность загрузки файлов, описанную в разделе Загрузка файла в хранилища в Руководстве по разработке приложений.
2.1. Настройка проекта
-
Запустите CUBA Studio, перейдите в окно Open project > Samples и загрузите проект Library.
-
Откройте проект Library в Studio.
-
Откройте окно свойств проекта Project properties → Edit и в списке App components включите компонент fts, затем сохраните изменения. Studio предложит пересоздать скрипты Gradle - согласитесь.
-
Запустите Run → Deploy. На этом этапе будет произведена сборка приложения и оно будет развернуто на сервере Tomcat в подкаталоге
build/tomcat
. -
Создайте базу данных приложения: Run → Create database.
-
Запустите сервер приложения: Run → Start application server.
-
Откройте веб-интерфейс приложения по адресу http://localhost:8080/app. Войдите в систему с именем
admin
и паролемadmin
. -
Для того, чтобы включить функциональность полнотекстового поиска, в главном меню приложения откройте Administration → Application properties, найдите и откройте список
fts
в таблице свойств, двойным щелчком откройте атрибут fts.enabled и выберите true в поле флажок Current value.
После выполнения вышеописанных действий функциональность полнотекстового поиска подключена к приложению и готова к работе. Если выйти из системы и снова выполнить логин, в правой части верхней панели главного окна приложения появится поле поиска. Однако поиск не будет давать результатов, так как никакие данные еще не проиндексированы.
Для однократного запуска индексации текущего состояния базы данных (а точнее, сущностей, описанных в конфигурационном файле FTS по умолчанию), откройте в главном меню Administration → JMX Console, найдите JMX-бин app-core.fts:type=FtsManager
и вызовите последовательно сначала метод reindexAll()
, а затем processQueue()
. После этого поиск например строки "adm" должен выдавать следующие результаты:
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
|
Автоматическое индексирование не затрагивает сущности, созданные до его запуска. Для постановки таких сущностей в очередь на индексацию воспользуйтесь методами |
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 = cuba-fts.xml fts.xml
В результате индексироваться будут и сущности, определенные в платформе в файле cuba-fts.xml
, и описанные в файле проекта fts.xml
.
Перезапустите сервер приложения. На данном этапе полнотекстовый поиск должен работать по всем сущностям модели приложения, а также по сущностям подсистемы безопасности платформы: Role
, Group
, User
.
2.4. Поиск по содержимому загруженных файлов
Теперь добавим функциональность загрузки файлов для каждой публикации и их отображение на экране списка сущности BookPublication
.
Для начала необходимо внести изменения в BookPublication
. Добавьте новый атрибут file
, который будет являться ссылкой на сущность FileDescriptor
с отношением много-к-одному. FileDescriptor
- это описатель загруженного файла (не путать с java.io.FileDescriptor
), позволяющий ссылаться на файл из объектов модели данных. При сохранении изменений выберите все экраны и представления, предложенные Studio, для добавления в них нового атрибута.
Сгенерируйте новые скрипты обновления БД, выполните команду обновления базы данных и перезапустите сервер приложения. При пересоздании базы данных полнотекстовый поиск по умолчанию отключается. Снова включите флажок Value для атрибута Enable в экране JMX Console, выполните индексацию всех файлов, выйдите из системы снова выполните логин.
Так как мы добавили новый атрибут, в таблице публикаций на экране списка сущности BookPublication
теперь появился новый пустой столбец: File. Чтобы его заполнить, откройте экран редактирования строки таблицы, с помощью нового поля File загрузите текстовый файл в систему и нажмите OK. По умолчанию CUBA поддерживает следующие форматы файлов: RTF
, TXT
, DOC
, DOCX
, XLS
, XSLX
, ODT
, ODS
и PDF
.
Новые файлы теперь отображаются в таблице. Внешний вид таблицы можно отредактировать.
Чтобы переиндексировать имеющиеся в базе данных сущности и файлы в соответствии с новой конфигурацией поиска, откройте в экране JMX Console JMX-бин app-core.fts:type=FtsManager
и вызовите последовательно сначала метод reindexAll()
, а затем processQueue()
. Все вновь добавляемые и изменяемые данные будут индексироваться автоматически, с задержкой, определяемой интервалом вызова назначенного задания, т.е. не более 30 секунд.
В результате, полнотекстовый поиск будет выводить все результаты, включая вхождения в содержимом загруженных файлов.
Более подробную информацию о 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