向量搜索

随着生成式 AI 的兴起,向量数据库在数据库世界中获得了强大的关注。这些数据库能够高效地存储和查询高维向量,使其非常适合语义搜索、推荐系统和自然语言理解等任务。

向量搜索是一种通过比较向量表示(也称为嵌入)而非依赖传统精确匹配查询来检索语义相似数据的技术。这种方法支持智能的、上下文感知的应用程序,超越了基于关键词的检索。

在 Spring Data 的上下文中,向量搜索为构建智能的、上下文感知的应用程序开辟了新的可能性,尤其是在自然语言处理、推荐系统和生成式 AI 等领域。通过使用熟悉的仓库抽象对基于向量的查询进行建模,Spring Data 允许开发人员以 Spring Data 编程模型的简洁性和一致性无缝集成基于相似度的向量数据库。

要使用 MongoDB 的向量搜索,您需要一个 MongoDB Atlas 实例,该实例可以在云端运行,或者通过使用 Docker 运行。

向量模型

为了以类型安全和惯用的方式支持向量搜索,Spring Data 引入了以下核心抽象

Vector

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

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

在域模型中使用 Vector 消除了使用原始数组或数字列表的需要,提供了一种更类型安全和富有表现力的方式来处理向量数据。这种抽象还允许轻松集成各种向量数据库和库。它还允许实现供应商特定的优化,例如不映射到标准浮点数(根据 IEEE 754floatdouble)表示的二进制或量化向量。域对象可以具有向量属性,可用于相似性搜索。请看以下示例

class Comment {

  @Id String id;
  String country;
  String comment;

  Vector embedding;

  // getters, setters, …
}
将向量与域对象关联会导致向量作为实体生命周期的一部分被加载和存储,这可能会增加检索和持久化操作的额外开销。

搜索结果

SearchResult<T> 类型封装了向量相似性查询的结果。它包括匹配的域对象和表示其与查询向量匹配程度的相关性分数。这种抽象提供了一种结构化的方式来处理结果排名,并使开发人员能够轻松处理数据及其上下文相关性。

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

  @VectorSearch(indexName = "my-index", numCandidates="#{#limit.max() * 20}")
  SearchResults<Comment> searchByCountryAndEmbeddingNear(String country, Vector vector, Score score,
    Limit limit);

  @VectorSearch(indexName = "my-index", limit="10", numCandidates="200")
  SearchResults<Comment> searchByCountryAndEmbeddingWithin(String country, Vector embedding,
      Score score);

}

SearchResults<Comment> results = repository.searchByCountryAndEmbeddingNear("en", Vector.of(…), Score.of(0.9), Limit.of(10));

MongoDB 向量搜索聚合 阶段定义了一组必需的参数和限制。请务必遵循指南,并确保提供诸如 limit 等必需参数。

在此示例中,searchByCountryAndEmbeddingNear 方法返回一个 SearchResults<Comment> 对象,其中包含一个 SearchResult<Comment> 实例列表。每个结果都包括匹配的 Comment 实体及其相关性得分。

相关性分数是一个数值,表示匹配向量与查询向量的匹配程度。根据分数是表示距离还是相似度,更高的分数可能意味着更接近的匹配或更远的匹配。

用于计算此分数的评分函数可以根据底层数据库、索引或输入参数而变化。

分数、相似度和评分函数

Score 类型保存一个数值,表示搜索结果的相关性。它可用于根据搜索结果与查询向量的相似性对结果进行排名。Score 类型通常是浮点数,其解释(越高越好或越低越好)取决于所使用的特定相似性函数。分数是向量搜索的副产品,并非成功搜索操作所必需。分数值不属于域模型,因此最好表示为带外数据。

通常,分数由 ScoringFunction 计算。用于计算此分数的实际评分函数可能取决于底层数据库,并且可以从搜索索引或输入参数中获取。

Spring Data 支持为常用函数声明常量,例如

欧几里得距离

计算 n 维空间中的直线距离,涉及平方差之和的平方根。

余弦相似度

通过首先计算点积,然后将其结果除以它们的长度之积进行归一化来测量两个向量之间的角度。

点积

计算元素乘法之和。

相似性函数的选择会影响搜索的性能和语义,并且通常由所使用的底层数据库或索引决定。Spring Data 适应数据库的本机评分函数功能以及分数是否可用于限制结果。

