Hibernate Search 是一个库,通过自动索引实体,将 Hibernate ORM 与 Apache Lucene 或 Elasticsearch 集成,从而实现高级搜索功能:全文搜索、地理空间搜索、聚合等功能。更多详情请访问 hibernate.org 上的 Hibernate Search。

我们很高兴宣布发布 Hibernate Search 7.2.0.Final 版本。

与 Hibernate Search 7.1 相比,本版本对 Search DSL 进行了许多改进,包括新的投影类型、新的谓词、对现有功能的增强、查询参数等。

本版本还包括许可证变更,改为 Apache License 2.0,这将作为项目的许可证。

本版本升级到 Hibernate ORM 6.6,引入了与 OpenSearch 2.14、2.15 和 2.16 的兼容性,以及与 Elasticsearch 8.14 和 8.15 的兼容性。

与 Hibernate Search 7.1 相比的新特性

要查看自 7.1 版本以来所有新功能和改进的总结,请访问 hibernate.org 上的专门页面

依赖项升级

Hibernate ORM (HSEARCH-5219)

Hibernate Search 现在依赖于 Hibernate ORM 6.6.0.Final。

Lucene (HSEARCH-5184)

Lucene 后端现在使用 Lucene 9.11.1。

OpenSearch (HSEARCH-5181)/(HSEARCH-5151)/(HSEARCH-5218)

Elasticsearch 后端现在与 OpenSearch 2.14/2.15/2.16 以及其他已兼容的版本兼容。

OpenSearch 2.16 介绍了范围聚合的问题,由于搜索查询被忽略,聚合的结果可能不正确。OpenSearch 团队已意识到 问题 并正在修复它。在决定升级到 2.16 时请考虑这一点。

Elasticsearch (HSEARCH-5164)/(HSEARCH-5220)

Elasticsearch后端现在与Elasticsearch 8.14/8.15兼容,以及与其他已经兼容的版本。

其他

knn谓词更新

OpenSearch 2.14版本引入了一种将评分/相似度过滤器应用于knn查询的方法。这意味着在OpenSearch分布的Elasticsearch后端使用时,之前对向量搜索过滤所施加的限制现在已被移除。值得注意的是,由于在OpenSearch端实现此过滤器的方式,应用相似度过滤器会导致忽略k值。

knn谓词,除了现有的.requiredMinimumSimilarity(..)过滤器外,现在还提供了一个基于评分的替代方案:requiredMinimumScore(..)。在knn搜索中,相似度和评分是相互推导的,在某些场景下,使用评分可能更简单,而在其他场景下则使用相似度。

提醒您向量搜索是如何工作的:要使向量字段可以索引,它们应该使用@VectorField注解进行标注

@Entity
@Indexed
public class Book {

    @Id
    private Integer id;

    @VectorField(dimension = 512)
    private float[] coverImageEmbeddings;

    // Other properties ...
}

然后,通过knn谓词执行向量相似度搜索

float[] coverImageEmbeddingsVector = /*...*/

List<Book> hits = searchSession.search( Book.class )
.where( f ->
    f.knn( 5 ) (1)
        .field( "coverImageEmbeddings" ) (2)
        .matching( coverImageEmbeddingsVector ) (3)
        .requiredMinimumSimilarity( similarity ) (4)
).fetchHits( 20 );
1 提供要查找的相似文档的数量。
2 指定向量字段的名称。
3 提供参考向量;匹配的文档将是那些索引向量与该向量“最相似”的文档。
4 指定参考向量和索引向量之间的所需最小相似度;索引向量相似度小于指定相似度值的文档将被过滤掉。或者,可以使用requiredMinimumScore(score)过滤器代替requiredMinimumSimilarity(similarity)

有关更多信息,请参阅关于向量字段knn谓词的参考文档的这一部分。

前缀谓词

prefix谓词匹配那些给定字段值以给定字符串开头的文档。

