前言
本文档可作为使用 CUBA 框架的 全文搜索(FTS)插件的指南。
目标读者
本手册适用于构建支持全文搜索的 CUBA 应用的开发人员。本手册假设读者已熟悉 开发人员手册 。
附加材料
本指南以及任何其它 CUBA 框架文档均可从 https://www.cuba-platform.com/documentation 获取。
CUBA 全文搜索插件基于 Apache Lucene 框架,因此熟悉该框架对于理解全文搜索会有很大的帮助。见 http://lucene.apache.org/core 。
反馈
如果您有任何改进本手册的建议,请随时在 GitHub 上的源码仓库报告问题。如果你看到拼写或措辞错误、bug 或不一致,请毫不犹豫地 fork 并修复。谢谢!
1. FTS 插件概述
CUBA 框架的全文搜索(FTS)功能在实体属性和上传文件的内容的内提供非结构化搜索。
CUBA 框架全文搜索的一个独特之处在于它专注于具有复杂数据模型的业务应用程序。特别是搜索结果不仅包括直接包含搜索关键字的实体,还包括在显示时使用了该实体类型属性的相关实体。例如,如果 Order
实体包含指向 'Customer` 的链接,并且搜索关键字是客户的名称,则搜索结果会包括 Customer
和相关的 Order
。对于通常在订单编辑界面中看到客户名称的用户来说,这种处理是合理的。
搜索结果也受框架安全子系统的约束,即如果当前用户的访问组不允许访问某些实体实例,则此类实例不会出现在搜索结果中。
全文搜索插件包含两个相互关联的机制:索引和搜索。
1.1. 索引
如果将 FTS 插件添加到应用程序,并且启用了 fts.enabled 属性,那么每次将可索引实体保存到数据库时,其标识符都会添加到索引队列表 - SYS_FTS_QUEUE 。
要使索引进程在后台自动运行,需要创建并激活计划任务。然后,单独的异步进程定期从队列中提取已更改实体的标识符,加载实体实例并对其进行索引。索引机制使用 Apache Lucene 库执行索引。Lucene 文档包含以下字段:
-
实体名称和实例标识符。
-
all
- 被索引实体的所有属性值的连接起来的字符串,其中仅包含 local 和FileDescriptor
类型属性。如果属性具有FileDescriptor
类型,则系统将索引相应文件的内容。本地属性可以是以下类型:字符串、数字、日期、枚举。 -
links
- 一些实体标识符的连接起来的字符串,这些标识符是引用类型的属性值对应的标识符。
被索引的属性是实体和相关实体(如果有)的属性,它们在 FTS 描述中定义。
索引存储在文件系统中; 默认情况下,它位于应用程序工作目录的 ftsindex
子文件夹中(由 cuba.dataDir 属性
定义); 对于标准部署,此目录是 tomcat/work/app-core/ftsindex
。可以使用fts.indexDir属性更改索引位置。
1.2. 搜索
根据以下规则执行搜索:
-
如果搜索词包含在引号中,系统将搜索相应的 短语(phrase) - 忽略标点符号的相同顺序的同一组词,即精确匹配。
-
如果搜索词以“*”开头,系统会将搜索词作为已索引单词的子串进行匹配。
-
否则,通过搜索词与索引单词的开头相匹配的方式进行搜索。
俄语和英语中的搜索是以单词的形式进行的。
搜索算法包含两个阶段:
-
首先,在 Lucene 文档的
all
字段中查找搜索词。所有找到的实体都会添加到结果列表中。 -
如果第一阶段产生结果,则在 Lucene 文档的
links
字段对第一阶段产生的实体的标识符进行搜索。在第二阶段找到的所有实体也会添加到搜索结果列表中。
Warning
|
如果搜索字符串包含多个单词(未用引号括起来),系统将使用 OR 条件分别搜索每个单词。即搜索结果将包含至少匹配一个输入单词的实体。 |
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 项目树中双击 Add-ons。
-
选择 Marketplace 标签页,然后找到 Full Text Search 扩展。
-
点击 Install 按钮,然后选择 Apply & Close。
-
在对话框中点击 Continue 。
然后会安装与当前平台版本兼容的扩展版本。
3. 快速开始
本章介绍在 Library 示例应用程序中使用全文搜索插件的示例,该示例应用程序源码在 GitHub 。 我们将把任务分为以下几个阶段:
3.1. 项目设置
-
下载 并解压 Library 应用程序的源代码仓库,或者直接从 git 克隆:
git clone https://github.com/cuba-platform/sample-library-cuba7
-
按照 CUBA Studio 用户手册 中 打开已有项目 部分的描述打开 Library 项目。
-
按照 安装 章节的描述,通过 CUBA Add-Ons 窗口为项目添加 Full Text Search 扩展。
-
在本地 HyperSQL 服务创建数据库:在主菜单点击 CUBA → Create database。
-
启动应用程序:点击主工具栏中
CUBA Application
配置旁边的 按钮。可以使用 CUBA 项目树中 Runs at… 部分的链接直接在 Studio 中通过浏览器打开应用程序。 -
打开 library 应用程序。
用户名和密码为
admin
/admin
。 -
要启用全文搜索功能,请在应用程序的主菜单中打开 Administration → Application properties,找到并打开表格中的
fts
属性列表。双击打开 fts.enabled 属性并在 Current value 字段中选择 true。Figure 2. fts.enabled
完成上述步骤后,全文搜索功能将被添加到应用程序中并准备工作。如果退出系统然后再次登录,则主应用程序窗口的右上方面板中将显示一个搜索输入框。全文搜索也可以在 过滤器 UI 组件中使用。
但是,这时搜索将不会产生任何结果,因为数据尚未编入索引。
要启动对当前数据库的(即默认情况下在 FTS 配置文件中列出的实体)进行一次性索引,请打开 Administration → JMX Console,找到 app-core.fts:type=FtsManager
JMX bean 然后首先调用 reindexAll()
,再调用 processQueue()
。
在此之后,搜索 "adm" 字符串则会给出以下结果:
3.2. 配置索引过程的调用
可以使用框架的 定时任务机制 按计划调用建索引过程。
首先,需要激活任务启动功能。将以下属性添加到项目 core 模块的 app.properties
文件中。
cuba.schedulingActive = true
重启应用服务,以 admin
的身份登录系统,打开 JMX Console 界面,找到并打开 app-core.cuba:type=Scheduling
JMX bean 确保 Active 属性被设置为 true
。
然后打开 Administration → Scheduled Tasks 界面,单击 Create 并为新任务填写以下属性值:
-
Defined by: Bean
-
Bean name: cuba_FtsManager
-
Method name: processQueue()
-
Singleton: true
-
Period, sec: 30
保存任务,在表格中选择它,然后单击 Activate。从现在开始,系统将开始每隔 30 秒为已更改的实体建索引。
Warning
|
自动索引不包括在启动之前创建的实体。要将此类实体加入索引队列,请使用 |
3.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 配置文件,在示例中它可以为所有领域模型实体及其所有属性建立索引。
以下属性会自动添加到应用程序 core 模块的 app.properties
文件中:
cuba.ftsConfig = +com/sample/library/fts.xml
这样,索引将包括平台的 com/haulmont/fts/fts.xml
文件和项目的 fts.xml
文件中定义的实体。
重启应用服务。从现在开始,全文搜索应该适用于应用程序模块的所有实体以及平台安全子系统的实体:Role
、Group
、User
。
3.4. 上传文件内容搜索
现在需要为每本图书出版物提供文件上传的功能,并将上传的文件添加到 BookPublication
浏览界面。
自定义 BookPublication
实体。首先,添加一个新的 file
属性,它与 FileDescriptor
实体多对一关联。FileDescriptor
是上传的文件的描述(不要与 java.io.FileDescriptor
混淆),它允许从数据模型对象中引用文件。
保存更改,然后将新属性附加到现有的 bookPublication.full
视图。之后,需要在 BookPublication
浏览和编辑界面添加 File
属性,将光标定位在包含属性的那行,然后按下 Alt+Enter。选择 Add entity attribute to screens,然后在弹出对话框中选择需要添加的页面。
生成新的 DB 脚本,更新数据库并重新启动应用程序服务。如果重新创建了 DB,则默认情况下将禁用全文搜索。在 JMX Console 复选框中再次选中 Value ,对所有文件重建索引,处理索引队列,注销并重新登录。
就所添加的新属性而言,BookPublication
浏览界面上的出版物的表格会增加一列:File。要填写这列,请打开任意行进行编辑,使用新上传控件上传文本文件,然后单击 OK。默认情况下,CUBA 支持对 RTF
、TXT
、DOC
、DOCX
、XLS
、XSLX
、ODT
、ODS
和 PDF
格式的文件建立索引。
新文件会出现在表格中。可以调整新列的外观。
打开 JMX Console 界面,打开 app-core.fts:type=FtsManager
JMX bean 并依次调用 reindexAll()
和 processQueue()
方法根据新的搜索配置来对数据库和文件中的现有实例重建索引。对所有新增和改变的数据自动建索引会有延迟,这取决于定时任务的间隔时间,即不超过 30 秒。
现在,Full text search 将输出所有记录,包括外部文件内容。
3.5. 运行和配置实体索引重建
可能启用全文搜索功能前已将某些数据添加到了数据库,这些数据应该被建立一次索引。可以使用 app-core.fts:type=FtsManager
JMX-bean 的方法将实体添加到索引队列。调用 JMX-bean 方法的一种简便方法是通过 Administration 菜单的 s JMX Console 界面。
JMX-bean app-core.fts:type=FtsManager
提供了两种将实体添加到索引队列的方法:
-
reindexAll()
- 将 FTS 配置中描述的实体同步添加到索引队列。如果数据量很大,这个过程可能会花费大量时间,因此建议使用asyncReindexAll()
。 -
asyncReindexAll()
- 使用FtsManager.reindexNextBatch()
方法批量异步地将实体添加到索引队列中。批量大小由fts.reindexBatchSize配置参数定义。FtsManager.reindexNextBatch()
方法应由定时任务机制或 Spring 定时任务调用。在完成索引队列构建之前,索引不会被执行。
Appendix A: FTS 配置文件
全文搜索配置文件是一个 XML 文件,通常位于 core 模块的 src
目录中,包含索引实体及其属性的描述。
该文件在cuba.ftsConfig应用程序属性中指定。
该文件的结构如下:
fts-config
- 根元素。
fts-config
元素:
-
entities
- 要建索引和搜索的实体列表。entities
元素:-
entity - 索引实体描述。
entity
属性:-
class
- 实体 Java 类。 -
show
- 定义此实体是否应出现在搜索结果中,false
值是用来设置不显示用户不感兴趣的关联实体,但这些实体是必须的,例如,链接上传的文件和其关联的域模型的实体。默认值为true
。
entity
元素:-
include
- 确定一个或多个需要作为索引的实体属性。include
属性:-
re
- 按名称选择属性的正则表达式。 -
name
- 属性名。它可以是引用属性路径(以点分隔)。不检查类型。但是,如果名称使用路径定义,则可以有两个选项:-
最终属性必须是非嵌入式实体。包含非实体类型属性在这里没有意义,因为它必须在其拥有的实体内建索引。
-
最终属性必须是嵌入实体的非实体字段。例如,如果索引实体具有 Address 类型(可嵌入实体)的"address"字段,则属性名称应为"address.city"或"address.street",而不是"address"。
-
-
-
-
-
exclude
- 排除之前include
元素中包含的属性,配置规则与include
元素相同 。 -
searchables
- 一个 Groovy 脚本,用于将与更改实体关联的任意实体添加到索引队列。例如,当添加或删除
CardAttachment
实例时,关联的Card
实例应该被重建索引。原因是Card
实例本身不会被添加到队列中,因为它没有被更改(它存储了一个CardAttachment
实例的集合)。这样,如果在其链接实体(新添加的CardAttachment
)中找到匹配数据,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>
Appendix B: 应用属性
本节列出了与全文搜索插件相关的应用程序属性。
- cuba.ftsConfig
-
定义项目的 FTS 配置文件的累加属性。
该文件使用
Resources
接口加载,因此它可以位于类路径或配置目录中。用于中间件 block。
例如:
cuba.ftsConfig = +com/company/sample/fts.xml
- cuba.gui.genericFilterFtsTableTooltipsEnabled
-
该标记可以启用在表格和数据网格组件中生成提示窗。提示窗包含搜索到实体的属性信息。生成提示窗会耗费很长时间,所以默认关闭。
配置接口:
ClientConfig
保存在数据库。
默认值:
false
- cuba.gui.genericFilterFtsDetailsActionEnabled
-
使用通用过滤器组件对表格或者数据网格进行全文检索后,使用该标记可以启用 "全文检索详情" 右键操作。
配置接口:
ClientConfig
保存在数据库。
默认值:
true
下面描述的所有属性都是存储在数据库中的运行时参数,可通过 FtsConfig
配置接口在应用程序代码中使用。
- fts.enabled
-
在项目中启用 FTS 功能的标志。
可以通过
app-core.fts:type=FtsManager
JMX bean 的 Enabled 属性进行更改。默认值:
false
- fts.indexDir
-
存储索引文件目录的绝对路径。如果未指定,则使用应用程序工作目录(由 cuba.dataDir 属性定义)的
ftsindex
子目录;在默认部署的配置中,它是 tomcat/work/app-core/ftsindex。默认值: 未指定
- fts.indexingBatchSize
-
每次调用
processQueue()
从索引队列中获取的记录数。此限制对于索引队列包含大量记录的情况下很重要,例如,在执行
app-core.fts:type=FtsManager
JMX bean 的reindexAll()
方法之后。在这种情况下,索引是分批完成的,这需要更多时间,但产生的服务器负载将是有限且可预测的。默认值:
300
- fts.reindexBatchSize
-
每次调用
reindexNextBatch()
时放入索引队列的记录数。默认值:
5000
- fts.maxSearchResults
-
搜索结果中实体的最大数。
默认值:
100