使用 Python 在 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 和说明信息。 还需要定义以下两个属性来包含矢量嵌入:

  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。)

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

vector_embedding_policy = { 
    "vectorEmbeddings": [ 
        { 
            "path": "/coverImageVector", 
            "dataType": "float32", 
            "distanceFunction": "dotproduct", 
            "dimensions": 8 
        }, 
        { 
            "path": "/contentVector", 
            "dataType": "float32", 
            "distanceFunction": "cosine", 
            "dimensions": 10 
        } 
    ]    
} 

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

确定矢量嵌入路径后,必须将向量索引添加到索引策略。 索引策略类似于以下示例:

indexing_policy = { 
    "includedPaths": [ 
        { 
            "path": "/*" 
        } 
    ], 
    "excludedPaths": [ 
        { 
            "path": "/\"_etag\"/?",
            "path": "/coverImageVector/*",
            "path": "/contentVector/*"
            
        } 
    ], 
    "vectorIndexes": [ 
        {"path": "/coverImageVector", 
         "type": "quantizedFlat" 
        }, 
        {"path": "/contentVector", 
         "type": "quantizedFlat" 
        } 
    ] 
} 

重要

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

目前,新容器仅支持 Azure Cosmos DB for NoSQL 中的矢量搜索。 创建容器时,需要同时设置容器向量策略和任何向量索引策略,因为以后无法对其进行修改。

使用矢量策略创建容器

目前,Azure Cosmos DB for NoSQL 的矢量搜索功能仅在新容器上受支持。 创建容器时,应用向量策略。 以后无法修改策略。

try:     
    container = db.create_container_if_not_exists( 
                    id=CONTAINER_NAME, 
                    partition_key=PartitionKey(path='/id'), 
                    indexing_policy=indexing_policy, 
                    vector_embedding_policy=vector_embedding_policy) 
    print('Container with id \'{0}\' created'.format(id)) 

except exceptions.CosmosHttpResponseError: 
        raise 

运行矢量相似性搜索查询

使用所需的向量策略创建容器并将矢量数据插入容器后,请在查询中使用 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])   

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

query_embedding = [1,2,3,4,5,6,7,8,9,10] 
# Query for items 
for item in container.query_items( 
            query='SELECT c.title, VectorDistance(c.contentVector,@embedding) AS SimilarityScore FROM c ORDER BY VectorDistance(c.contentVector,@embedding)', 
            parameters=[ 
                {"name": "@embedding", "value": query_embedding} 
            ], 
            enable_cross_partition_query=True): 
    print(json.dumps(item, indent=True))