使用路径

本页面介绍了如何在 Spanner Graph 中处理图路径。

在图数据库中,图路径数据类型表示与边交织的节点序列,并显示这些节点和边的关系。如需详细了解路径数据类型,请参阅图路径类型

借助 Spanner Graph Language (GQL),您可以构建图表路径并对其执行查询。本文档中的示例使用与设置和查询 Spanner Graph 页面上相同的 Spanner Graph 架构。

构建图表路径

您可以在图模式中创建路径变量,也可以使用 PATH 函数来构建图路径。

我们建议使用路径变量来构建图表路径。创建路径变量的格式如下:

MATCH p = PATH_PATTERN

如需了解详情,请参阅图表模式

示例

在以下示例中,查询会查找 FinGraph 内的账号之间资金转移模式。

GRAPH FinGraph
MATCH p = (src:Account {id: 16})-[t:Transfers]->{2}(dst:Account {id: 7})
RETURN TO_JSON(p) AS full_path;

结果

full_path
[{"identifier": ..., "properties": {"id": 16, ...}, ...}, {"identifier": ..., "properties": {"amount": 300.0, ...}, ...}, ...]

结果表明,查询在数据库中找到了 Account -> Transfers -> Account 模式。

查询图表路径

您可以使用以下特定于路径的函数来查询图路径。如需详细了解 Spanner Graph 查询,请参阅查询概览

EDGES

EDGES 函数会返回图路径中的所有边。如需了解详细语义,请参阅 EDGES

示例

此查询用于查找两个账号之间经过中间账号的路径。 它会返回路径中第二个 Transfers 边的量,该边可能位于 srcmid 之间,也可能位于 middst 之间。

GRAPH FinGraph
MATCH p = (src:Account {id: 7})-[t1:Transfers]->{1,3}(mid:Account)-[t2:Transfers]->
  {1,3}(dst:Account {id: 16})
LET second_edge = EDGES(p)[1]
RETURN DISTINCT src.id AS src, dst.id AS dst, second_edge.amount AS second_edge_amount;

结果

src dst second_edge_amount
7 16 300

NODES

NODES 函数会返回图路径中的所有节点。如需了解详细语义,请参阅 NODES

示例

此查询会查找两次转账的图路径,然后返回表示该路径的 JSON 列表。

GRAPH FinGraph
MATCH p = (src:Account)-[t:Transfers]->{2}(dst:Account)
RETURN TO_JSON(NODES(p)) AS nodes;

结果

