|
以下是详细说明的功能仍在孵化中:仍在处于积极开发阶段。 不适用于通常的 兼容性策略:正在孵化元素(例如类型、方法、配置属性等)的约定在后续版本中可能会以向后不兼容的方式更改,甚至移除。 我们强烈建议您使用孵化中的功能,以便开发团队可以获取反馈并对其进行改进,但您应做好准备根据需要更新依赖于这些功能的代码。 |
本指南将引导您了解使用 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 | Book 嵌入了来自 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 );
}