本页介绍了如何在 Spanner 图中使用图路径。
在图数据库中,图路径数据类型表示与边交错的节点序列,并显示这些节点和边之间的关系。如需详细了解路径数据类型,请参阅图表路径类型。
借助 Spanner Graph Language (GQL),您可以构建图路径并对其执行查询。本文档中的示例使用与设置和查询 Spanner Graph 页面上所示的 Spanner Graph 架构相同的架构。
构建图表路径
您可以通过在图表模式中创建路径变量或使用 PATH
函数来构建图表路径。
我们建议使用路径变量构建图表路径。创建路径变量的格式为:
MATCH p = PATH_PATTERN
如需了解详情,请参阅图表模式。
示例
在以下示例中,查询会查找 FinGraph
中账号之间的转账模式。
GRAPH FinGraph
MATCH p = (src:Account {id: 16})-[t1:Transfers]->(mid:Account)-[t2:Transfers]->
(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
边的数量,该数量可能介于 src
和 mid
之间或介于 mid
和 dst
之间。
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)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(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)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
RETURN IS_ACYCLIC(p) AS is_acyclic_path, src.id AS source_account_id,
mid.id AS mid_account_id, dst.id AS dst_account_id;
结果
is_acyclic_path | source_account_id | mid_account_id | dst_account_id |
---|---|---|---|
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)-[t1:Transfers]->(mid1:Account)-[t2:Transfers]->
(mid2:Account)-[t3:Transfers]->(dst:Account)
WHERE src.id < dst.id
RETURN IS_TRAIL(p) AS is_trail_path, t1.id AS t1_id, t2.id AS t2_id, t3.id AS t3_id;
结果
is_trail_path | t1_id | t2_id | t3_id |
---|---|---|---|
FALSE | 16 | 20 | 16 |
TRUE | 7 | 16 | 20 |
TRUE | 7 | 16 | 20 |
路径模式
在 Spanner Graph 中,默认行为是返回所有路径,包括包含重复节点和边的路径。您可以使用以下路径模式来包含或排除包含重复节点和边的路径。如需了解详细语义,请参阅 Pathmode 文档。
WALK
WALK
路径模式会返回所有路径,包括包含重复节点和边的路径。WALK
是默认的路径模式。
示例
以下查询演示了如何对非量化路径模式使用 WALK
路径模式。结果中的第一个路径使用了 t1
和 t3
的相同边。
GRAPH FinGraph
MATCH p = WALK (src:Account)-[t1:Transfers]->(mid1:Account)-[t2:Transfers]->
(mid2:Account)-[t3:Transfers]->(dst:Account)
WHERE src.id < dst.id
RETURN t1.id AS transfer1_id, t2.id AS transfer2_id, t3.id AS transfer3_id;
结果
transfer1_id | transfer2_id | transfer3_id |
---|---|---|
16 | 20 | 16 |
7 | 16 | 20 |
7 | 16 | 20 |
ACYCLIC
ACYCLIC
路径模式会滤除包含重复节点的路径。
示例
以下查询演示了如何对非量化路径模式使用 ACYCLIC
路径模式。系统会滤除具有相同 src
和 dst
节点的路径。
GRAPH FinGraph
MATCH p = ACYCLIC (src:Account)-[t1:Transfers]->(mid:Account)-[t2:Transfers]->(dst:Account)
RETURN src.id AS account1_id, mid.id AS account2_id, dst.id AS account3_id;
结果
account1_id | account2_id | account3_id |
---|---|---|
20 | 7 | 16 |
20 | 7 | 16 |
7 | 16 | 20 |
7 | 16 | 20 |
16 | 20 | 7 |
TRAIL
TRAIL
路径模式会滤除具有重复边的路径。
示例
以下查询演示了如何对非量化路径模式使用 TRAIL
路径模式。系统会滤除 t1
边和 t3
边相等的路径。
GRAPH FinGraph
MATCH p = TRAIL (src:Account)-[t1:Transfers]->(mid1:Account)-[t2:Transfers]->
(mid2:Account)-[t3:Transfers]->(dst:Account)
RETURN
t1.id AS transfer1_id, t2.id AS transfer2_id, t3.id AS transfer3_id;
结果
transfer1_id | transfer2_id | transfer3_id |
---|---|---|
16 | 20 | 7 |
16 | 20 | 7 |
20 | 7 | 16 |
20 | 7 | 16 |
7 | 16 | 20 |
7 | 16 | 20 |
7 | 16 | 20 |
7 | 16 | 20 |
20 | 16 | 20 |
路径搜索前缀
您可以使用路径搜索前缀来限制路径模式,以返回每个数据分区中的最短路径。如需了解详细语义,请参阅路径搜索前缀。
ANY SHORTEST
ANY SHORTEST
路径搜索前缀会返回与每个数据分区中的模式匹配的最短路径(边数最少的路径)。如果每个分区有多个最短路径,则返回其中任意一条。
示例
以下查询会匹配每对 [a, b]
之间的任何路径。
GRAPH FinGraph
MATCH p = ANY SHORTEST (a:Account)-[t:Transfers]->{1,4}(b:Account)
WHERE a.is_blocked
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
的一个或三个账号进行转送。
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 |