MongoDB 直接将分数报告为相似度值。评分函数必须在索引中指定,因此,向量搜索方法不考虑 Score.scoringFunction。由于搜索结果中没有关于分数如何计算的信息,评分函数默认为 ScoringFunction.unspecified()

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

  @VectorSearch(…)
  SearchResults<Comment> searchTop10ByEmbeddingNear(Vector vector, Score similarity);

  @VectorSearch(…)
  SearchResults<Comment> searchTop10ByEmbeddingNear(Vector vector, Similarity similarity);

  @VectorSearch(…)
  SearchResults<Comment> searchTop10ByEmbeddingNear(Vector vector, Range<Similarity> range);
}

repository.searchByEmbeddingNear(Vector.of(…), Score.of(0.9));                (1)

repository.searchByEmbeddingNear(Vector.of(…), Similarity.of(0.9));           (2)

repository.searchByEmbeddingNear(Vector.of(…), Similarity.between(0.5, 1));   (3)
1 运行搜索并返回相似度为 0.9 或更高的结果。
2 返回相似度为 0.9 或更高的结果。
3 返回相似度介于 0.51.0 之间的结果。

向量搜索方法

向量搜索方法使用与标准 Spring Data 查询方法相同的约定在存储库中定义。这些方法返回 SearchResults<T> 并需要一个 Vector 参数来定义查询向量。实际实现取决于底层数据存储的实际内部结构及其向量搜索功能。

如果您不熟悉 Spring Data 存储库,请务必熟悉存储库定义和查询方法的基础知识

通常,您可以选择使用两种方法声明搜索方法

  • 查询派生

  • 声明基于字符串的查询

向量搜索方法必须声明一个 Vector 参数来定义查询向量。

派生搜索方法

派生搜索方法使用方法名来派生查询。向量搜索支持以下关键词在声明搜索方法时运行向量搜索

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

NEAR

Near, IsNear

WITHIN

Within, IsWithin

MongoDB 搜索方法必须使用 @VectorSearch 注解来定义 $vectorSearch 聚合阶段的索引名称。

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

  @VectorSearch(indexName = "my-index", numCandidates="200")
  SearchResults<Comment> searchTop10ByEmbeddingNear(Vector vector, Score score);

  @VectorSearch(indexName = "my-index", numCandidates="200")
  SearchResults<Comment> searchTop10ByEmbeddingWithin(Vector vector, Range<Similarity> range);

  @VectorSearch(indexName = "my-index", numCandidates="200")
  SearchResults<Comment> searchTop10ByCountryAndEmbeddingWithin(String country, Vector vector, Range<Similarity> range);
}

派生搜索方法可以定义领域模型属性来为索引字段创建预过滤器。

派生搜索方法通常更容易阅读和维护,因为它们依赖方法名来表达查询意图。但是,派生搜索方法需要声明一个 ScoreRange<Score>ScoreFunction 作为 Near/Within 关键词的第二个参数,以通过分数限制搜索结果。

注解搜索方法

注解方法提供对查询语义和参数的完全控制。与派生方法不同,它们不依赖于方法名约定。

带注解的搜索方法使用 @VectorSearch 注解来定义 $vectorSearch 聚合阶段的参数。

示例 4. 使用 @VectorSearch 搜索方法
interface CommentRepository extends Repository<Comment, String> {

  @VectorSearch(indexName = "cos-index", filter = "{country: ?0}", limit="100", numCandidates="2000")
  SearchResults<Comment> searchAnnotatedByCountryAndEmbeddingWithin(String country, Vector embedding,
      Score distance);

  @VectorSearch(indexName = "my-index", filter = "{country: ?0}", limit="?3", numCandidates = "#{#limit * 20}",
				searchType = VectorSearchOperation.SearchType.ANN)
  List<Comment> findAnnotatedByCountryAndEmbeddingWithin(String country, Vector embedding, Score distance, int limit);
}

带注解的搜索方法可以定义 filter 用于预过滤。

filterlimitnumCandidates 支持值表达式,允许引用搜索方法参数。

通过对实际查询的更多控制,Spring Data 可以对查询及其参数做出更少的假设。例如,Similarity 归一化使用查询中的原生评分函数将给定相似度归一化为评分谓词值,反之亦然。如果注解查询未定义,例如,分数,则返回的 SearchResult<T> 中的分数将为零。

排序

默认情况下,搜索结果按其分数排序。您可以使用 Sort 参数覆盖排序。

示例 5. 在存储库搜索方法中使用 Sort
interface CommentRepository extends Repository<Comment, String> {

  SearchResults<Comment> searchByEmbeddingNearOrderByCountry(Vector vector, Score score);

  SearchResults<Comment> searchByEmbeddingWithin(Vector vector, Score score, Sort sort);
}

请注意,自定义排序不允许将分数表示为排序条件。您只能引用域属性。

© . This site is unofficial and not affiliated with VMware.