适用范围: NoSQL
在使用矢量索引和搜索功能之前,必须先启用该功能。 本文涵盖以下步骤:
- 启用 Azure Cosmos DB for NoSQL 中的矢量搜索功能。
 - 为矢量搜索设置 Azure Cosmos DB 容器
 - 创建矢量嵌入策略
 - 将矢量索引添加到容器索引策略
 - 使用矢量索引和矢量嵌入策略创建容器
 - 对存储的数据执行矢量搜索
 
本指南逐步讲解创建矢量数据、为数据编制索引,然后查询容器中的数据的过程。
先决条件
- 一个现有的 Azure Cosmos DB for NoSQL 帐户。
- 如果没有 Azure 订阅,请 试用 Azure Cosmos DB for NoSQL。
 - 如果你当前有 Azure 订阅,请创建新的 Azure Cosmos DB for NoSQL 帐户。
 
 - 最新版本的 Azure Cosmos DB .NET SDK(正式版:3.45.0 或更高版本,预览版:3.46.0-preview.0 或更高版本)。
 
启用功能
若要对 Azure Cosmos DB for NoSQL 进行矢量搜索,则需要通过完成以下步骤来启用该功能:
- 导航到 Azure Cosmos DB for NoSQL 资源页。
 - 选择“设置”菜单项下的“功能”窗格。
 - 选择“Azure Cosmos DB for NoSQL 中的矢量搜索”。
 - 阅读有关该功能的说明,确认你想要启用该功能。
 - 选择“启用”以启用 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”,基于书籍封面图像生成。
- 为要执行矢量搜索的字段创建并存储矢量嵌入。
 - 在矢量嵌入策略中指定矢量嵌入路径。
 - 在容器的索引策略中包含任何所需的矢量索引。
 
在本文的后续部分中,我们假设存储在容器中的项采用以下结构:
{
"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}");
      }
  }