前言

本文档可作为使用 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属性更改索引位置。

根据以下规则执行搜索: * 如果搜索词包含在引号中,系统将搜索相应的 短语(phrase) - 忽略标点符号的相同顺序的同一组词,即精确匹配。 * 如果搜索词以“*”开头,系统会将搜索词作为已索引单词的子串进行匹配。 * 否则,通过搜索词与索引单词的开头相匹配的方式进行搜索。

俄语和英语中的搜索是以单词的形式进行的。

搜索算法包含两个阶段:

  • 首先,在 Lucene 文档的 all 字段中查找搜索词。所有找到的实体都会添加到结果列表中。

  • 如果第一阶段产生结果,则在 Lucene 文档的 links 字段对第一阶段产生的实体的标识符进行搜索。在第二阶段找到的所有实体也会添加到搜索结果列表中。

Warning

如果搜索字符串包含多个单词(未用引号括起来),系统将使用 OR 条件分别搜索每个单词。即搜索结果将包含至少匹配一个输入单词的实体。

1.3. 索引和搜索示例

考虑一下上面提到的相关联的 OrderCustomer 实体的简单情况。

Example1Classes
Figure 1. 数据模型

在这种情况下,如果所有对象属性都被编入索引,则对 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. 快速开始

本章介绍在 Library 示例应用程序中使用全文搜索插件的示例,该示例应用程序源码在 GitHub 。 我们将把任务分为以下几个阶段:

  1. 为项目启用搜索功能,配置索引处理并验证其是否有效。

  2. 调整 FTS 配置文件以包含示例 Library 数据模型中的实体。

  3. 使用 BookPublication 实体和 开发者手册文件存储 部分中描述的文件上传功能来演示对已加载文件的搜索功能。

2.1. 项目设置

  1. 下载 并解压 Library 应用程序的源代码仓库,或者直接从 git 克隆:

      git clone https://github.com/cuba-platform/sample-library-cuba7
  2. 按照 CUBA Studio 用户手册打开已有项目 部分的描述打开 Library 项目。

  3. 在 CUBA Studio中打开 Project Properties 编辑器:点击 CUBAProject Properties 主菜单项。在 App components 列表中添加 fts 应用程序组件。当 Studio 建议重新创建 Gradle 脚本时,确认即可。Studio 将会下载需要的源代码和二进制 工件 。如果项目没有自动同步 Gradle 文件,可以点击 Gradle 工具窗口的 refresh_button 按钮手动同步。

  4. 在本地 HyperSQL 服务创建数据库:在主菜单点击 CUBACreate database

  5. 启动应用程序:点击主工具栏中 CUBA Application 配置旁边的 run_button 按钮。可以使用 CUBA 项目树中 Runs at…​ 部分的链接直接在 Studio 中通过浏览器打开应用程序。

  6. 打开 library 应用程序。

    用户名和密码为 admin / admin

  7. 要启用全文搜索功能,请在应用程序的主菜单中打开 AdministrationApplication properties,找到并打开表格中的 fts 属性列表。双击打开 fts.enabled 属性并在 Current value 字段中选择 true

    fts enabled true
    Figure 2. fts.enabled

完成上述步骤后,全文搜索功能将被添加到应用程序中并准备工作。如果退出系统然后再次登录,则主应用程序窗口的右上方面板中将显示一个搜索输入框。全文搜索也可以在 过滤器 UI 组件中使用。

但是,这时搜索将不会产生任何结果,因为数据尚未编入索引。

要启动对当前数据库的(即默认情况下在 FTS 配置文件中列出的实体)进行一次性索引,请打开 AdministrationJMX Console,找到 app-core.fts:type=FtsManager JMX bean 然后首先调用 reindexAll(),再调用 processQueue()

jmx fts setup
Figure 3. JMX 安装

在此之后,搜索 "adm" 字符串则会给出以下结果:

2.1 project setup
Figure 4. 搜索结果

2.2. 配置索引过程的调用

可以使用框架的 定时任务机制 按计划调用建索引过程。

