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

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

在使用矢量索引和搜索功能之前,必须先启用该功能。 本文涵盖以下步骤:

  1. 启用 Azure Cosmos DB for NoSQL 中的矢量搜索功能。
  2. 为矢量搜索设置 Azure Cosmos DB 容器
  3. 创建矢量嵌入策略
  4. 将矢量索引添加到容器索引策略
  5. 使用矢量索引和矢量嵌入策略创建容器
  6. 对存储的数据执行矢量搜索

本指南逐步讲解创建矢量数据、为数据编制索引,然后查询容器中的数据的过程。

先决条件

启用功能

若要对 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 更新帐户的功能以支持 NoSQL 矢量搜索。

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

注意

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

让我们以为 Internet 书店创建数据库为例,为每本书存储“书名”、“作者”、ISBN 和“描述”。 我们还定义了两个属性来包含矢量嵌入。 第一个是“contentVector”属性,它包含从书籍文本内容生成的文本嵌入(例如,在创建嵌入之前将“书名”“作者”“isbn”和“描述”属性连接起来)。 第二个是“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 系统函数中的矢量属性。 如果你选择指定矢量索引策略,此策略还会为矢量索引策略提供必要的信息。 包含的矢量策略中包含以下信息:

  • “path”:包含矢量的属性路径
  • “datatype”:矢量元素的类型(默认为 Float32)
  • “dimensions”:路径中每个矢量的长度(默认为 1536)
  • “distanceFunction”:用于计算距离/相似性的指标(默认为余弦)

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

  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”会导致矢量插入的 RU 费用和延迟较高。

运行矢量相似性搜索查询

使用所需矢量策略创建容器并将矢量数据插入容器后,可以在查询中使用矢量距离系统函数进行矢量搜索。 假设你想通过查看描述来搜索有关食谱的书籍,首先需要获取查询文本的嵌入。 在这种情况下,可能需要为查询文本“食谱”生成嵌入。 获得搜索查询的嵌入后,可以在矢量搜索查询中的 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}");
      }
  }