本部分面向新的 Hibernate 搜索贡献者,为他们提供了关于 Hibernate Search 如何工作的介绍。
了解 Hibernate Search API 及其使用方法是理解本部分内容的要求。
概述
本部分重点阐述了 Hibernate Search 不同部分的高层次内容以及它们如何互相交互。
Hibernate Search 内部分为三个部分
- 后端
-
后端是“执行任务”的地方。它们为映射程序实现了通用的索引编制和搜索界面,通过“索引管理器”使用,它们各自提供对一个索引的访问。示例包括 Lucene 后端(委托给 Lucene 库)和 Elasticsearch 后端(委托给远程 Elasticsearch 集群)。
根据上下文,“后端”一词可能指的是整个 Maven 模块(例如“Elasticsearch 后端”),也可能指的是此模块中的单个中央类,(例如实现 `Backend` 界面的 `ElasticsearchBackend` 类)。 - 映射程序
-
映射程序是用户看到的。它们将用户模型“映射”到索引,并提供与用户模型一致的 API 来执行索引编制和搜索。例如,POJO 映射程序提供 API,允许根据引导时提供的配置对 Java 对象的 getter 和字段建立索引。
根据上下文,“映射程序”一词可能指的是整个 Maven 模块(例如“POJO 映射程序”),也可能指的是此模块中的单个中央类,(例如实现 `Mapper` 界面的 `PojoMapper` 类)。 - 引擎
-
引擎定义某些 API、大量 SPI,并实现启动和停止 Hibernate Search 所需的代码,以及在引导期间将映射程序和后端“粘合”在一起的代码。
这些部分严格分开,以便允许它们可以互换使用。例如,Elasticsearch 后端可以与 POJO 映射程序或 JSON 映射程序一起使用,并且我们只需实现一次后端。
以下是从高层次的角度来看 Hibernate Search 在运行时可能的样子
在这里,“映射”是一个非常粗略的术语。例如,一个 POJO 映射可能支持很多经过索引编制的实体。 |
映射在引导过程中借助几个“索引管理器”提供,每个都公开允许搜索和索引的 SPI。映射的目的是将调用其 API 转变为对索引管理器 SPI 的调用。这需要执行以下转换:
-
已索引数据:映射操纵的数据可以采用任意形式,但必须转换为索引管理器接受的文档。
-
索引引用(例如,以类
MyEntity
和MyOtherEntity
为目标的搜索查询,必须改为以索引管理器 1
和索引管理器 2
为目标)。 -
文档引用(例如,在索引管理器级别执行的搜索查询可能返回“索引 1 中的文档 1 与查询匹配”,但用户想要看到“类型为 MyEntity 的实体 1 与查询匹配”)。
SearchIntegration
的主要目的是跟踪引导时创建的每个资源(映射或后端)并允许通过单次调用关闭所有这些资源。
最后,后端及其索引管理器的目的是执行实际工作并在相关时返回结果。
此架构能够支持更复杂的用户配置。以下示例显示一个具有两个映射的 Hibernate Search 实例:一个 POJO 映射和一个 JSON 映射。
此示例故意有点做作,目的是为了演示一些细微差别。
-
此示例中有两个映射。大多数设置只会配置一个映射,但必须牢记可能有多个映射。特别是,我们预期 Infinispan 可能需要在单个 Hibernate Search 实例中使用多个不同的映射,以便处理从用户接受的多种输入类型。
-
此示例中有多个后端。同样,大多数设置只会配置一个后端,但可能有多个正当理由使用更多后端。例如,如果某人希望在一个 Elasticsearch 集群中索引一部分实体,在另一个集群中索引另一部分实体。
-
此处,两个映射都使用来自相同 Elasticsearch 后端的索引管理器。目前这是可行的,尽管是否存在此类有效用例仍有待确定,主要取决于 Infinispan 的需求。
引导
引导首先创建至少三个组件
-
SearchIntegration.Builder
,它允许配置 Hibernate Search 的引擎部分。 -
SearchIntegrationEnvironment
,该环境会传递给SearchIntegration.Builder
并允许配置环境:bean 解析器、后端的配置属性源、类解析… -
至少一个
MappingInitiator
实例(由映射器模块提供类型),它将自身注册到SearchIntegration.Builder
。从引擎的角度来看,这是一个稍后会发挥作用的回调。
理念是,SearchIntegration.Builder
将允许一个或多个发起者提供有关其映射的配置,尤其是各种“可映射”类型的元数据(简略来说,就是指用户处理的类型)。然后,构建器会组织此元数据,在某种程度上检查一致性,根据需要创建后端和索引管理器构建器,然后再将(组织的)元数据连同索引管理器构建器的句柄提供回映射器模块,以便它能够开始自身的引导。
总结一下:SearchIntegration.Builder
是一种促进程序,允许通过所有必要手段启动映射器引导
-
引擎服务和组件 (
BuildContext
); -
配置属性 (
ConfigurationPropertySource
); -
组织的元数据 (
TypeMetadataContributorProvider
); -
对每个索引类型的后端层的一个句柄 (
IndexManagerBuildingState
)。
所有这些都是通过 MappingInitiator
和 Mapper
接口提供给映射器的。
映射器引导完全取决于映射器模块,但有一件事不会改变,即映射器可以使用哪些后端层句柄。这些句柄是 IndexManagerBuildingState
的实例,每一个都代表着一个正在构建的索引管理器。当映射器检查元数据时,它将推断索引中必需的字段,并将此信息使用专门的 SPI 提交给后端:IndexModelBindingContext
、IndexNode
、IndexSchemaFieldContext
是最重要的部分。
有关必需字段及其选项(字段类型、是否存储、分析方式等)的所有这些信息都会经过验证,并允许后端构建索引架构的内部表示,该表示将用于各种特定于后端的用途,例如初始化远程 Elasticsearch 索引或推断给定字段上范围查询所需的参数类型。
索引
索引的入口点特定于每个映射器,每个映射器实现的较高级别也一样。但在较低级别,映射器中的索引归结为使用后端 SPI。
在索引时,映射器必须构建一个将传递给后端的文档。这是使用文档元素和索引字段引用完成的。在引导期间,无论映射器何时声明一个字段,后端都会返回一个引用(请参阅 IndexSchemaFieldFinalStep#toReference
)。为了构建一个文档,映射器会从要索引的对象中提取数据,从后端检索文档元素,然后连同值一起将字段引用传递给文档元素,以便将该值添加到字段中。
索引(或以任何方式更改索引)的另一部分是向索引管理器提供一条命令:“添加此文档”、“删除此文档”…。这是通过 IndexIndexingPlan
类完成的。映射器应在需要添加、更新或删除文档时创建索引计划。
IndexIndexingPlan
承载通常与 JPA 中的“会话”关联的一些上下文,在特定情况下包括多租户的情况下还包括租户标识符。因此,每当此上下文发生更改,映射器都应实例化新的索引计划。
冲洗、合并段等索引扩展操作无法从索引计划中获得。它们通过单独的类 IndexWorkspace 访问。 |
搜索
搜索与索引略有不同,因为向用户显示的 API 侧重于索引,而不是映射对象。这样做是考虑到了,当您搜索时,您主要针对的是索引字段,而不是映射对象的属性(尽管它们可能碰巧具有相同的名称)。
因此,映射器 API 仅定义搜索的入口点,以便提供更自然的方式来定义搜索范围并提供其他设置。例如,PojoSearchManager#search
允许使用映射类型的 Java 类来定义搜索范围,而不是索引名称。但在某些 API 调用中,映射器最终会公开通用 API,例如 SearchQueryResultDefinitionContext
或 SearchPredicateContainerContext
。
这些通用 API 基本上是在引擎中实现的。实现本身依赖于后端实现的低级别、不太“以用户为中心”的 SPI,例如 SearchPredicateFactory
或 FieldSortBuilder
。
此外,后端实现的 SPI 允许映射器注入“加载上下文”(请参阅 SearchQueryBuilderFactory.selectEntity
),该上下文实质上会将文档引用转换为最初编入索引的实体。
POJO 映射器基础
我们所说的 POJO 映射器基础(或简称 POJO 映射器)是一个抽象基础,用于将 Java 对象实现为全文索引的映射器。此模块实现了大部分必要的逻辑,并定义了 SPI 来实现特定于每个映射器的位。
目前只有两个实现:Hibernate ORM 映射器和 Stanadlone POJO 映射器。第二个映射器基本上用来演示实现不依赖于 Hibernate ORM 的映射器是可能的:我们不期望现实中会有大量的使用。
以下部分并未涉及 POJO 映射器中的所有内容,而是重点关注了更复杂的部分。
POJO 元模型的表示
POJO 映射器的引导过程严重依赖 POJO 元模型来推断在运行时需要做什么。有多种构造用于表示此元模型。
- 模型
-
PojoTypeModel
,PojoPropertyModel
等是所有内容的基础。它们是 SPI,例如,由 Hibernate ORM 映射器实现,并且它们提供有关已映射类型的基本信息:Java 注释、属性列表、每个属性的类型、“句柄”用于访问此类型实例上的每个属性,… - 容器值提取器路径
-
ContainerExtractorPath
和BoundContainerExtractorPath
都表示将应用于某个属性的ContainerExtractor
列表。例如,它们允许表示从类型为Map<String, List<MyEntity>>
的属性获取一系列MyEntity
的方法。两个版本之间的区别在于,“已绑定”版本已应用于 POJO 模型,保证了在应用于该模型时其可工作,并允许推断提取值的类型。有关详细信息,请参见ContainerExtractorBinder
。 - 路径
-
POJO 路径有两种类型:
PojoModelPath
和BoundPojoModelPath
。每种类型都有多个子类型,在路径中表示“节点”。POJO 路径通过访问属性、提取容器值(请参见上面的容器值提取器路径)和强制转换类型,表示如何从给定类型获取给定值。与容器值提取器路径一样,两个版本之间的区别在于,“已绑定”版本已应用于 POJO 模型,保证了在应用于该模型时其可工作(强制转换类型例外),并允许推断提取值的类型。 - 其他元数据
-
PojoTypeAdditionalMetadata
、PojoPropertyAdditionalMetadata
和PojoValueAdditionalMetadata
允许表示在没有注释的情况下通常不会在“普通 Java 对象”中找到的 POJO 元数据。元数据可能来自不同的来源:Hibernate Search 注释、Hibernate Search 的编程 API,甚至来自其他元模型,例如 Hibernate ORM 的元模型。“其他元数据”对象是一种表示此元数据的方式,无论其来自何处。其他元数据的示例包括给定类型是否为实体类型、属性标记(“此属性表示纬度”)或有关实体间关联的信息。 - 模型元素
-
PojoModelElement
、PojoModelProperty
等是 POJO 元模型的表示形式,可供 Hibernate Search 用户在桥接中使用。它们是 API,与PojoTypeModel
相反,后者是 SPI,但它们的实现依赖于 POJO 模型和其他元数据。它们的主要目的是保护用户免受我们的 SPI 中的最终更改的影响,并允许用户获取“访问器”,从而他们可以在运行时从桥接元素中提取信息。当获取访问器时,用户间接声明了 POJO 模型的哪些部分将提取并在 bridge 中使用,而 Hibernate Search 实际上会使用这些信息(请参阅隐式重新索引解析器)。
索引处理器
索引处理器是负责从 POJO 中提取数据并将其推送到文档的对象。
索引处理器按树组织,每个节点都是 PojoIndexingProcessor
的实现。POJO 映射器为每种索引实体类型分配一棵树。
以下是主要节点类型
-
PojoIndexingProcessorOriginalTypeNode
:表示 POJO 类型(Java 类)的节点。 -
PojoIndexingProcessorPropertyNode
:表示 POJO 属性的节点。 -
PojoIndexingProcessorContainerElementNode
:表示容器中的元素(List
、Optional
、…)的节点。
在运行时,将把根节点传递给要索引的实体和正在构建的文档的句柄。然后,每个节点都会“处理”其输入,即执行以下一个(或多个)操作
-
从作为输入传递的 Java 对象中提取数据:提取属性值、列表的元素、…。
-
将提取的数据和正在构建的文档的句柄传递给用户配置的桥接器,该桥接器会向文档添加字段。
-
将提取的数据和正在构建的文档的句柄传递给嵌套节点,该节点将依次“处理”其输入。
对于表示索引的嵌入字段的节点,需要进行更多工作来向文档添加一个对象字段,并确保嵌套节点向该对象字段而不是根文档添加字段。但这具体取决于索引的嵌入字段:一般来说,文档的处理仅由桥接器执行。 |
这种表示形式非常灵活,它几乎可以表示任何映射,只需定义适当的节点类型并确保正确构建索引处理器树即可,而且足够明确,因此不需要在运行时查找任何元数据。
在引导期间,索引处理器以 debug 级别记录。如果想要了解针对给定映射生成的索引处理器树,请启用 Hibernate Search 类的这一日志级别。 |
引导
对于每个索引类型,构建过程包括创建根 PojoIndexingProcessorOriginalTypeNode
生成器,并将元数据贡献者应用于此生成器(请参阅Bootstrap),并在需要时创建嵌套生成器(例如,当一个元数据贡献者提到 POJO 属性时)。每当找到 @IndexedEmbedded
时,只需递归地将该进程应用于在 @IndexedEmbedded
属性节点下创建的类型节点。
作为一个例子,我们考虑映射模型
类 IndexedEntityClass
已被索引。它有 2 个映射字段,以及一个名为 embedded
的类型为 EmbeddedEntityClass
的属性上的索引嵌入类 EmbeddedEntityClass
有一个映射字段,以及一个名为 secondLevelEmbedded
的类型为 SecondLevelEmbeddedEntityClass
的属性上的索引嵌入类 SecondLevelEmbeddedEntityClass
最后有一个映射字段,以及一个名为 thirdLevelEmbedded
的类型为 IndexedEntityClass
的属性上的索引嵌入。为了避免任何无限递归,索引嵌入的深度最大限制为 1,这意味着它将嵌入直接在 IndexedEntityClass
类型中映射的字段,但不会以传递方式包括任何索引嵌入。
使用以上描述的过程将此模型转换为此节点生成器树
虽然映射模型最初被组织为循环图,但索引处理器节点被组织为树,这意味着它无环。为了以直接的方式在运行时处理实体,而不需要依赖复杂逻辑、可变状态或元数据查找,这是必需的。
从潜在的循环图转换为树是由以下事实引起的,我们“展开”索引嵌入定义,通过为在不同嵌入级别出现的相同类型的多个索引处理器节点创建来破坏循环。
在我们的示例中,IndexedEntityClass
正好是这种情况下:根节点表示此类型,但是底部的类型节点还表示相同的类型,只在不同的嵌入级别上。
如果您想了解有关如何处理 @IndexedEmbedded 路径筛选、深度筛选、循环和前缀的更多信息,一个良好的起点是 IndexModelBindingContextImpl#addIndexedEmbeddedIfIncluded 。 |
最终,创建的索引处理树将遵循与生成器树大致相同的结构。由于优化,索引处理器树与生成器树可能有些不同。具体而言,如果我们检测到节点在运行时不会为文档添加任何内容,一些节点可能会被修剪,当使用具有路径筛选 (includePaths
) 或深度筛选 (includeDepth
) 的 @IndexedEmbedded
时,这可能发生在一些属性节点上。
在我们的示例中,“embedded”节点底部的情况就是这样。生成器节点在应用和解释元数据时创建,但事实证明该节点没有任何子级也没有任何桥梁。因此,在创建索引处理器时将忽略此节点。
隐式重新索引解析器
重新索引解析器是负责确定实体发生更改时的对象,其中其他实体在其索引形式中包括该已更改实体,因此应该重新索引该对象。
与索引处理器类似,PojoImplicitReindexingResolver
包含按树形组织的节点,每个节点都是 PojoImplicitReindexingResolverNode
的实现。POJO 映射器为每个索引或包含的实体类型分配一个包含一个树形结构的 PojoImplicitReindexingResolver
。已索引的实体类型是指映射到索引的类型(使用 @Indexed
或类似方法),而“包含的”实体类型是指 @IndexedEmbedded
的目标或在使用 PojoModelElement
API 时在桥接器中进行操作。
以下是主要节点类型
-
PojoImplicitReindexingResolverOriginalTypeNode
:表示 POJO 类型(Java 类)的节点。 -
PojoImplicitReindexingResolverCastedTypeNode
:表示要强制转换成父类型或子类型的 POJO 类型(Java 类)的节点,仅在强制转换成功时应用嵌套节点。 -
PojoImplicitReindexingResolverPropertyNode
:表示 POJO 属性的节点。 -
PojoImplicitReindexingResolverContainerElementNode
:表示容器中的元素(List
、Optional
、…)的节点。 -
PojoImplicitReindexingResolverDirtinessFilterNode
:表示过滤器的节点,仅在某些精确路径被认为已修改时授权其嵌套节点。 -
PojoImplicitReindexingResolverMarkingNode
:表示要标记为“重新索引”的值的节点。
在运行时,根节点将传递已更改的实体、该实体的“修改状态”(简而言之,该实体中更改的属性列表)和要重新索引的实体收集器。然后,每个节点将根据其输入“解析”要重新索引的实体,即执行下列一项(或多项操作)
-
检查“修改状态”是否包含使重新索引与此节点相关的特定已修改路径
-
从作为输入传递的 Java 对象提取数据:提取属性的值、列表中的元素、尝试将对象强制转换成给定类型,…
-
将提取的数据传递到收集器
-
提取的数据连同收集器一同传递到嵌套节点,后者将根据其输入“解析”要重新索引的实体。
与索引处理器一样,此表示非常灵活,但明确到足以在运行时不需要任何元数据查找。
重建期间会以 debug 级别记录重新索引解析器。要在给定映射中了解生成用于给定映射的重新索引解析器树形结构,请为 Hibernate Search 类启用此日志级别。 |
启动
在启动期间,为每个已索引的或包含的类型生成一个重新索引解析器树形结构。构建这些解析器的入口点可能并不明显:它是索引解析器构建过程。事实上,由于我们在给定已索引类型中构建索引处理器,我们将发现索引此类型时在实体关系图中将遍历的所有路径,从而发现已索引类型的索引过程明确地依赖关系。这些就是构建重新索引解析器所需的所有信息。
为了了解重新索引解析器的构建方式,务必记住重新索引解析器镜像索引处理器:如果实体 A
的索引处理器在某个时间点引用实体 B
,则可以确定实体 B
的重新索引解析器会在某个时间点引用实体 A
。
作为示例,我们来考虑上一节中的索引处理器构建器树 (索引处理器)
当我们构建索引处理器时,我们还将构造另一棵树,以表示从根类型(IndexedEntityClass
)到每个依赖的依赖项。这就是依赖项收集器发挥作用的地方。
依赖项收集器的组织方式与索引处理器构建器大致相同,呈树状结构。向根构建器提供根节点,然后为其每个子项创建一个节点,依此类推。在此过程中,每个构建器都能够通知其依赖项收集器它将实际构建一个索引处理器(它不会由于某些优化而被删减),这意味着需要在依赖项树中考虑此节点。这是通过 PojoIndexingDependencyCollectorValueNode#collectDependency
方法完成的,该方法会触发一些其他步骤。
TypeBridge 和 PropertyBridge 实现获准遍历关联并从不同实体访问属性。出于此原因,当此类桥接器出现在索引处理器中时,我们会根据需要创建依赖项收集器节点来建模桥接器的依赖项。有关更多信息,请参阅 PojoModelTypeRootElement#contributeDependencies (类型桥接)和 PojoModelPropertyRootElement#contributeDependencies (属性桥接)。 |
让我们看看我们的依赖项收集器树最终会是什么样子
以红色显示的值节点是我们将使用 PojoIndexingDependencyCollectorValueNode#collectDependency
标记为依赖项的节点。最底部的 embedded
属性将被检测为在索引期间未使用,因此相应的 value 节点将不会标记为依赖项,但所有其他 value 节点都会标记为依赖项。
实际的重新索引解析器构建发生在针对每个值节点调用 PojoIndexingDependencyCollectorValueNode#collectDependency
时。为了了解其工作原理,我们以 longField
的值节点为例。
当这个节点上调用 collectDependency
时,依赖项收集器将首先回溯到最后遇到的实体类型,因为那是 POJO 映射器将接收“更改事件”的类型。一旦找到此实体类型,依赖项收集器类型节点将从一个公用池中检索此类型的重新索引解析器构建器,该公用池在所有索引类型的依赖项收集器之间共享。
重新索引解析器构建器遵循与其构建的重新索引解析器相同的结构:它们是树中的节点,每种类型的重新索引解析器节点都有一个构建器类型:PojoImplicitReindexingResolverOriginalTypeNodeBuilder
、PojoImplicitReindexingResolverPropertyNodeBuilder
、…
回到我们的示例,当在 longField
的值节点上调用 collectDependency
时,我们回溯到遇到的最后一个实体类型,依存关系收集器类型节点检索到什么将会成为我们的“根”重新索引解析器节点的生成器
从那里,重新索引解析器生成器传递给下一个依存关系收集器值节点,使用 PojoIndexingDependencyCollectorValueNode#markForReindexing
方法。此方法还将所依存属性的路径作为参数,在本例中为 longField
。
然后值节点将使用其对依存关系树的了解(使用其在依存关系收集器树中的祖先)从上一实体类型构建一个 BoundPojoModelPath
到该值。在我们的例子中,此路径为 Type EmbeddedEntityClass ⇒ Property "secondLevelEmbedded" ⇒ No container value extractor
。
此路径表示两个实体类型之间的关联:包含方上的 EmbeddedEntityClass
和被包含方上的 SecondLevelEmbeddedEntityClass
。为了完成重新索引解析器树,我们需要 **反转** 此关联,即找出从 SecondLevelEmbeddedEntityClass
到 EmbeddedEntityClass
的反路径。此操作在 PojoAssociationPathInverter
中使用 POJO 元模型的表示 中提到的“附加元数据”完成。
反转路径成功后,依存关系收集器值节点可以向重新索引解析器生成器添加新的子项
然后将产生的重新索引解析器生成器传递到下一个依存关系收集器值节点,并且此过程会重复
一旦我们到达依存关系收集器根,我们几乎完成了。重新索引解析器生成器树已经填充了每当 SecondLevelEmbeddedEntityClass
的 longField
属性发生更改时重新索引 IndexedEntityClass
所需的每个节点。
现在唯一要做的是注册所依存的路径(在我们的示例中为 longField
)。通过注册此路径,我们将能够建立一个 PojoPathFilter
,以便每当 SecondLevelEmbeddedEntityClass
发生更改时,我们将遍历该树,但不是全部树:如果在某个点我们注意到一个节点仅在 longField
发生更改时才相关,但是“变脏状态”告诉我们 longField
并未更改,那么我们可以跳过树的一个分支,从而避免无用的延迟加载和重新索引。
上面的示例故意简单,是为了给出一个关于如何构建重新索引解析器的总体概念。在实际算法中,我们必须处理多种情况,使得整个过程变得更加复杂
- 多态性
-
由于多态性,关联在运行时的目标可能不是模型中声明的确切类型。同样由于多态性,关联可能定义在抽象实体类型上,但具有不同的反向侧,甚至不同的目标类型,具体取决于具体实体子类型。
有各种各样的错综复杂的情况需要考虑,但它们主要是通过以下方式解决的
-
每当我们在 Reindexing Resolver 构建树中创建一个类型节点时,我们会小心确定考虑类型的所有可能的具体实体类型,并为每个可能的实体类型创建一个 Reindexing Resolver 类型节点构建器。
-
每当我们解析关联的反向一侧时,请小心针对每个具体的“源”实体类型解析它,并应用所有产生的反向路径。
如果你想观察算法如何实时处理此问题,请尝试调试
AutomaticIndexingPolymorphicOriginalSideAssociationIT
或AutomaticIndexingPolymorphicInverseSideAssociationIT
,并设置断点在依赖收集器的collectDependency
/markForReindexing
方法中。
-
- 嵌入式类型
-
依赖收集器树中的类型不总是实体类型。因此,关联路径(要反转的和反向路径)可能比一个属性加上一个容器值提取器复杂。
如果你想观察算法如何实时处理此问题,请尝试调试
AutomaticIndexingEmbeddableIT
,并设置断点在依赖收集器的collectDependency
/markForReindexing
方法中。 - 细粒度脏检查
-
细粒度脏检查包含跟踪给定实体中哪些属性是脏的,以便仅重新索引实际使用至少一个脏属性的“包含”实体。如果没有此检查,Hibernate Search 可能时不时触发不必要的重新索引,具体取决于用户模型,这会对性能产生非常不利的影响。
为了实现细粒度脏检查,每个 Reindexing Resolver 节点构建器不仅存储了在根实体更改时应该重新索引相应节点的信息,还跟踪了根实体的哪些属性应该触发此特定节点的重新索引。每个构建器在一个
PojoImplicitReindexingResolverMarkingNodeBuilder
实例中保留此状态,并将其委托给它。如果你想观察算法如何实时处理此问题,请尝试调试
AutomaticIndexingBasicIT.directValueUpdate_nonIndexedField
,并在依赖收集器的collectDependency
/markForReindexing
方法(查看引导时发生的情况)和PojoImplicitReindexingResolverDirtinessFilterNode
的resolveEntitiesToReindex
方法(查看运行时发生的情况)设置断点。
提供客户端实例
如果你已经在应用程序中使用了底层 Elasticsearch 客户端(一个 org.elasticsearch.client.RestClient
实例),你可以告诉 Hibernate Search 使用该客户端,而不是创建自己的客户端。
提供 RestClient
需要两个步骤
-
在受支持的依赖项注入框架中,将
org.elasticsearch.client.RestClient
实例定义为 bean,例如通过定义附注有@Named("myRestClient")
的 CDI bean 生产者。 -
通过将配置属性
hibernate.search.backend.client.instance
设置为指向 bean 的 bean 引用,例如bean:myRestClient
,将 Hibernate Search 配置为使用该实例。
如果 Hibernate Search 使用已提供的客户端,它就不会创建自己的客户端,因此所有其他客户端配置属性都会被忽略。 |
Hibernate Search 引擎
-
hibernate.search.bean_configurers
-
用于通过编程方式将名称分配给 Bean 的
BeanConfigurer
实例。需要对类型为
BeanConfigurer
的 bean 的多值引用。默认无值。
-
hibernate.search.thread_provider
-
用于创建线程的
ThreadProvider
。需要对类型为
ThreadProvider
的 bean 的引用。默认为
EngineSpiSettings.Defaults.THREAD_PROVIDER
,即嵌入式线程提供程序。
Hibernate Search 后端 - Lucene
-
hibernate.search.backend.backend_work_executor_provider
-
用于创建工作执行器的
LuceneWorkExecutorProvider
。需要对类型为
LuceneWorkExecutorProvider
的 bean 的引用。
Hibernate Search 后端 - Elasticsearch
-
hibernate.search.client.instance
-
Hibernate Search 在所有 Elasticsearch 请求中应使用的外部 Elasticsearch 客户机实例。
如果设置此值,Hibernate Search 将不会尝试创建自己的 Elasticsearch,且所有其他与客户端相关的配置属性(主机/URI、身份验证、发现、超时、最大连接数、配置器等)将被忽略。
需要
RestClient
类型 Bean 的引用。默认为无:如果没有提供客户端实例,Hibernate Search 将创建自己的客户端实例。
警告 - 孵化版 API:底层客户端类可能会在不事先通知的情况下发生变更。
-
hibernate.search.client_factory
-
已弃用。
-
hibernate.search.backend.backend_work_executor_provider
-
ElasticsearchWorkExecutorProvider
用于创建工作执行器。需要
ElasticsearchWorkExecutorProvider
类型 Bean 的引用。
Hibernate Search ORM 集成
-
hibernate.search.integration_partial_build_state
-
hibernate.search.jboss.log-version
-
== Hibernate Search ORM 集成 - 协调 - 轮询出站
-
hibernate.search.coordination.agent.entity.mapping
-
允许用户为代理表定义特定的 Hibernate 映射。
仅当 "hibernate.search.coordination.strategy" 为 "outbox-polling" 时才可用。
需要一个包含 XML 值的字符串,该 XML 表示该实体的 Hibernate 映射。
此值的默认值为
OutboxPollingAgentAdditionalJaxbMappingProducer.ENTITY_DEFINITION
由于此配置完全覆盖实体映射,因此不能将其与定义代理表目录/模式/表/标识生成器的任何属性联合使用(
HibernateOrmMapperOutboxPollingSettings.COORDINATION_ENTITY_MAPPING_AGENT_CATALOG
、HibernateOrmMapperOutboxPollingSettings.COORDINATION_ENTITY_MAPPING_AGENT_SCHEMA
、HibernateOrmMapperOutboxPollingSettings.COORDINATION_ENTITY_MAPPING_AGENT_TABLE
、HibernateOrmMapperOutboxPollingSettings.COORDINATION_ENTITY_MAPPING_AGENT_UUID_GEN_STRATEGY
、HibernateOrmMapperOutboxPollingSettings.COORDINATION_ENTITY_MAPPING_AGENT_UUID_TYPE
)。如果出现此类错误配置,将引发异常(SearchException
)。 -
hibernate.search.coordination.outboxevent.entity.mapping
-
允许用户为 Outbox 事件表定义特定的 Hibernate 映射。
仅当 "hibernate.search.coordination.strategy" 为 "outbox-polling" 时才可用。
需要一个包含 XML 值的字符串,该 XML 表示该实体的 Hibernate 映射。
此值的默认值为
OutboxPollingOutboxEventAdditionalJaxbMappingProducer.ENTITY_DEFINITION
由于此配置完全覆盖实体映射,因此不能将其与定义 Outbox 事件表的目录/模式/表/标识生成器的任何属性联合使用(
HibernateOrmMapperOutboxPollingSettings.COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_CATALOG
、HibernateOrmMapperOutboxPollingSettings.COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_SCHEMA
、HibernateOrmMapperOutboxPollingSettings.COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_TABLE
、HibernateOrmMapperOutboxPollingSettings.COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_UUID_GEN_STRATEGY
、HibernateOrmMapperOutboxPollingSettings.COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_UUID_TYPE
)。如果出现此类错误配置,将引发异常(SearchException
)。