节点
[{"identifier": "...", "properties": {"id": 16}, ...}, {"identifier": "...", "properties": {"id": 20, ...}, ...]

PATH_FIRST

PATH_FIRST 函数用于查找图路径中的第一个节点。如需了解详细语义,请参阅 PATH_FIRST

示例

此查询用于查找包含两次转移的图路径中的第一个节点。它会返回 Account 节点的标签和账号的昵称。

GRAPH FinGraph
MATCH p = -[:Transfers]->{1,3}(dst:Account{id: 7})
RETURN DISTINCT PATH_FIRST(p).id AS can_reach_target;

结果

can_reach_target
7
16
20

PATH_LAST

PATH_LAST 函数用于查找图路径中的最后一个节点。如需了解详细语义,请参阅 PATH_LAST

示例

此查询用于查找包含两次转移的图路径中的最后一个节点。它会返回 Account 节点的标签和账号的昵称。

GRAPH FinGraph
MATCH p =(start:Account{id: 7})-[:Transfers]->{1,3}
RETURN DISTINCT PATH_LAST(p).id as can_reach_target;

结果

can_reach_target
7
16
20

PATH_LENGTH

PATH_LENGTH 函数用于查找图路径中的边数。如需了解详细语义,请参阅 PATH_LENGTH

示例

此查询用于查找包含 1 到 3 次换乘的图路径中的边数。

GRAPH FinGraph
MATCH p = (src:Account)-[e:Transfers]->{1,3}(dst:Account)
RETURN PATH_LENGTH(p) AS num_transfers, COUNT(*) AS num_paths;

结果

num_transfers num_paths
1 5
2 7
3 11

IS_ACYCLIC

IS_ACYCLIC 函数用于检查图路径是否包含重复节点。如果找到重复项,则返回 TRUE;否则返回 FALSE。如需了解详细语义,请参阅 IS_ACYCLIC

示例

此查询用于检查相应图路径是否包含重复节点。

GRAPH FinGraph
MATCH p = (src:Account)-[t:Transfers]->{2}(dst:Account)
RETURN IS_ACYCLIC(p) AS is_acyclic_path,
       ARRAY_TRANSFORM(NODES(p), n->n.id) AS account_ids;

结果

is_acyclic_path account_ids
TRUE 16,20,7
TRUE 20,7,16
TRUE 20,7,16
FALSE 16,20,16
TRUE 7,16,20
TRUE 7,16,20
FALSE 20,16,20

IS_TRAIL

IS_TRAIL 函数用于检查图路径是否具有重复的边。如果找到重复项,则返回 TRUE;否则返回 FALSE。如需了解详细语义,请参阅 IS_TRAIL

示例

此查询用于检查相应图路径是否具有重复的边。

GRAPH FinGraph
MATCH p = (src:Account)-[t:Transfers]->{3}(dst:Account)
WHERE src.id < dst.id
RETURN IS_TRAIL(p) AS is_trail_path,
       ARRAY_TRANSFORM(t, t->t.id) AS transfer_ids

结果

is_trail_path transfer_ids
FALSE 16,20,16
TRUE 7,16,20
TRUE 7,16,20

路径模式

在 Spanner Graph 中,默认行为是返回所有路径,包括具有重复节点和边的路径。您可以使用以下路径模式来包含或排除具有重复节点和边的路径。如需了解详细的语义,请参阅路径模式文档。

WALK

WALK 路径模式会返回所有路径,包括包含重复节点和边的路径。WALK 是默认的路径模式。

示例

以下查询演示了如何在量化路径模式中使用 WALK 路径模式。 结果中的第一个路径具有重复的边。

GRAPH FinGraph
MATCH p = WALK (src:Account)-[t:Transfers]->{3}(dst:Account)
WHERE src.id < dst.id
RETURN ARRAY_TRANSFORM(t, t->t.id) AS transfer_ids

结果

transfer_ids
16,20,16
7,16,20
7,16,20

ACYCLIC

ACYCLIC 路径模式会过滤掉具有重复节点的路径。

示例

以下查询演示了如何在量化路径模式中使用 ACYCLIC 路径模式。 具有相同 srcdst 节点的路径会被过滤掉。

GRAPH FinGraph
MATCH p = ACYCLIC (src:Account)-[t:Transfers]->{2}(dst:Account)
RETURN ARRAY_TRANSFORM(NODES(p), n->n.id) AS account_ids

结果

account_ids
16,20,7
20,7,16
20,7,16
7,16,20
7,16,20

TRAIL

TRAIL 路径模式会过滤掉具有重复边的路径。

示例

以下查询演示了如何在量化路径模式中使用 TRAIL 路径模式。 系统会过滤掉具有重复边的路径。

GRAPH FinGraph
MATCH p = TRAIL (src:Account)-[t:Transfers]->{3}(dst:Account)
WHERE src.id < dst.id
RETURN ARRAY_TRANSFORM(t, t->t.id) AS transfer_ids

结果

transfer_ids
7,16,20
7,16,20

路径搜索前缀

您可以使用路径搜索前缀来限制路径模式,以返回每个数据分区的最短路径。如需了解详细语义,请参阅路径搜索前缀

ANY SHORTEST

ANY SHORTEST 路径搜索前缀会返回与每个数据分区中的模式匹配的最短路径(边数最少的路径)。如果每个分区有多个最短路径,则返回其中任意一个。

示例

以下查询会匹配每对 [a, b] 之间的任何路径。

GRAPH FinGraph
MATCH p = ANY SHORTEST (a:Account {is_blocked:true})-[t:Transfers]->{1,4}(b:Account)
LET total_amount = SUM(t.amount)
RETURN a.id AS account1_id, total_amount, b.id AS account2_id;

结果

account1_id total_amount account2_id
16 500 16
16 800 7
16 300 20

转换规则

如需了解详情,请参阅 GRAPH_PATH 转换规则

应用场景示例

在以下用例示例中,您将找到从账号 ID 20 开始,通过 1 到 3 个账号进行路由的所有账号。

GRAPH FinGraph
MATCH p = (start:Account {id: 20})-[:Transfers]->{1,3}(dst:Account)
RETURN DISTINCT dst.id AS dst;

结果

dst
7
16
20

不过,返回账号 ID 20 的查询可能过于宽泛,因为它以账号 ID 20 开头。如需显示更具体的结果,您可以强制查询仅显示不含任何重复节点的无环图路径。为此,您可以:

  • 使用 MATCH p = ACYCLIC <path_pattern>;或者
  • 在查询中应用 IS_ACYCLIC(p) 过滤条件

以下查询使用 MATCH p = ACYCLIC PATH_PATTERN

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->{1,3}(dst:Account)
RETURN DISTINCT dst.id AS dst;

结果

dst
7
16

如果您想知道资金首次转移所经过的账号,可以运行以下查询:

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})(-[:Transfers]->
  (nexts:Account)){1,3}(dst:Account)
RETURN dst.id AS dst, ARRAY_AGG(DISTINCT nexts[0].id) AS unique_starts;

此查询之所以非常规,是因为它在量化路径内引入了一个新变量,并使用 nexts 来获取结果。使用路径变量,您可以简化查询:

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->{1,3}(dst:Account)
RETURN dst.id AS dst, ARRAY_AGG(DISTINCT NODES(p)[OFFSET(1)].id) AS unique_starts;

使用 NODES(p) 会返回路径中的所有节点。由于第一个节点账号指定为 start,因此下一个节点账号(位于第一个偏移量)是资金转移所经过的第一个账号。

结果

dst unique_starts
7 16, 7

当存在多个量化路径时,路径会更有用。您可以添加一项限制,规定从 start 找到的路径必须经过账号 ID 7

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->
  {1,3}(mid:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
RETURN dst.id AS dst,
  ARRAY_AGG(DISTINCT NODES(p)[OFFSET(1)].id) AS unique_starts;

虽然 MATCH 语句发生了变化,但查询的其余部分无需更改。如果不使用路径变量,在某些情况下,Spanner 无法静态地知道要检查哪个量化路径。

使用路径变量,您可以获取所有转移的总和:

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->
  {1,3}(mid:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
LET all_transfers = EDGES(p)
LET transfer_amounts = SUM(all_transfers.amount)
RETURN dst.id AS dst,
  ARRAY_AGG(DISTINCT NODES(p)[OFFSET(1)].id) AS participating_neighbor_nodes, transfer_amounts;

结果

dst participating_neighbor_nodes transfer_amounts
16 7 600
16 7 800

后续步骤