将矢量搜索与 Spanner Graph 搭配使用

本页介绍了如何在 Spanner 图中使用向量搜索查找 K 最近邻 (KNN) 和近似最近邻 (ANN)。您可以使用向量距离函数执行 KNN 和 ANN 向量搜索,以便在生成式 AI 应用中实现相似度搜索或检索增强生成等用例。

Spanner Graph 支持以下距离函数来执行 KNN 向量相似度搜索

  • COSINE_DISTANCE():衡量两个向量之间的最短距离。
  • EUCLIDEAN_DISTANCE():测量两个向量之间的余弦角度。
  • DOT_PRODUCT():计算角度余弦乘以相应矢量大小的乘积。如果您知道数据集中的所有向量嵌入均已归一化,则可以使用 DOT_PRODUCT() 作为距离函数。

如需了解详情,请参阅通过查找 K 个最近邻在 Spanner 中执行向量相似度搜索

Spanner Graph 还支持以下近似距离函数,以执行 ANN 向量相似度搜索

  • APPROX_COSINE_DISTANCE:衡量两个向量之间的近似最短距离。
  • APPROX_EUCLIDEAN_DISTANCE:测量两个向量之间的夹角的近似余弦值。
  • APPROX_DOT_PRODUCT:计算角度的近似余弦乘以相应矢量大小的乘积。如果您知道数据集中的所有向量嵌入都已归一化,则可以将 DOT_PRODUCT() 用作距离函数。

如需了解详情,请参阅查找近似最近邻、创建矢量索引和查询矢量嵌入

准备工作

如需运行本文档中的示例,您必须先按照设置和查询 Spanner 图中的步骤执行以下操作:

  1. 创建实例
  2. 创建采用 Spanner 图架构的数据库
  3. 插入基本图表数据

插入基本图表数据后,请对数据库进行以下更新。

在图数据库中插入其他矢量数据

如需对图表数据库进行必要的更新,请执行以下操作:

  1. Account 输入表添加新列 nick_name_embeddings

    ALTER TABLE Account
    ADD COLUMN nick_name_embeddings ARRAY<FLOAT32>(vector_length=>4);
    
  2. nick_name 列添加数据。

    UPDATE Account SET nick_name = "Fund for a refreshing tropical vacation" WHERE id = 7;
    UPDATE Account SET nick_name = "Fund for a rainy day!" WHERE id = 16;
    UPDATE Account SET nick_name = "Saving up for travel" WHERE id = 20;
    
  3. nick_name 列中的文本创建嵌入,并将其填充到新的 nick_name_embeddings 列中。

    如需为 Spanner Graph 中的运营数据生成 Vertex AI 嵌入,请参阅获取 Vertex AI 文本嵌入

    为了说明问题,我们的示例使用了人工的低维向量值。

    UPDATE Account SET nick_name_embeddings = ARRAY<FLOAT32>[0.3, 0.5, 0.8, 0.7] WHERE id = 7;
    UPDATE Account SET nick_name_embeddings = ARRAY<FLOAT32>[0.4, 0.9, 0.7, 0.1] WHERE id = 16;
    UPDATE Account SET nick_name_embeddings = ARRAY<FLOAT32>[0.2, 0.5, 0.6, 0.6] WHERE id = 20;
    
  4. AccountTransferAccount 输入表格添加两个新列:notesnotes_embeddings

    ALTER TABLE AccountTransferAccount
    ADD COLUMN notes STRING(MAX);
    ALTER TABLE AccountTransferAccount
    ADD COLUMN notes_embeddings ARRAY<FLOAT32>(vector_length=>4);
    
  5. notes 列中的文本创建嵌入,并将其填充到 notes_embeddings 列中。

    如需为 Spanner Graph 中的运营数据生成 Vertex AI 嵌入,请参阅获取 Vertex AI 文本嵌入

    为了说明问题,我们的示例使用了人工的低维向量值。

    UPDATE AccountTransferAccount
    SET notes = "for shared cost of dinner",
      notes_embeddings = ARRAY<FLOAT32>[0.3, 0.5, 0.8, 0.7]
    WHERE id = 16 AND to_id = 20;
    UPDATE AccountTransferAccount
    SET notes = "fees for tuition",
      notes_embeddings = ARRAY<FLOAT32>[0.1, 0.9, 0.1, 0.7]
    WHERE id = 20 AND to_id = 7;
    UPDATE AccountTransferAccount
    SET notes = 'loved the lunch',
      notes_embeddings = ARRAY<FLOAT32>[0.4, 0.5, 0.7, 0.9]
    WHERE id = 20 AND to_id = 16;
    
  6. AccountAccountTransferAccount 输入表添加新列后,使用以下语句更新属性图定义。如需了解详情,请参阅更新现有节点或边定义

    CREATE OR REPLACE PROPERTY GRAPH FinGraph
    NODE TABLES (Account, Person)
    EDGE TABLES (
      PersonOwnAccount
        SOURCE KEY (id) REFERENCES Person (id)
        DESTINATION KEY (account_id) REFERENCES Account (id)
        LABEL Owns,
      AccountTransferAccount
        SOURCE KEY (id) REFERENCES Account (id)
        DESTINATION KEY (to_id) REFERENCES Account (id)
        LABEL Transfers
    );
    

