向量搜索

随着生成式人工智能的兴起,向量数据库在数据库领域获得了广泛关注。 这些数据库能够高效地存储和查询高维向量,非常适合用于语义搜索、推荐系统和自然语言理解等任务。spring-doc.cadn.net.cn

向量搜索是一种通过比较向量表示(也称为嵌入)来检索语义上相似数据的技术,而不是依赖传统的精确匹配查询。 这种方法能够实现智能的、具备上下文感知能力的应用程序,超越基于关键词的检索。spring-doc.cadn.net.cn

在 Spring Data 的上下文中,向量搜索为构建智能、上下文感知的应用程序开辟了新的可能性,特别是在自然语言处理、推荐系统和生成式 AI 等领域。 通过使用熟悉的 Repository 抽象来建模基于向量的查询,Spring Data 使开发者能够将支持相似性搜索的向量数据库无缝集成到 Spring Data 编程模型中,同时保持其简洁性和一致性。spring-doc.cadn.net.cn

向量模型

为了以类型安全且符合习惯的方式支持向量搜索,Spring Data 引入了以下核心抽象:spring-doc.cadn.net.cn

Vector

Vector 类型表示一个 n 维数值嵌入,通常由嵌入模型生成。 在 Spring Data 中,它被定义为围绕浮点数数组的轻量级包装器,以确保不可变性和一致性。 该类型可用作搜索查询的输入,或作为领域实体上的属性来存储关联的向量表示。spring-doc.cadn.net.cn

Vector vector = Vector.of(0.23f, 0.11f, 0.77f);