首先,需要激活任务启动功能。将以下属性添加到项目 core 模块的 app.properties 文件中。

cuba.schedulingActive = true

重启应用服务,以 admin 的身份登录系统,打开 JMX Console 界面,找到并打开 app-core.cuba:type=Scheduling JMX bean 确保 Active 属性被设置为 true

然后打开 AdministrationScheduled Tasks 界面,单击 Create 并为新任务填写以下属性值:

  • Defined by: Bean

  • Bean name: cuba_FtsManager

  • Method name: processQueue()

  • Singleton: true

  • Period, sec: 30

保存任务,在表格中选择它,然后单击 Activate。从现在开始,系统将开始每隔 30 秒为已更改的实体建索引。

Warning

自动索引不包括在启动之前创建的实体。要将此类实体加入索引队列,请使用 app-core.fts:type=FtsManager JMX bean 的 reindexAll()asyncReindexAll() 方法。参阅运行和配置实体索引重建

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 配置文件,在示例中它可以为所有领域模型实体及其所有属性建立索引。

以下属性会自动添加到应用程序 core 模块的 app.properties 文件中:

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

这样,索引将包括平台的 com/haulmont/fts/fts.xml 文件和项目的 fts.xml 文件中定义的实体。

重启应用服务。从现在开始,全文搜索应该适用于应用程序模块的所有实体以及平台安全子系统的实体:RoleGroupUser

2.4. 上传文件内容搜索

现在需要为每本图书出版物提供文件上传的功能,并将上传的文件添加到 BookPublication 浏览界面。

自定义 BookPublication 实体。首先,添加一个新的 file 属性,它与 FileDescriptor 实体多对一关联。FileDescriptor 是上传的文件的描述(不要与 java.io.FileDescriptor 混淆),它允许从数据模型对象中引用文件。保存更改,然后在 Studio 中将新属性附加到现有的 bookPublication.full 界面以及 BookPublication 的浏览和编辑界面。

book publication new attribute
Figure 5. BookPublication 实体的新属性

生成新的 DB 脚本,更新数据库并重新启动应用程序服务。如果重新创建了 DB,则默认情况下将禁用全文搜索。在 JMX Console 复选框中再次选中 Value ,对所有文件重建索引,处理索引队列,注销并重新登录。

就所添加的新属性而言,BookPublication 浏览界面上的出版物的表格会增加一列:File。要填写这列,请打开任意行进行编辑,使用新上传控件上传文本文件,然后单击 OK。默认情况下,CUBA 支持对 RTFTXTDOCDOCXXLSXSLXODTODSPDF 格式的文件建立索引。

book publication file is not
Figure 6. BookPublication 编辑界面

新文件会出现在表格中。可以调整新列的外观。

book publication files uploaded
Figure 7. BookPublication 浏览

打开 JMX Console 界面,打开 app-core.fts:type=FtsManager JMX bean 并依次调用 reindexAll()processQueue() 方法根据新的搜索配置来对数据库和文件中的现有实例重建索引。对所有新增和改变的数据自动建索引会有延迟,这取决于定时任务的间隔时间,即不超过 30 秒。

现在,Full text search 将输出所有记录,包括外部文件内容。

book publication fts result
Figure 8. 搜索结果

可以在 开发人员手册 的相应章节中找到有关 FileStorageAPIFileDescriptor更多信息

2.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 - 属性名。它可以是引用属性路径(以点分隔)。不检查类型。但是,如果名称使用路径定义,则可以有两个选项:

          1. 最终属性必须是非嵌入式实体。包含非实体类型属性在这里没有意义,因为它必须在其拥有的实体内建索引。

          2. 最终属性必须是嵌入实体的非实体字段。例如,如果索引实体具有 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

下面描述的所有属性都是存储在数据库中的运行时参数,可通过 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

fts.searchResultsBatchSize

搜索结果中单批次的元素个数。用户需要在结果界面上单击 More 以查看下一批次结果。

默认值: 5

. . .