以下是详细说明的功能仍在孵化中:仍在处于积极开发阶段。 不适用于通常的 兼容性策略:正在孵化元素(例如类型、方法、配置属性等)的约定在后续版本中可能会以向后不兼容的方式更改,甚至移除。 我们强烈建议您使用孵化中的功能,以便开发团队可以获取反馈并对其进行改进,但您应做好准备根据需要更新依赖于这些功能的代码。 |
本指南将引导您了解使用 Hibernate Search 索引和查询您的实体所需的初始步骤,前提是您的实体未在 Hibernate ORM 中定义。
如果您的实体确实在 Hibernate ORM 中定义,请参阅 本指南。
1. 假设
本入门指南旨在具有通用性,且不会绑定到任何特定框架。如果您使用 Quarkus、WildFly 或 Spring Boot,请确保先查看 框架支持 以获取提示并了解怪癖。
为简单起见,本指南假定您正在创建一个部署为单个节点的单个实例的应用程序。对于更高级的设置,我们强烈建议您查看 架构示例。
2. 依赖项
可以在 Maven 的 中央存储库 中找到 Hibernate Search 工件。如果您从 Maven 获取 Hibernate Search,建议将 Hibernate Search BOM 导入为依赖管理的一部分,以确保其所有工件版本保持一致
<dependencyManagement>
<dependencies>
<!-- Import Hibernate Search BOM to get all of its artifact versions aligned: -->
<dependency>
<groupId>org.hibernate.search</groupId>
<artifactId>hibernate-search-bom</artifactId>
<version>7.2.0.Final</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Any other dependency management entries -->
</dependencies>
</dependencyManagement>
如果您不想或无法从 Maven 存储库下载 JAR,则可以从 Sourceforge 托管的发行版包 中获取这些文件。
为了使用 Hibernate Search,您至少需要两个直接依赖项
以下是快速入门最常见的设置和匹配依赖项;请阅读 体系结构 以获取更多信息。
- 独立式 POJO 映射器 + Lucene
-
允许在一个应用程序节点中对未在 Hibernate ORM 中定义的实体进行索引化,并将索引存储在本地文件系统中。
如果通过 Maven 获取 Hibernate Search,请使用以下依赖项
<dependencies> <!-- Add dependencies without specifying versions as that is already taken care by dependency management: --> <dependency> <groupId>org.hibernate.search</groupId> <artifactId>hibernate-search-mapper-pojo-standalone</artifactId> </dependency> <dependency> <groupId>org.hibernate.search</groupId> <artifactId>hibernate-search-backend-lucene</artifactId> </dependency> <!-- Any other dependency entries --> </dependencies>
如果通过发布包获取 Hibernate Search,请从
dist/engine
、dist/mapper/pojo-standalone
、dist/backend/lucene
及其各自的lib
子目录复制 JAR。 - 独立式 POJO 映射器 + Elasticsearch(或 OpenSearch
-
允许在多个应用程序节点上对未在 Hibernate ORM 中定义的实体进行索引化,并将索引存储在远程 Elasticsearch 或 OpenSearch 集群中(需要单独配置)。
如果通过 Maven 获取 Hibernate Search,请使用以下依赖项
<dependencies> <!-- Add dependencies without specifying versions as that is already taken care by dependency management: --> <dependency> <groupId>org.hibernate.search</groupId> <artifactId>hibernate-search-mapper-pojo-standalone</artifactId> </dependency> <dependency> <groupId>org.hibernate.search</groupId> <artifactId>hibernate-search-backend-elasticsearch</artifactId> </dependency> <!-- Any other dependency entries --> </dependencies>
如果通过发布包获取 Hibernate Search,请从
dist/engine
、dist/mapper/pojo-standalone
、dist/backend/elasticsearch
及其各自的lib
子目录复制 JAR。
3. 配置
将所需的所有依赖项添加到应用程序中后,可以查看配置了。
带有 独立式 POJO 映射器 的 Hibernate Search 配置属性在构建映射时通过编程进行设置。
CloseableSearchMapping searchMapping = SearchMapping.builder( AnnotatedTypeSource.fromClasses( (1)
Book.class, Author.class
) )
.property( "hibernate.search.backend.directory.root",
"some/filesystem/path" ) (2)
.build(); (3)
1 | 创建一个构建器,传入一个 AnnotatedTypeSource ,让 Hibernate Search 知道在哪里查找注释。 |
2 | 设置文件系统中索引的位置。默认情况下,后端会将索引存储在当前工作目录中。 |
3 | 构建 SearchMapping 。 |
CloseableSearchMapping searchMapping = SearchMapping.builder( AnnotatedTypeSource.fromClasses( (1)
Book.class, Author.class
) )
.property( "hibernate.search.backend.hosts",
"elasticsearch.mycompany.com" ) (2)
.property( "hibernate.search.backend.protocol",
"https" ) (3)
.property( "hibernate.search.backend.username",
"ironman" ) (4)
.property( "hibernate.search.backend.password",
"j@rV1s" )
.build(); (5)
1 | 创建一个构建器,传入一个 AnnotatedTypeSource ,让 Hibernate Search 知道在哪里查找注释。 |
2 | 设置要连接的 Elasticsearch 主机。默认情况下,后端会尝试连接到 localhost:9200 。 |
3 | 设置协议。默认的是 http ,但可能需要使用 https 。 |
4 | 设置用于基本 HTTP 身份验证的用户名和密码。你还可能对 AWS IAM 身份验证 感兴趣。 |
5 | 构建 SearchMapping 。 |
由于有 类路径扫描,你的 另请参阅 此部分 以对类路径扫描进行故障排除或提高其性能。 |
有几个 实用方法 可以用来简化属性键的动态构建。 |
4. 映射
假设你的应用程序包含类 Book
和 Author
,并且你希望对它们进行索引化以便搜索书籍。
import java.util.HashSet;
import java.util.Set;
public class Book {
private Integer id;
private String title;
private String isbn;
private int pageCount;
private Set<Author> authors = new HashSet<>();
public Book() {
}
// Getters and setters
// ...
}
import java.util.HashSet;
import java.util.Set;
public class Author {
private Integer id;
private String name;
private Set<Book> books = new HashSet<>();
public Author() {
}
// Getters and setters
// ...
}
要在这些类型中进行搜索,需要将它们标记为 实体类型,并将它们映射到索引结构。
以下是如何映射以上模型的示例。
import java.util.HashSet;
import java.util.Set;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.DocumentId;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexedEmbedded;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.KeywordField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.SearchEntity;
@SearchEntity (1)
@Indexed (2)
public class Book {
@DocumentId (3)
private Integer id;
@FullTextField (4)
private String title;
@KeywordField (5)
private String isbn;
@GenericField (6)
private int pageCount;
@IndexedEmbedded (7)
private Set<Author> authors = new HashSet<>();
public Book() {
}
// Getters and setters
// ...
}
import java.util.HashSet;
import java.util.Set;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.AssociationInverseSide;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.DocumentId;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ObjectPath;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.PropertyValue;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.SearchEntity;
@SearchEntity (8)
(9)
public class Author {
@DocumentId (10)
private Integer id;
@FullTextField (5)
private String name;
@AssociationInverseSide(inversePath = @ObjectPath(@PropertyValue(propertyName = "authors"))) (11)
private Set<Book> books = new HashSet<>();
public Author() {
}
// Getters and setters
// ...
}
1 | @SearchEntity 将 Book 类型标记为 实体类型 ,这是将它映射到索引的必要条件。 |
2 | @Indexed 将 Book 类型标记为 已索引,即为该实体创建索引,并将该索引保持最新状态。请注意,此处可以添加 @SearchEntity ,但没有必要:使用 独立 POJO 映射器 时,@Indexed 类型始终隐式地是实体。 |
3 | @DocumentId 标记用于生成文档标识符的属性。 |
4 | @FullTextField 将属性映射到同名同类型的全文索引字段。全文字段分解为标记并进行归一化(小写,…)。在此,我们依赖默认分析配置,但大多数应用程序需要对其进行自定义;将在 下方进一步讨论此问题。 |
5 | @KeywordField 将属性映射到未分析的索引字段。例如,此属性对标识符有用。 |
6 | Hibernate Search 不仅用于全文搜索:可以使用 @GenericField 批注对非 String 类型进行索引。开箱即用地支持 广泛的属性类型,例如基本类型(int 、double ,…)及其包装类型(Integer 、Double ,…)、枚举、日期/时间类型、BigInteger /BigDecimal 等。 |
7 | @IndexedEmbedded 将关联对象的(实体或可嵌入对象)的已索引表单“嵌入”到嵌入实体的已索引表单中。在此处, |
8 | @SearchEntity 将 Author 标记为实体类型。 |
9 | Author 在其他实体中为 @IndexedEmbedded ,但本身不需要有可搜索性,因此不需要索引且不需要使用 @Indexed 进行注解。 |
10 | Author 不做索引,但它是 @IndexedEmbedded ,因此 Hibernate Search 需要处理 Author 实例的标识符。将 @DocumentId 放置在唯一标识属性上可以实现此操作。 |
11 | Boo k 嵌入了来自 Author 实体的数据,因此当 Author 发生变化时,Book 需要重新编制索引,进而 Hibernate Search 需要在作者发生变化时检索特定作者的书籍。出于此原因,我们需要向 Hibernate Search 提供有关关联关系反向端的信息。 |
这是一个非常简单的示例,但足够作为入门。只要记住 Hibernate Search 允许更加复杂的映射就行
-
存在多个
@*Field
注解,其中某些允许全文搜索,而其他则允许针对特定类型的字段进行更细粒度的配置。您可以在使用@GenericField
、@FullTextField
等将属性映射到索引字段中详细了解@*Field
注解。 -
可以通过“桥接器”使用更细粒度的控制来映射属性,甚至映射类型。这允许映射开箱即用不受支持的类型。请参阅绑定和桥接器了解更多信息。
5. 初始化
在首次启动应用程序之前,可能需要进行一些初始化
-
需要创建索引及其模式。
-
需要对另一个数据存储(如果存在)中已经存在的数据编制索引。
5.1. 模式管理
在索引之前,需要创建索引及其模式,可以在磁盘上(Lucene)或通过 REST API 调用(Elasticsearch)创建。
幸运的是,默认情况下,Hibernate Search 将负责在首次启动时创建索引:您无需执行任何操作。
下次启动应用程序时,将重新使用现有索引。
在应用程序重启两次之间,如果对映射进行任何更改(例如,添加新字段、更改现有字段的类型等),都将需要更新索引模式。 虽然这需要进行一些特殊的处理,但是可以通过删除并重新创建索引轻松解决。请参阅更改现有应用程序的映射了解更多信息。 |
5.2. 初始索引编制
正如我们之后将看到的,每当实体在应用程序中发生变化时,Hibernate Search 会自动为其编制索引。
然而,如果您使用 Hibernate Search 索引其他数据存储中的数据,则该数据存储在您添加 Hibernate Search 集成时可能已包含数据。Hibernate Search 无法得知该数据,因此必须通过批处理进行索引。
这得益于大规模索引器 API,但比与 Hibernate ORM 映射器 结合使用时需要更复杂一些,因为需要插入一种从其他数据存储加载实体的方式。
由于这种(相对的)复杂性,本指南不会涉及大规模索引,但如果您想了解更多,可以参阅 大规模索引。
6. 索引
索引是通过可从 SearchSession
访问的 SearchIndexingPlan
明确执行的。
默认情况下,尤其是在使用 Elasticsearch 后端时,会话关闭后不会立即看到更改。Elasticsearch 需要稍等一会儿(默认一 秒)来处理更改。 出于此原因,如果您在会话中修改了实体,然后在会话关闭后立即执行搜索查询,则搜索结果可能与您刚刚执行的更改不一致。 有关此行为及其调整方式的更多信息,请参阅 与索引同步。 |
在首次添加实体并且您绝对确定它还不存在于索引中的情况下,请使用 add
try ( SearchSession session = searchMapping.createSession() ) { (1)
Author author = new Author(); (2)
author.setId( 1 );
author.setName( "John Doe" );
Book book = new Book();
book.setId( 2 );
book.setTitle( "Refactoring: Improving the Design of Existing Code" );
book.setIsbn( "978-0-58-600835-5" );
book.setPageCount( 200 );
book.getAuthors().add( author );
author.getBooks().add( book );
session.indexingPlan().add( author ); (3)
session.indexingPlan().add( book );
}
在实体可能已存在于索引中的情况下,请使用 addOrUpdate
添加/更新实体
try ( SearchSession session = searchMapping.createSession() ) {
book.setTitle( "On Cleanup of Existing Code" );
session.indexingPlan().addOrUpdate( book );
}
添加使用 delete
从索引中删除实体
try ( SearchSession session = searchMapping.createSession() ) {
session.indexingPlan().delete( book );
}
7. 搜索
一旦索引了数据,您就可以执行搜索查询。
以下代码将准备一个针对 Book
实体的索引的搜索查询,过滤结果,以便 title
和 authors.name
中至少有一个字段包含字符串 refactoring
。匹配隐式地针对单词(“标记”)而非整个字符串,并且不区分大小写:这是因为目标字段具有应用了 默认分析器 的全文字段。
try ( SearchSession session = searchMapping.createSession() ) { (1)
SearchResult<Integer> result = session.search( Book.class ) (2)
.select( f -> f.id( Integer.class ) ) (3)
.where( f -> f.match() (4)
.fields( "title", "authors.name" )
.matching( "refactoring" ) )
.fetch( 20 ); (5)
long totalHitCount = result.total().hitCount(); (6)
List<Integer> hits = result.hits(); (7)
List<Integer> hits2 =
/* ... same DSL calls as above... */
.fetchHits( 20 ); (8)
}
1 | 创建一个称为 SearchSession 的 Hibernate Search 会话。 |
2 | 对映射到 Book 实体的索引发起搜索查询。 |
3 | 定义我们仅检索实体标识符。直接检索实体是可能的,但这需要 更多配置,因此本指南不会对其进行讨论。 |
4 | 定义仅返回与给定谓词匹配的文档。使用作为 lambda 表达式参数传递给工厂 f 来创建谓词。 |
5 | 构建查询并提取结果,限制前 20 个命中。 |
6 | 检索匹配实体总数。 |
7 | 检索匹配实体的标识符。 |
8 | 如果您对整个结果不感兴趣,而仅对命中感兴趣,您也可以直接调用 fetchHits() 。 |
如果您出于某种原因不想使用 lambda,您可以使用基于对象的备用语法,但它将更详细。
try ( SearchSession session = searchMapping.createSession() ) { (1)
SearchScope<Book> scope = session.scope( Book.class ); (2)
SearchResult<Integer> result = session.search( scope ) (3)
.select( f -> f.id( Integer.class ) ) (4)
.where( scope.predicate().match() (5)
.fields( "title", "authors.name" )
.matching( "refactoring" )
.toPredicate() )
.fetch( 20 ); (6)
long totalHitCount = result.total().hitCount(); (7)
List<Integer> hits = result.hits(); (8)
List<Integer> hits2 =
/* ... same DSL calls as above... */
.fetchHits( 20 ); (9)
}
1 | 创建一个称为 SearchSession 的 Hibernate Search 会话。 |
2 | 创建表示将被查询的已编制索引类型的“搜索范围”。 |
3 | 发起针对搜索范围的搜索查询。 |
4 | 定义我们仅检索实体标识符。直接检索实体是可能的,但这需要 更多配置,因此本指南不会对其进行讨论。 |
5 | 定义仅返回与给定谓词匹配的文档。使用与查询相同的搜索范围创建谓词。 |
6 | 构建查询并提取结果,限制前 20 个命中。 |
7 | 检索匹配实体总数。 |
8 | 检索匹配实体的标识符。 |
9 | 如果您对整个结果不感兴趣,而仅对命中感兴趣,您也可以直接调用 fetchHits() 。 |
可以使用 fetchTotalHitCount()
仅获取总命中数。
try ( SearchSession session = searchMapping.createSession() ) {
long totalHitCount = session.search( Book.class )
.where( f -> f.match()
.fields( "title", "authors.name" )
.matching( "refactoring" ) )
.fetchTotalHitCount(); (1)
}
1 | 获取总命中数。 |
虽然以上示例将命中作为实体标识符检索,但这只是可能命中类型之一。请参阅 投影 DSL 了解更多信息。 |
8. 分析
全文搜索允许对不区分大小写的单词快速匹配,这比关系数据库中的子字符串搜索更进一步。但可能会更好:如果我们希望使用术语“重构”进行搜索以匹配标题包含“重构”的书籍,该怎么办?这可以通过自定义分析实现。
分析定义了如何处理索引文本和搜索词。这样包括依次应用的三个类型的组件:分析器。
-
零个或(极少)更多字符过滤器,以清理输入文本:
A <strong>GREAT</strong> résume
⇒A GREAT résume
。 -
一个分词器, 将输入文本分割为单词,称为“标记”:
A GREAT résume
⇒[A, GREAT, résume]
。 -
零个或更多标记过滤器,对标记进行规范化并去除无意义的标记。
[A, GREAT, résume]
⇒[great, resume]
。
有内置分析器,尤其是默认分析器,它将
-
根据 Unicode 文本分割算法 的词切分规则对输入进行标记(分割);
-
通过将大写字母转换为小写字母来过滤(规范化)标记。
默认分析器适合大多数语言,但不够高级。要充分利用分析功能,您需要根据特定需求选择最适合的分词器和过滤器,来定义一个自定义分析器。
以下段落将介绍如何配置和使用一个简单但实用的分析器。有关分析及其配置方式的更多信息,请参阅 分析 部分。
每个自定义分析器都需要在 Hibernate Search 中指定一个名称。这是通过分析配置器完成的,该配置器是针对每个后端定义的。
-
首先,您需要实现一个分析配置器,该配置器是一个 Java 类,它实现了一个特定于后端的接口:
LuceneAnalysisConfigurer
或ElasticsearchAnalysisConfigurer
。 -
其次,您需要更改后端的配置,以实际使用您的分析配置器。
例如,假设您编入索引的 Book
实体之一的标题为“重构:改进现有代码的设计”,而您希望对以下任何搜索词获取命中项:“重构”、“重构”、“重构”和“重构”。实现这一目标的一种方法是使用带有以下组件的分析器:
-
“标准”分词器,它在空格、标点符号和连字符处分割单词。它是一个通用的分词器。
-
“小写”过滤器,它将每个字符都转换为小写。
-
“词干”过滤器,它应用特定于语言的 词干提取。
-
最后,一个“ASCII 折叠”过滤器,它用它们的 ASCII 等效内容(“e”、“a”...)替换带有变音符号的字符(“é”、“à”...)。
以下示例根据您选择的后台,演示如何定义带有这些组件的分析器。
package org.hibernate.search.documentation.mapper.pojo.standalone.gettingstarted.withhsearch.customanalysis;
import org.hibernate.search.backend.lucene.analysis.LuceneAnalysisConfigurationContext;
import org.hibernate.search.backend.lucene.analysis.LuceneAnalysisConfigurer;
public class MyLuceneAnalysisConfigurer implements LuceneAnalysisConfigurer {
@Override
public void configure(LuceneAnalysisConfigurationContext context) {
context.analyzer( "english" ).custom() (1)
.tokenizer( "standard" ) (2)
.tokenFilter( "lowercase" ) (3)
.tokenFilter( "snowballPorter" ) (3)
.param( "language", "English" ) (4)
.tokenFilter( "asciiFolding" );
context.analyzer( "name" ).custom() (5)
.tokenizer( "standard" )
.tokenFilter( "lowercase" )
.tokenFilter( "asciiFolding" );
}
}
CloseableSearchMapping searchMapping = SearchMapping.builder( AnnotatedTypeSource.fromClasses(
Book.class, Author.class
) )
.property( "hibernate.search.backend.directory.root",
"some/filesystem/path" )
.property( "hibernate.search.backend.analysis.configurer",
BeanReference.of( MyLuceneAnalysisConfigurer.class, BeanRetrieval.CLASS ) ) (6)
.build();
1 | 定义一个名为“english”的自定义分析器,分析英语文本(例如书名)。 |
2 | 将分词器设置为标准分词器。您需要传递特定的 Lucene 名称来引用分词器;有关可用分词器、它们的名称和参数的信息,请参阅 自定义分析器和归一化器。 |
3 | 设置标记过滤器。标记过滤器以给定的顺序应用。在这里同样需要使用特定于 Lucene 的名称;有关可用标记过滤器、它们的名称和参数的信息,请参阅 自定义分析器和归一化器。 |
4 | 为最后添加的字符过滤器/分词器/标记过滤器设置参数的值。 |
5 | 定义另一个名为“name”的自定义分析器,分析作者姓名。与第一个不同,不要启用词干提取(没有 snowballPorter 标记过滤器),因为不太可能对专有名词产生有用的结果。 |
6 | 在 Hibernate Search 配置属性中将配置器分配给后端。有关 Bean 引用的更多信息,请参阅 Bean 引用。 |
package org.hibernate.search.documentation.mapper.pojo.standalone.gettingstarted.withhsearch.customanalysis;
import org.hibernate.search.backend.elasticsearch.analysis.ElasticsearchAnalysisConfigurationContext;
import org.hibernate.search.backend.elasticsearch.analysis.ElasticsearchAnalysisConfigurer;
public class MyElasticsearchAnalysisConfigurer implements ElasticsearchAnalysisConfigurer {
@Override
public void configure(ElasticsearchAnalysisConfigurationContext context) {
context.analyzer( "english" ).custom() (1)
.tokenizer( "standard" ) (2)
.tokenFilters( "lowercase", "snowball_english", "asciifolding" ); (3)
context.tokenFilter( "snowball_english" ) (4)
.type( "snowball" )
.param( "language", "English" ); (5)
context.analyzer( "name" ).custom() (6)
.tokenizer( "standard" )
.tokenFilters( "lowercase", "asciifolding" );
}
}
CloseableSearchMapping searchMapping = SearchMapping.builder( AnnotatedTypeSource.fromClasses(
Book.class, Author.class
) )
.property( "hibernate.search.backend.hosts",
"elasticsearch.mycompany.com" )
.property( "hibernate.search.backend.analysis.configurer",
BeanReference.of( MyElasticsearchAnalysisConfigurer.class, BeanRetrieval.CLASS ) ) (7)
.build();
1 | 定义一个名为“english”的自定义分析器,分析英语文本(例如书名)。 |
2 | 将分词器设置为标准分词器。你需要传递 Elasticsearch 专用名称来引用分词器;有关可用分词器、其名称及其参数的信息,请参阅 自定义分析器和归一化器。 |
3 | 设置令牌过滤器。令牌过滤器按所给出顺序应用。这里也需要 Elasticsearch 专用名称;有关可用令牌过滤器及其名称的信息,请参阅 自定义分析器和归一化器。 |
4 | 请注意,对于 Elasticsearch,任何带参数的字符过滤器、分词器或令牌过滤器都必须单独定义并分配一个新名称。 |
5 | 设置要定义的字符过滤器/分词器/令牌过滤器的参数值。 |
6 | 定义另一个自定义分析器(名为“名称”)来分析作者姓名。与第一个分析器相反,请不要启用词干提取(无 snowball_english 令牌过滤器),因为它不太可能对专有名词产生有用的结果。 |
7 | 在 Hibernate Search 配置属性中将配置器分配给后端。有关 Bean 引用的更多信息,请参阅 Bean 引用。 |
配置分析后,必须调整映射以将相关分析器分配给每个字段
import java.util.HashSet;
import java.util.Set;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.DocumentId;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexedEmbedded;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.KeywordField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.SearchEntity;
@SearchEntity
@Indexed
public class Book {
@DocumentId
private Integer id;
@FullTextField(analyzer = "english") (1)
private String title;
@KeywordField
private String isbn;
@GenericField
private int pageCount;
@IndexedEmbedded
private Set<Author> authors = new HashSet<>();
public Book() {
}
// Getters and setters
// ...
}
import java.util.HashSet;
import java.util.Set;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.AssociationInverseSide;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.DocumentId;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ObjectPath;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.PropertyValue;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.SearchEntity;
@SearchEntity
public class Author {
@DocumentId
private Integer id;
@FullTextField(analyzer = "name") (1)
private String name;
@AssociationInverseSide(inversePath = @ObjectPath(@PropertyValue(propertyName = "authors")))
private Set<Book> books = new HashSet<>();
public Author() {
}
// Getters and setters
// ...
}
1 | 用 @FullTextField 替换 @GenericField 注释,并将 analyzer 参数设置为此前提出的自定义分析器的名称。 |
就是这样!现在,一旦重新编制这些实体索引,你就可以搜索“重构”、“重建”、“重构”或“重构”等词语,并且名为“重构:改善现有代码的设计”的书籍将会显示在结果中。
try ( SearchSession session = searchMapping.createSession() ) {
SearchResult<Integer> result = session.search( Book.class )
.select( f -> f.id( Integer.class ) )
.where( f -> f.match()
.fields( "title", "authors.name" )
.matching( "refactored" ) )
.fetch( 20 );
}