你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

使用 .NET 在 Azure Cosmos DB for NoSQL 中编制矢量索引和查询矢量

本文指导你完成如何创建矢量数据、为数据编制索引,然后查询容器中的数据的过程。

在使用矢量索引和搜索之前,必须先在 Azure Cosmos DB for NoSQL 中启用矢量搜索。 设置用于矢量搜索的 Azure Cosmos DB 容器后,将创建矢量嵌入策略。 接下来,将向量索引添加到容器索引策略。 然后,创建包含矢量索引和矢量嵌入策略的容器。 最后,对存储的数据执行矢量搜索。

先决条件

启用功能

若要为 Azure Cosmos DB for NoSQL 启用矢量搜索,请执行以下步骤:

  1. 转到 Azure Cosmos DB for NoSQL 资源页。
  2. 在左窗格中的 “设置”下,选择“ 功能”。
  3. 在 Azure Cosmos DB for NoSQL 中选择矢量搜索
  4. 阅读该功能的说明以确认要启用该功能。
  5. 选择 “启用” 以在 Azure Cosmos DB for NoSQL 中启用矢量搜索。

提示

或者,使用 Azure CLI 更新帐户的功能,以支持 Azure Cosmos DB for NoSQL 矢量搜索。

az cosmosdb update \
     --resource-group <resource-group-name> \
     --name <account-name> \
     --capabilities EnableNoSQLVectorSearch

注册请求已自动批准,但可能需要 15 分钟才能生效。

以下步骤假定你知道如何 设置 Azure Cosmos DB for NoSQL 帐户并创建数据库。 现有容器当前不支持矢量搜索功能。 需要创建新的容器。 创建容器时,可以指定容器级矢量嵌入策略和矢量索引策略。

让我们以一个示例为例,了解如何为基于 Internet 的书店创建数据库。 你想要存储每本书的标题、作者、ISBN 和说明信息。 还需要定义以下两个属性来包含矢量嵌入:

  • contentVector 属性包含从书籍的文本内容生成的 文本嵌入 。 例如,在创建嵌入之前,您需要连接titleauthorisbndescription属性。
  • coverImageVector 属性是从 书籍封面的图像生成的。

若要执行矢量搜索,请执行以下作:

  1. 为要执行矢量搜索的字段创建并存储矢量嵌入。
  2. 在矢量嵌入策略中指定矢量嵌入路径。
  3. 在容器的索引策略中包含所需的任何向量索引。

对于本文的后续部分,请考虑容器中存储的项的以下结构:

{
"title": "book-title", 
"author": "book-author", 
"isbn": "book-isbn", 
"description": "book-description", 
"contentVector": [2, -1, 4, 3, 5, -2, 5, -7, 3, 1], 
"coverImageVector": [0.33, -0.52, 0.45, -0.67, 0.89, -0.34, 0.86, -0.78] 
} 

为容器创建矢量嵌入策略

现在需要定义容器向量策略。 此策略提供了用于通知 Azure Cosmos DB 查询引擎如何处理系统函数中的 VectorDistance 向量属性的信息。 如果选择指定一个,此策略还会向向量索引策略提供必要的信息。

容器向量策略中包括以下信息:

参数 DESCRIPTION
path 包含向量的属性路径。
datatype 矢量的元素的类型。 (默认值为 Float32。)
dimensions 路径中每个向量的长度。 (默认值为 1536。)
distanceFunction 用于计算距离/相似度的指标。 (默认值为 Cosine。)

对于包含书籍详细信息的示例,矢量策略可能如以下示例所示:

  Database db = await client.CreateDatabaseIfNotExistsAsync("vector-benchmarking");
  List<Embedding> embeddings = new List<Embedding>()
  {
      new Embedding()
      {
          Path = "/coverImageVector",
          DataType = VectorDataType.Float32,
          DistanceFunction = DistanceFunction.Cosine,
          Dimensions = 8,
      },
      new Embedding()
      {
          Path = "/contentVector",
          DataType = VectorDataType.Float32,
          DistanceFunction = DistanceFunction.Cosine,
          Dimensions = 10,
      }
  };

在索引策略中创建矢量索引

确定矢量嵌入路径后,必须将向量索引添加到索引策略。 目前,Azure Cosmos DB for NoSQL 的矢量搜索功能仅在新容器上受支持。 创建容器时,应用向量策略。 以后无法修改策略。 索引策略类似于以下示例:

    Collection<Embedding> collection = new Collection<Embedding>(embeddings);
    ContainerProperties properties = new ContainerProperties(id: "vector-container", partitionKeyPath: "/id")
    {   
        VectorEmbeddingPolicy = new(collection),
        IndexingPolicy = new IndexingPolicy()
        {
            VectorIndexes = new()
            {
                new VectorIndexPath()
                {
                    Path = "/vector",
                    Type = VectorIndexType.QuantizedFlat,
                }
            }
        },
    };
    properties.IndexingPolicy.IncludedPaths.Add(new IncludedPath { Path = "/*" });    
    properties.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/vector/*" });

重要

将向量路径添加到索引策略的 excludedPaths 节,以确保插入性能的优化。 excludedPaths 不添加向量路径会增加矢量插入的请求单位费用和延迟时间。

运行矢量相似性搜索查询

使用所需的向量策略创建容器并将矢量数据插入容器后,请在查询中使用 VectorDistance 系统函数执行矢量搜索。

假设你想要通过查看描述来搜索有关食物食谱的书籍。 首先需要获取查询文本的嵌入。 在这种情况下,你可能希望为查询文本 food recipe生成嵌入内容。 在为搜索查询生成嵌入后,您可以在矢量搜索查询的VectorDistance函数中使用该嵌入,以获取与查询类似的所有项。

SELECT TOP 10 c.title, VectorDistance(c.contentVector, [1,2,3,4,5,6,7,8,9,10]) AS SimilarityScore   
FROM c  
ORDER BY VectorDistance(c.contentVector, [1,2,3,4,5,6,7,8,9,10])   

此查询检索书名以及与你的查询相对应的相似度分数。 下面是一个使用 .NET 的示例:

  float[] embedding = {1f,2f,3f,4f,5f,6f,7f,8f,9f,10f};
  var queryDef = new QueryDefinition(
      query: $"SELECT c.title, VectorDistance(c.contentVector,@embedding) AS SimilarityScore FROM c ORDER BY VectorDistance(c.contentVector,@embedding)"
      ).WithParameter("@embedding", embedding);
  using FeedIterator<Object> feed = container.GetItemQueryIterator<Object>(
      queryDefinition: queryDef
  );
  while (feed.HasMoreResults) 
  {
      FeedResponse<Object> response = await feed.ReadNextAsync();
      foreach ( Object item in response)
      {
          Console.WriteLine($"Found item:\t{item}");
      }
  }