List<Book> hits = searchSession.search( Book.class )
    .where( f -> f.prefix().field( "description" )
        .matching( "rob" ) )
    .fetchHits( 20 );

有关更多信息,请参阅关于prefix谓词的参考文档的这一部分。

ValueConvert弃用

Search DSL一直使用ValueConvert枚举来让用户指定搜索查询中的值如何转换,无论是传递给谓词/聚合/排序的值,还是由投影/聚合返回的值。从现在开始,为了更好地支持新用例并提高期望的清晰度,这个枚举被新的一个枚举所取代:ValueModel。目前ValueModel提供了以下选项

映射

这是允许以在实体侧定义的类型工作的默认模型。

索引

此模型不进行转换,允许以在索引侧定义的类型工作。

字符串

此模型应用格式化和解析,允许处理值的字符串表示。

原始

此模型不进行转换,允许在底层使用后端操作的类型。

对于每个接受ValueConvert输入的已弃用方法,现在有一个接受ValueModel的替代方法。在您的代码中将ValueConvert.YES替换为ValueModel.MAPPING,将ValueConvert.NO替换为ValueModel.INDEX,其中值已显式设置。

有关传递给DSL的参数类型和投影值类型的更多信息,请参阅参考文档的此部分此部分

within/withinAny用于范围谓词

range谓词现在可以接受多个范围,当值至少位于提供的其中一个范围内时匹配文档。

List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.range().field( "pageCount" )
                .withinAny(
                        Range.between( 200, 250 ),
                        Range.between( 500, 800 )
                ) )
        .fetchHits( 20 );

数字/日期字段上的queryString/simpleQueryString谓词

simpleQueryStringqueryString现在可以应用于数字和日期字段。

List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.queryString()
                .field( "numberOfPages" )
                .matching( "[350 TO 800]" )
        )
        .fetchHits( 20 );

有关详细信息,请参阅simpleQueryString simpleQueryStringqueryString queryString文档中相应的部分。

match谓词和应匹配的最小术语数

随着minimumShouldMatch选项的引入,类似于已为boolqueryStringsimpleQueryString谓词提供的,现在可以通过match谓词匹配,要求匹配字符串中的任意数量术语都存在于文档中。

List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.match()
                .field( "title" )
                .matching( "investigation detective automatic" )
                .minimumShouldMatchNumber( 2 ) ) (1)
        .fetchHits( 20 ); (2)
1 至少有两个术语必须匹配,此谓词才能匹配。
2 所有返回的命中项至少匹配两个术语:它们的标题将匹配investigationdetectiveinvestigationautomaticdetectiveautomatic,或者所有这三个术语。

查询级别的参数基本支持

Search DSL中引入了多个withParameters(..)方法。通过它们,现在可以使用查询参数构建聚合谓词投影排序。当需要在查询的多个部分中使用相同的参数或需要为各种参数值执行相同的查询时,这些非常有用。

SearchScope<Book> scope = searchSession.scope( Book.class );
SearchPredicateFactory factory = scope.predicate();
SearchPredicate predefinedPredicate = factory.withParameters(
        params -> factory.bool() (1)
                .should( factory.match().field( "title" )
                        .matching( params.get( "title-param", String.class ) ) ) (2)
                .filter( factory.match().field( "genre" )
                        .matching( params.get( "genre-param", Genre.class ) ) ) (3)
).toPredicate();

List<Book> crimeBooks = searchSession.search( Book.class )
        .where( predefinedPredicate ) (4)
        .param( "title-param", "robot" )  (5)
        .param( "genre-param", Genre.CRIME_FICTION )
        .fetchHits( 20 );

List<Book> scienceFictionBooks = searchSession.search( Book.class )
        .where( predefinedPredicate ) (6)
        .param( "title-param", "spaceship" ) (7)
        .param( "genre-param", Genre.SCIENCE_FICTION )
        .fetchHits( 20 );
