7.4. 将实体映射到索引结构
7.4.1. 映射实体 复制链接链接已复制到粘贴板!
索引实体所需的所有元数据信息都通过注释进行描述,因此无需 XML 映射文件。您仍然可以将 Hibernate 映射文件用于基本 Hibernate 配置,但是必须通过注释来表示 Hibernate Search 特定配置。
7.4.1.1. 基本映射 复制链接链接已复制到粘贴板!
让我们从最常用于映射实体的注释开始。
基于 Lucene 的查询 API 使用以下常见注解来映射实体:
- @Indexed
- @Field
- @NumericField
- @Id
7.4.1.2. @Indexed 复制链接链接已复制到粘贴板!
最重要的是,我们必须将持久类声明为可索引。这可以通过使用 @Indexed
为类添加注解(索引流程将忽略未注释 @Indexed 的
所有实体):
@Entity @Indexed public class Essay { ... }
@Entity
@Indexed
public class Essay {
...
}
您可以选择指定 @Indexed 注释的 index
属性,以更改索引的默认名称。
7.4.1.3. @Field 复制链接链接已复制到粘贴板!
对于实体的每个属性(或属性),您可以描述如何对其进行索引。默认(不存在)表示索引过程中会忽略该属性。
在 Hibernate Search 5 之前,只有通过 @NumericField
明确请求时,才会选择数字字段编码。从 Hibernate Search 5 开始,系统将自动为数字类型选择此编码。为避免数字编码,您可以通过 @Field.bridge 或
明确指定非数字字段网桥。软件包 @Field
Bridgeorg.hibernate.search.bridge.builtin
包含一组网桥,这些网桥编号编码为字符串,如 org.hibernate.search.bridge.builtin.IntegerBridge
。
@field 确实将属性声明
为索引,并允许通过设置以下一个或多个属性来配置索引过程的多个方面:
-
Name :
describe 在哪个名称下,该属性应存储在 Lucene 文档中。默认值为属性名称(遵循 JavaBeans 约定) -
存储
:描述属性是否存储在 Lucene 索引中。您可以存储值Store.YES
(在索引中消耗更多空间,但允许 投射,以压缩方式存储 Store.COMPRESS
(这确实消耗更多 CPU),或者避免任何存储Store.NO
(这是默认值)。存储属性时,您可以从 Lucene 文档检索其原始值。这与元素是否索引无关。 index
:描述属性是否索引。不同的值是 index.NO
,这表示它不会被索引,不能被查询和Index.YES
找到,即元素会被索引并可以搜索。默认值为Index.YES
。index. no
在不需要可搜索属性但应该可用于投射的情形中,将非常有用。注意index.NO
与 Analyze.YES
或Norms.YES
结合使用,因为分析和
强制要求
对该属性进行索引。分析
:确定属性是否被分析(Analyze.YES
)还是不分析(Analyze.NO
)。默认值为 Analyze.YES
。注意是否要分析属性取决于您是否希望按原样搜索元素,或按其包含的词语搜索。分析文本字段会有意义,但可能不是日期字段。
注意不得分析用于排序的字段。
-
强制
:描述索引时间提升信息应存储(Norms.YES
)还是不存储(Norms.NO
)。不存储存储会节省大量内存,但没有任何索引时间提高可用信息。默认值为Norms.YES
。 termVector
:描述术语频率对的集合。这个属性允许在索引过程中将术语向量存储在文档中。默认值为TermVector.NO
。此属性的不同值有:
Expand 值 定义 TermVector.YES
存储每个文档的术语向量。这会生成两个同步的数组,一个包含文档术语,另一个包含术语的频率。
TermVector.NO
不要存储术语向量。
TermVector.WITH_OFFSETS
存储术语向量和令牌偏移信息。这与 TermVector.YES 加上它包含术语的起始和结束偏移位置信息相同。
TermVector.WITH_POSITIONS
存储术语向量和令牌位置信息。这与 TermVector.YES 相同,还包含每一次在文档中出现某个术语的规范。
TermVector.WITH_POSITION_OFFSETS
存储术语向量、令牌位置和偏移信息。这是 YES、WITH_OFFSETS 和 WITH_POSITIONS 的组合。
indexNullAs
: Per default null 值将被忽略且未索引。不过,您可以使用indexNullAs
指定一个字符串,该字符串将被插入为null
值的令牌。默认情况下,此值设置为Field。DO_NOT_INDEX_NULL
表示不应索引null
值。您可以将这个值设置为Field.DEFAULT_NULL_TOKEN
,以指示应使用默认的空
令牌。可以使用hibernate.search.default_null_token
在配置中指定此默认空
令牌。如果未设置此属性,并且指定了Field.DEFAULT_NULL_TOKEN
,则字符串 "null" 将用作默认值。注意使用
indexNullAs
参数时,务必要在搜索查询中使用相同的令牌来搜索null
值。此外,建议仅在未分析的字段(分析.NO
)中使用此功能。警告在实施自定义 FieldBridge 或 TwoWayFieldBridge 时,开发人员可以处理 null 值的索引(请参阅 JavaDocs LuceneOptions.indexNullAs())。
7.4.1.4. @NumericField 复制链接链接已复制到粘贴板!
@Field 有一个相应的注释,名为 @NumericField,其范围与 @Field 或 @DocumentId 相同。它可用于 Integer、Long、Float 和 Double 属性。在索引时,将使用 Trie 结构对该值进行索引。当属性索引为数字字段时,它实现了有效的范围查询和排序,与对标准 @Field 属性执行相同的查询相比,它实现了高效的范围查询和排序。@NumericField 注释接受以下参数:
值 | 定义 |
---|---|
forField | (可选)指定将索引为数字的相关 @Field 的名称。只有 属性包含超过 @Field 声明时,才强制使用它 |
precisionStep | (可选)更改 Trie 结构存储在索引中的方式。较小的精度Steps会导致更多磁盘空间使用量、更快的范围和排序查询。较大的值会导致使用空间减少,范围查询性能更接近普通 @Fields 中的范围查询。默认值为 4。 |
@NumericField 仅支持 Double、Long、Integer 和 Float。其他数字类型无法利用 Lucene 中的类似功能,因此其余类型应使用默认或自定义 TwoWayFieldBridge 进行字符串编码。
假设您可以在类型转换过程中处理近似值,可以使用自定义 NumericFieldBridge:
示例:定义自定义数字FieldBridge
7.4.1.5. @Id 复制链接链接已复制到粘贴板!
最后,实体的 id
(识别符)属性是 Hibernate Search 使用的特殊属性,用于确保给定实体的索引唯一性。按照设计,必须存储 id
,且不能进行令牌化。要将属性标记为索引标识符,可使用 @DocumentId
注释。如果您使用的是 Jakarta Persistence 并且指定了 @Id,您可以省略 @DocumentId。选定的实体标识符也可用作文档标识符。
Infinispan Query 使用实体的 id
属性来确保索引唯一标识。按照设计,ID 会存储,且不得转换为令牌。要将属性标记为索引 ID,可使用 @DocumentId
注释。
示例:指定索引的属性
上例定义了四个字段的索引: id
、Abs tract
、文本
和 评级
。请注意,默认情况下字段名称没有大写,符合 JavaBean 规范。grade
字段标为数字,其精确步骤略大于默认值。
7.4.1.6. 映射属性多次 复制链接链接已复制到粘贴板!
有时,您需要为每个索引多次映射一个属性,索引策略略有不同。例如,根据字段排序查询需要取消分析字段。要按此属性上的词语搜索,仍需要对它进行索引 - 旦分析过,一旦未分析一次。@Fields 允许您实现此目标。
示例:使用 @Fields 映射多个属性时间
在本例中,字段 概述
被索引两次,一次以令牌化的方式作为 概述
,一次是以未令牌的方式作为 summary_forSort
索引。
7.4.1.7. 嵌入式和关联对象 复制链接链接已复制到粘贴板!
可以将关联的对象和嵌入式对象作为根实体索引的一部分进行索引。如果您希望根据相关对象的属性搜索给定实体,这很有用。目标是返回相关城市为 Atlanta 的位置(在 Lucene 查询解析器语言中,语言将转换为 address.city:Atlanta
)。位置字段将在位置索引中 索引
。Place
索引文档还将包含您可以 查询的 address.id
、address .
字段。
street
和 address.city
示例:索引关联
由于在使用 @IndexedEmbedded
技术时,Lucene 索引中数据已被非常规化,因此 Hibernate Search 必须了解 Place 对象中的任何变化,以及 Address 对象中的任何更改,以便索引保持最新。为确保在地址发生更改时 Lucene 文档得到更新,请使用 @ContainedIn
标记双向关系的另一侧。
@ContainedIn
对于指向实体的关联和嵌入式(收集)对象的关联都很有用。
若要对此展开,以下示例演示了嵌套 @IndexedEmbedded
:
示例:@IndexedEmbedded 和 @ContainedIn 的嵌套使用
任何 @*ToMany
、@*ToOne
和 @Embedded
属性都可以标上 @IndexedEmbedded
。然后,关联的类的属性将添加到主实体索引中。索引将包含以下字段:
- id
- name
- address.street
- address.city
- address.ownedBy_name
默认前缀是 propertyName.
,遵循传统的对象导航约定。您可以使用 prefix
属性覆盖它,如 ownedBy
属性中所示。
前缀不能设置为空字符串。
当对象图包含类(而非实例)的循环依赖项时,需要 深度
属性。例如,如果所有者指向位置:Hibernate 搜索将在到达预期深度(或到达对象图形边界)后停止包含索引的嵌入式属性。具有自引用的类是 cyclic 依赖项示例。在我们的示例中,由于 深度
设置为 1,Owner 中的任何 @IndexedEmbedded
属性都将被忽略。
使用 @IndexedEmbedded
作为对象关联可让您表达查询(使用 Lucene 的查询语法),例如:
返回名称包含 JBoss 以及地址城市为 Atlanta 的位置。在 Lucene 查询中,这是:
+name:jboss +address.city:atlanta
+name:jboss +address.city:atlanta
Copy to Clipboard Copied! Toggle word wrap Toggle overflow 返回名称包含 JBoss 的位置,以及所有者名称包含 Joe 的位置。在 Lucene 查询中,该选项为
+name:jboss +address.ownedBy_name:joe
+name:jboss +address.ownedBy_name:joe
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
此行为以更有效的方式(降低数据重复的成本)模仿关系连接操作。请记住,开箱即用时 Lucene 索引没有任何关联概念,连接操作也不存在。这有助于保持关系模式规范化,同时从完整的文本索引速度和特性丰富的中受益。
关联的对象本身(但不必)为 @Indexed
当 @IndexedEmbedded
指向某一实体时,其关联必须是方向的,并且必须给另一方添加注释 @ContainedIn
(如上例中所示)。如果没有,Hibernate Search 将无法在相关实体更新时更新根索引(在本示例中,相关地址实例更新时必须更新 Place
索引文档)。
有时,由 @IndexedEmbedded
标注的对象类型并非 Hibernate 和 Hibernate Search 所针对的对象类型。当接口用于代替其实施时,尤其会出现这种情况。因此,您可以使用 targetElement
参数覆盖 Hibernate Search 所针对的对象类型。
示例:使用 @IndexedEmbedded targetElement 属性
7.4.1.8. 将对象嵌入式限制为特定路径 复制链接链接已复制到粘贴板!
@IndexedEmbedded 注释也提供 includePaths 属性,可用于作为深度的替代方案或与其组合。
仅使用深度时,会在同一深度以递归方式添加嵌入式类型的所有索引字段。这样更加难以仅选择特定路径而不添加所有其他字段,而可能不需要这些字段。
为避免不必要的加载和索引实体,您可以精确指定所需的路径。典型的应用程序可能需要不同路径的不同深度,或者换句话说可能需要明确指定路径,如下例所示:
示例:使用 @IndexedEmbedded 的 includePaths 属性
使用上例中所示的映射,您可以按 名称和
/或 姓氏
搜索 Person,以及/或父 名称
。它不会索引父名称 的姓氏
,因此无法对父名进行搜索,但会加快索引、节省空间并提高整体性能。
@IndexedEmbeddedincludePaths 除常规地为深度指定有限值的索引外,还包括指定的路径。使用 includePaths 并保留深度未定义时,行为等同于设置 depth=0:只有包含的路径才会被索引。
示例:使用 @IndexedEmbedded 的 includePaths 属性
在上面的示例中,每个人都将拥有其名称和姓氏属性索引。父项的名称和姓氏也将进行索引,其中最多可递归为第二行,因为存在depth 属性。可以按姓名或姓氏直接搜索该人员、其父项或父项。除第二个级别外,我们还会再为一个级别编制索引,而仅索引名称,而非姓氏。
这会在索引中生成以下字段:
-
ID
:作为主密钥 -
_hibernate_class
:存储实体类型 -
名称
:作为直接字段 -
Sur
name:作为直接字段 -
parent.name
:作为嵌入式字段(深度 1) -
parent.surname
:作为嵌入式字段,在深度 1 中 -
parent.parents.name
:作为嵌入式字段,位于深度 2 -
parent.parents.surname
:作为嵌入式字段,显示深度 2 -
parent.parents.parents.name
:作为 includePaths 指定的额外路径。第一个父级.
从字段名称中推断,剩余的路径是 includePaths 的属性。
如果您要首先定义所需的查询(如此时您准确知道需要哪些字段)以及不需要哪些其他字段来实施您的用例,那么明确控制索引路径可能会更加简单。