查找 K 个最近邻

在以下示例中,使用 EUCLIDEAN_DISTANCE() 函数对图数据库中的节点和边执行 KNN 向量搜索。

对图节点执行 KNN 向量搜索

您可以对 Account 节点的 nick_name_embeddings 属性执行 KNN 向量搜索。此 KNN 向量搜索会返回账号所有者的 name 和账号的 nick_name。在以下示例中,结果显示了休闲旅行和度假的账号(由 [0.2, 0.4, 0.9, 0.6] 向量嵌入表示)的前两个 K 个最近邻。

GRAPH FinGraph
MATCH (p:Person)-[:Owns]->(a:Account)
RETURN p.name, a.nick_name
ORDER BY EUCLIDEAN_DISTANCE(a.nick_name_embeddings,
  -- An illustrative embedding for 'accounts for leisure travel and vacation'
  ARRAY<FLOAT32>[0.2, 0.4, 0.9, 0.6])
LIMIT 2;

结果

name nick_name
Alex 为热带度假储备资金
Dana 存钱准备旅行

对图边执行 KNN 向量搜索

您可以对 Owns 边的 notes_embeddings 属性执行 KNN 向量搜索。此 KNN 向量搜索会返回账号所有者的 name 和转移的 notes。在以下示例中,结果显示了餐费(由 [0.2, 0.4, 0.9, 0.6] 向量嵌入表示)的前两个 K 个最近邻。

GRAPH FinGraph
MATCH (p:Person)-[:Owns]->(:Account)-[t:Transfers]->(:Account)
WHERE t.notes_embeddings IS NOT NULL
RETURN p.name, t.notes
ORDER BY EUCLIDEAN_DISTANCE(t.notes_embeddings,
  -- An illustrative vector embedding for 'food expenses'
  ARRAY<FLOAT32>[0.2, 0.4, 0.9, 0.6])
LIMIT 2;

结果

name 注释
Lee 用于分摊晚餐费用
Dana 非常喜欢午餐

创建向量索引并查找近似最近邻

如需执行 ANN 搜索,您必须创建一个专用矢量索引,供 Spanner Graph 用于加速矢量搜索。向量索引必须使用特定的距离衡量标准。您可以将 distance_type 参数设置为 COSINEDOT_PRODUCTEUCLIDEAN 之一,从而选择最适合您的用例的距离衡量标准。如需了解详情,请参阅 VECTOR INDEX 语句

在以下示例中,您将使用欧几里得距离类型在 Account 输入表的 nick_name_embedding 列上创建向量索引:

CREATE VECTOR INDEX NickNameEmbeddingIndex
ON Account(nick_name_embeddings)
WHERE nick_name_embeddings IS NOT NULL
OPTIONS (distance_type = 'EUCLIDEAN', tree_depth = 2, num_leaves = 1000);

对图节点执行 ANN 向量搜索

创建向量索引后,您可以对 Account 节点的 nick_name 属性执行 ANN 向量搜索。ANN 矢量搜索会返回账号所有者的 name 和账号的 nick_name。在以下示例中,结果显示了休闲旅行和度假的账号(由 [0.2, 0.4, 0.9, 0.6] 向量嵌入表示)的前两个近似最近邻。

图表提示会强制查询优化器在查询执行计划中使用指定的矢量索引。

GRAPH FinGraph
MATCH (@{FORCE_INDEX=NickNameEmbeddingIndex} a:Account)
WHERE a.nick_name_embeddings IS NOT NULL
RETURN a, APPROX_EUCLIDEAN_DISTANCE(a.nick_name_embeddings,
  -- An illustrative embedding for 'accounts for leisure travel and vacation'
  ARRAY<FLOAT32>[0.2, 0.4, 0.9, 0.6],
  options => JSON '{"num_leaves_to_search": 10}') AS distance
ORDER BY distance
LIMIT 2

NEXT

MATCH (p:Person)-[:Owns]->(a)
RETURN p.name, a.nick_name;

结果

name nick_name
Alex 为热带度假储备资金
Dana 存钱准备旅行

后续步骤