1 开始创建.withParameters()谓词。
2 在构建谓词时访问查询参数title-param,它为String类型。
3 在构建谓词时访问查询参数genre-param,它为Genre枚举类型。
4 在查询中使用预定义的参数化谓词。
5 在查询级别设置谓词所需的参数。
6 在查询中重用预定义的参数化谓词。
7 在查询级别设置谓词所需的不同参数对。

@DistanceProjection 将构造函数参数映射到距离投影

随着查询参数的引入,现在可以定义一个 @DistanceProjection,该投影可以在投影构造函数中使用。

@ProjectionConstructor
public record MyAuthorPlaceProjection(
        @DistanceProjection( (1)
                fromParam = "point-param", (2)
                path = "placeOfBirth") (3)
        Double distance ) {
}
1 使用 @DistanceProjection 注解应接收距离值的参数。
2 指定将用于计算距离的查询参数。
3 可选地,自定义路径,因为实体类的 GeoPoint 属性可能不同于投影中的距离属性。
List<MyAuthorPlaceProjection> hits = searchSession.search( Author.class )
        .select( MyAuthorPlaceProjection.class )
        .where( f -> f.matchAll() )
        .param( "point-param", GeoPoint.of( latitude, longitude ) ) (1)
        .fetchHits( 20 );
1 传递一个查询参数值,与投影构造函数中 @DistanceProjectionfromParampoint-param 名称相同。

文档树投影

使用 Lucene 后端,现在可以请求文档树投影。这个新的 .documentTree() 投影返回匹配的文档作为包含原生 Lucene Document 和相应的嵌套树节点的树。

List<DocumentTree> hits = searchSession.search( Book.class )
        .extension( LuceneExtension.get() )
        .select( f -> f.documentTree() )
        .where( f -> f.matchAll() )
        .fetchHits( 20 );

DocumentTree documentTree = hits.get( 0 );
Document rootDocument = documentTree.document();
Map<String, Collection<DocumentTree>> nestedDocuments = documentTree.nested();
// ...

其他改进和错误修复

  • HSEARCH-5170:修复在删除实体之前将关联设置为 null 可能不会触发索引的潜在问题

  • HSEARCH-5161:防止在配置了多租户的情况下,在启动时请求大量索引器时删除模式。

  • HSEARCH-5162:确保 Hibernate Search 在启用 Hibernate ORM 的 JPA 兼容性时(hibernate.jpa.compliance.query=true)正确工作。

  • HSEARCH-5107/HSEARCH-5108:更快地关闭 Lucene 索引读取器。

  • HSEARCH-4572:使用比搜索查询更广泛的范围使用 SearchPredicate/SearchProjection/SearchSort。

  • HSEARCH-4929:在启动大量索引时使用 Jakarta Batch 集成添加一个选项来删除和创建模式。

  • HSEARCH-4963:API 以在给定的 String 上运行分析。

  • HSEARCH-5006:非字符串租户标识符

  • HSEARCH-5016:在 numberOfFragments(1) 时,允许将 @HighlightProjection 绑定到单值 String(而不是 List

  • HSEARCH-5039:修复 knn 谓词的租户过滤器,并将其用作 knn 谓词的过滤器

  • HSEARCH-5124:Jakarta Batch Job 参数 purgeAllOnStart 现在将仅清除 entityTypes 中指定的类型的文档,而不是清除所有文档。

与 Hibernate Search 7.2.0.CR1 相比的新增功能

如何获取此版本

所有详细信息均可在 hibernate.org 的专用页面上 获取并保持最新。

入门,迁移

对于新应用程序,请参阅以下指南:

对于现有应用程序,Hibernate Search 7.2 可以直接替换 7.1 版本,前提是你也升级了依赖项。有关弃用的配置和 API 的信息包含在 迁移指南 中。

反馈、问题、想法?

要取得联系,请使用以下渠道


返回顶部