在您的领域模型中使用 Vector 可以避免直接操作原始数组或数字列表,从而提供一种类型更安全、表达力更强的方式来处理向量数据。 这种抽象还便于与各种向量数据库和库轻松集成。 它还支持实现特定于提供商的优化,例如二进制向量或量化向量,这些向量无法映射到标准的浮点数(根据 IEEE 754 标准,即 doublehttps://en.wikipedia.org/wiki/IEEE_754)表示形式。 一个领域对象可以拥有一个向量属性,该属性可用于相似性搜索。 请考虑以下示例:spring-doc.cadn.net.cn

class Comment {

  @Id String id;

  @SaiIndexed
  String country;
  String comment;

  @VectorType(dimensions = 5)
  @SaiIndexed
  Vector embedding;

  // getters, setters, …
}

@VectorType@SaiIndexed 注解用于辅助生成 schema 和提供类型提示。spring-doc.cadn.net.cn

将一个向量与领域对象关联,会导致该向量作为实体生命周期的一部分被加载和存储,这可能会在检索和持久化操作中引入额外的开销。

搜索结果

SearchResult<T> 类型封装了向量相似性查询的结果。 它既包含匹配的领域对象,也包含一个相关性得分,用于指示该对象与查询向量的匹配程度。 这一抽象提供了一种结构化的方式来处理结果排序,使开发者能够轻松地同时处理数据及其上下文相关性。spring-doc.cadn.net.cn

示例 1. 在 Repository 搜索方法中使用 SearchResults<T>
interface CommentRepository extends Repository<Comment, String> {

  SearchResults<Comment> searchByEmbeddingNearAndCountry(Vector vector, ScoringFunction function, String country, Limit limit);

}

SearchResults<Comment> results = repository.searchByEmbeddingNearAndCountry(Vector.of(…), ScoringFunction.cosine(), "…", Limit.of(10));

在此示例中,searchByEmbeddingNearAndCountry 方法返回一个 SearchResults<Comment> 对象,其中包含一组 SearchResult<Comment> 实例。 每个结果都包含匹配的 Comment 实体及其相关性得分。spring-doc.cadn.net.cn

相关性得分是一个数值,用于表示匹配向量与查询向量的对齐程度。 根据得分代表的是距离还是相似度,较高的得分可能意味着更接近的匹配,也可能意味着更远的距离。spring-doc.cadn.net.cn

用于计算此分数的评分函数可能会根据底层数据库、索引或输入参数的不同而有所变化。spring-doc.cadn.net.cn

分数、相似度和评分函数

Score 类型包含一个数值,用于表示搜索结果的相关性。 它可用于根据结果与查询向量的相似度对结果进行排序。 Score 类型通常是一个浮点数,其解释方式(值越大越好还是越小越好)取决于所使用的具体相似度函数。 分数是向量搜索的副产品,并非成功执行搜索操作所必需。 分数值不属于领域模型的一部分,因此最适合表示为带外数据。spring-doc.cadn.net.cn

通常,得分(Score)由一个ScoringFunction计算得出。 用于计算该得分的实际评分函数可能取决于底层数据库,并可从搜索索引或输入参数中获取。spring-doc.cadn.net.cn

Spring Data 支持为常用函数声明常量,例如:spring-doc.cadn.net.cn

欧几里得距离

计算 n 维空间中的直线距离,涉及各维度差值平方和的平方根。spring-doc.cadn.net.cn

余弦相似度

通过先计算两个向量的点积,然后将其结果除以它们长度的乘积进行归一化,从而测量两个向量之间的夹角。spring-doc.cadn.net.cn

点积

计算逐元素相乘后的总和。spring-doc.cadn.net.cn

相似度函数的选择会影响搜索的性能和语义,通常由所使用的底层数据库或索引决定。 Spring Data 会适配数据库原生的评分函数功能,以及该评分是否可用于限制结果数量。spring-doc.cadn.net.cn

Cassandra 通过 similarity_euclideansimilarity_cosinesimilarity_dot_product 函数直接将分数作为相似度值返回。spring-doc.cadn.net.cn

示例2. 在仓库搜索方法中使用ScoringFunction
interface CommentRepository extends Repository<Comment, String> {

  SearchResults<Comment> searchByEmbeddingNear(Vector vector, ScoringFunction function);
}

repository.searchByEmbeddingNear(Vector.of(…), ScoringFunction.cosine());     (1)

repository.searchByEmbeddingNear(Vector.of(…), ScoringFunction.euclidean());  (2)
1 运行搜索并返回结果,使用余弦距离函数计算相似度。
2 运行一次搜索并返回结果,使用欧几里得距离函数来计算相似度。

向量搜索方法

向量搜索方法在仓库中使用与标准 Spring Data 查询方法相同的约定进行定义。 这些方法返回 SearchResults<T>,并需要一个 Vector 参数来定义查询向量。 具体的实现取决于底层数据存储的实际内部机制及其对向量搜索的支持能力。spring-doc.cadn.net.cn

如果你是初次使用 Spring Data 仓库,请务必先熟悉仓库定义和查询方法的基础知识

通常,您可以选择使用两种方法来声明搜索方法:spring-doc.cadn.net.cn

向量搜索方法必须声明一个 Vector 参数以定义查询向量。spring-doc.cadn.net.cn

派生查询方法

派生的搜索方法使用方法名称来推导查询。 在声明搜索方法时,向量搜索支持以下关键字以执行向量搜索:spring-doc.cadn.net.cn

表1. 查询谓词关键字
逻辑关键字 关键字表达式

NEARspring-doc.cadn.net.cn

Near, IsNearspring-doc.cadn.net.cn

WITHINspring-doc.cadn.net.cn

Within, IsWithinspring-doc.cadn.net.cn

示例 3. 在 Repository 搜索方法中使用 NearWithin 关键字
interface CommentRepository extends Repository<Comment, String> {

  List<Comment> searchByEmbeddingNear(Vector vector);

  SearchResults<Comment> searchByEmbeddingNearAndCountry(Vector vector, ScoringFunction function, String country);

}

Cassandra 不允许通过评分来限制搜索结果。 您可以声明一个 ScoringFunction 参数,以指定使用哪种评分函数来计算相似度。spring-doc.cadn.net.cn

派生的搜索方法通常更易于阅读和维护,因为它们依靠方法名称来表达查询意图。 然而,派生的搜索方法需要在 Score/Range<Score> 关键字后将 ScoreFunctionNearWithin 之一作为第二个参数进行声明,以根据评分限制搜索结果。spring-doc.cadn.net.cn

已注解的搜索方法

带注解的方法可完全控制查询语义和参数。 与派生方法不同,它们不依赖于方法命名约定。spring-doc.cadn.net.cn

示例 4. 使用 @Query
interface CommentRepository extends Repository<Comment, String> {

  @Query("""
         SELECT id, description, country, similarity_cosine(embedding,:embedding) AS score
         FROM comments
         WHERE country = :country
         ORDER BY embedding ANN OF :embedding LIMIT :limit
         """)
  SearchResults<WithVectorFields> searchByEmbeddingNearAndCountry(Vector embedding, String country, Limit limit);
}

Cassandra 不允许通过相关性得分来限制搜索结果。 如果您希望返回得分值,声明的搜索方法可以包含一个 score 投影列。 您可以声明一个 ScoringFunction 参数,以指定用于计算相似度的评分函数。spring-doc.cadn.net.cn

通过对实际查询拥有更多控制权,Spring Data 可以减少对查询及其参数的假设。 例如,Similarity 归一化使用查询内部的原生评分函数,将给定的相似度归一化为评分谓词值,反之亦然。 如果注解的查询未定义评分(例如 score),则返回的 SearchResult<T> 中的评分值将为零。spring-doc.cadn.net.cn

排序

Cassandra 向量搜索结果根据其得分进行排序,使用 ORDER BY embedding ANN OF [vector] 子句。spring-doc.cadn.net.cn