此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Data JPA 3.5.2! |
矢量搜索
随着生成式人工智能的兴起,矢量数据库在数据库领域获得了强大的关注。这些数据库可以高效存储和查询高维向量,使其非常适合语义搜索、推荐系统和自然语言理解等任务。
向量搜索是一种通过比较向量表示(也称为嵌入)而不是依赖传统的精确匹配查询来检索语义相似数据的技术。这种方法支持智能的、上下文感知的应用程序,超越基于关键字的检索。
在 Spring Data 的上下文中,向量搜索为构建智能、上下文感知应用程序开辟了新的可能性,特别是在自然语言处理、推荐系统和生成式人工智能等领域。通过使用熟悉的存储库抽象对基于向量的查询进行建模,Spring Data 允许开发人员将基于相似性的向量数据库与 Spring Data 编程模型的简单性和一致性无缝集成。
要使用 Hibernate 矢量搜索,您需要向项目添加以下依赖项。
以下示例展示了如何在 Maven 和 Gradle 中设置依赖项:
-
Maven
-
Gradle
<dependencies>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-vector</artifactId>
<version>${hibernate.version}</version>
</dependency>
</dependencies>
dependencies {
implementation 'org.hibernate.orm:hibernate-vector:${hibernateVersion}'
}
虽然您可以使用Vector 作为查询的类型,您不能在域模型中使用它,因为 Hibernate 需要浮点数组或双数组作为向量类型。 |
矢量模型
为了以类型安全和惯用的方式支持向量搜索,Spring Data 引入了以下核心抽象:
Vector
这Vector
type 表示 n 维数值嵌入,通常由嵌入模型生成。在 Spring Data 中,它被定义为浮点数组周围的轻量级包装器,确保不变性和一致性。此类型可用作搜索查询的输入或作为域实体上的属性来存储关联的向量表示。
Vector vector = Vector.of(0.23f, 0.11f, 0.77f);
用Vector
在域模型中,无需使用原始数组或数字列表,从而提供了一种类型安全和富有表现力的方式来处理矢量数据。这种抽象还允许与各种矢量数据库和库轻松集成。它还允许实现特定于提供商的优化,例如未映射到标准浮点(float
和double
截至 IEEE 754) 表示。域对象可以具有向量属性,可用于相似性搜索。考虑以下示例:
class Comment {
@Id String id;
String country;
String comment;
@Column(name = "the_embedding")
@JdbcTypeCode(SqlTypes.VECTOR)
@Array(length = 5)
Vector embedding;
// getters, setters, …
}
将向量与域对象相关联会导致向量作为实体生命周期的一部分加载和存储,这可能会给检索和持久化作带来额外的开销。 |
搜索结果
这SearchResult<T>
类型封装向量相似性查询的结果。它包括匹配的域对象和相关性分数,该分数指示它与查询向量的匹配程度。这种抽象提供了一种处理结果排名的结构化方法,使开发人员能够轻松处理数据及其上下文相关性。
SearchResult<T>
在存储库搜索方法中interface CommentRepository extends Repository<Comment, String> {
SearchResults<Comment> searchByCountryAndEmbeddingNear(String country, Vector vector, Score distance,
Limit limit);
@Query("""
SELECT c, cosine_distance(c.embedding, :embedding) as distance FROM Comment c
WHERE c.country = ?1
AND cosine_distance(c.embedding, :embedding) <= :distance
ORDER BY distance asc""")
SearchResults<Comment> searchAnnotatedByCountryAndEmbeddingWithin(String country, Vector embedding,
Score distance);
}
SearchResults<Comment> results = repository.searchByCountryAndEmbeddingNear("en", Vector.of(…), Score.of(0.9), Limit.of(10));
在此示例中,searchByCountryAndEmbeddingNear
方法返回一个SearchResults<Comment>
对象,其中包含SearchResult<Comment>
实例。
每个结果都包括匹配的Comment
实体及其相关性分数。
相关性分数是一个数值,指示匹配向量与查询向量的一致性。 根据分数是否代表距离或相似性,较高的分数可能意味着更接近或更远的匹配。
用于计算此分数的评分函数可能会根据底层数据库、索引或输入参数而有所不同。
评分、相似性和评分函数
这Score
type 包含一个数值,指示搜索结果的相关性。
它可用于根据结果与查询向量的相似性对结果进行排名。
这Score
类型通常是一个浮点数,其解释(越高越好或越低越好)取决于所使用的特定相似性函数。
分数是向量搜索的副产品,不是成功搜索作所必需的。
分数值不是域模型的一部分,因此最好表示为带外数据。
通常,分数由ScoringFunction
.
用于计算此分数的实际评分函数可能取决于基础数据库,并且可以从搜索索引或输入参数中获取。
Spring Data 支持为常用函数声明常量,例如:
- Euclidean Distance
-
计算 n 维空间中的直线距离,涉及差分平方和的平方根。
- 余弦相似度
-
通过首先计算点积,然后通过除以它们长度的乘积来归一化其结果,来测量两个向量之间的角度。
- 点积
-
计算元素乘法的总和。
相似性函数的选择会影响搜索的性能和语义,并且通常由所使用的基础数据库或索引决定。 Spring Data 采用数据库的原生评分函数能力,以及分数是否可以用来限制结果。
Hibernate 将距离函数调用转换为 PGvector 和 Oracle 的本机数据库函数。
它们的结果通常是距离。
使用时Similarity
而不是Score
,Spring Data 将距离分数归一化为 0 和 1 之间的相似性分数。分数越高,两个向量越相似。
Score
和Similarity
在存储库中搜索方法interface CommentRepository extends Repository<Comment, String> {
SearchResults<Comment> searchByEmbeddingNear(Vector vector, ScoringFunction function);
SearchResults<Comment> searchByEmbeddingNear(Vector vector, Score score);
SearchResults<Comment> searchByEmbeddingNear(Vector vector, Similarity similarity);
SearchResults<Comment> searchByEmbeddingNear(Vector vector, Range<Similarity> range);
}
repository.searchByEmbeddingNear(Vector.of(…), ScoringFunction.cosine()); (1)
repository.searchByEmbeddingNear(Vector.of(…), Score.of(0.9, ScoringFunction.cosine())); (2)
repository.searchByEmbeddingNear(Vector.of(…), Similarity.of(0.9, ScoringFunction.cosine())); (3)
repository.searchByEmbeddingNear(Vector.of(…), Similarity.between(0.5, 1, ScoringFunction.euclidean()));(4)
1 | 运行搜索并返回与给定结果类似的结果Vector 应用余弦评分。 |
2 | 运行搜索并返回得分为0.9 或使用余弦距离更小。 |
3 | 运行搜索并将分数规范化为相似性值。
返回相似度为0.9 或更高,使用余弦评分。 |
4 | 运行搜索并将分数规范化为相似性值。
返回相似度为0.5 和1.0 或更高,使用欧几里得评分。 |
JPA 要求ScoringFunction 创建时提供Score 或Similarity 实例以选择评分函数。 |
向量搜索方法
向量搜索方法使用与标准 Spring Data 查询方法相同的约定在存储库中定义。这些方法返回SearchResults<T>
并要求Vector
参数来定义查询向量。实际实现取决于基础数据存储的实际内部结构及其围绕向量搜索的功能。
如果您是 Spring Data 存储库的新手,请务必熟悉存储库定义和查询方法的基础知识。 |
通常,您可以选择使用两种方法声明搜索方法:
-
查询派生
-
声明基于字符串的查询
向量搜索方法必须声明Vector
参数来定义查询向量。
派生搜索方法
派生搜索方法使用方法的名称来派生查询。 向量搜索支持以下关键字在声明搜索方法时运行向量搜索:
逻辑关键字 | 关键字表达式 |
---|---|
|
|
|
|
Near
和Within
存储库搜索方法中的关键字interface CommentRepository extends Repository<Comment, String> {
SearchResults<Comment> searchByEmbeddingNear(Vector vector, Score score);
SearchResults<Comment> searchByEmbeddingWithin(Vector vector, Range<Similarity> range);
SearchResults<Comment> searchByCountryAndEmbeddingWithin(String country, Vector vector, Range<Similarity> range);
}
派生搜索方法可以声明域模型属性和 Vector 参数的谓词。
派生搜索方法通常更易于阅读和维护,因为它们依赖于方法名称来表达查询意图。但是,派生搜索方法需要声明Score
,Range<Score>
或ScoreFunction
作为第二个参数Near
/Within
关键字以按分数限制搜索结果。
带注释的搜索方法
带注释的方法提供对查询语义和参数的完全控制。与派生方法不同,它们不依赖于方法名称约定。
带注释的搜索方法必须定义整个 JPQL 查询才能运行矢量搜索。
@Query
搜索方法interface CommentRepository extends Repository<Comment, String> {
@Query("""
SELECT c, cosine_distance(c.embedding, :embedding) as distance FROM Comment c
WHERE c.country = ?1
AND cosine_distance(c.embedding, :embedding) <= :distance
ORDER BY distance asc""")
SearchResults<Comment> searchAnnotatedByCountryAndEmbeddingWithin(String country, Vector embedding,
Score distance);
@Query("""
SELECT c FROM Comment c
WHERE c.country = ?1
AND cosine_distance(c.embedding, :embedding) <= :distance
ORDER BY cosine_distance(c.embedding, :embedding) asc""")
List<Comment> findAnnotatedByCountryAndEmbeddingWithin(String country, Vector embedding, Score distance);
}
矢量搜索方法不需要在其投影中包含分数或距离。
使用带注释的搜索方法返回SearchResults
,则执行机制假定如果存在第二个投影列,则该投影列保存分数值。
通过对实际查询的更多控制,Spring Data 可以对查询及其参数做出更少的假设。
例如Similarity
规范化使用查询中的本机 score 函数将给定的相似性规范化为分数谓词值,反之亦然。
如果带注释的查询未定义分数,则返回的SearchResult<T>
将为零。
排序
默认情况下,搜索结果根据其分数进行排序。
您可以使用Sort
参数:
Sort
在存储库搜索方法中interface CommentRepository extends Repository<Comment, String> {
SearchResults<Comment> searchByEmbeddingNearOrderByCountry(Vector vector, Score score);
SearchResults<Comment> searchByEmbeddingWithin(Vector vector, Score score, Sort sort);
}
请注意,自定义排序不允许将分数表示为排序标准。 您只能引用域属性。