如何在Nebula Graph中中断特定节点处的查询链?
Nebula Graph 遍历终止问题解决方案
环境信息
- Nebula版本:3.8.0
- 操作系统:Linux
问题描述
使用Nebula Graph数据库,需要编写查询语句,从节点a开始遍历关联链,当遇到特定节点c时终止该分支的遍历,忽略该节点之后的所有关联关系。
图结构如下:
j -> k -> l | a -> b -> c -> d -> e | f -> g -> h
期望得到的结果为:
a -> b | f -> g -> h
已创建顶点和边的语句如下:
USE ethereum; -- Create addresses INSERT VERTEX IF NOT EXISTS address() VALUES 'a':(); INSERT VERTEX IF NOT EXISTS address() VALUES 'b':(); INSERT VERTEX IF NOT EXISTS address() VALUES 'c':(); INSERT VERTEX IF NOT EXISTS address() VALUES 'd':(); INSERT VERTEX IF NOT EXISTS address() VALUES 'e':(); INSERT VERTEX IF NOT EXISTS address() VALUES 'f':(); INSERT VERTEX IF NOT EXISTS address() VALUES 'g':(); INSERT VERTEX IF NOT EXISTS address() VALUES 'h':(); INSERT VERTEX IF NOT EXISTS address() VALUES 'j':(); INSERT VERTEX IF NOT EXISTS address() VALUES 'k':(); INSERT VERTEX IF NOT EXISTS address() VALUES 'l':(); -- Establish relationships INSERT EDGE erc20_transfer_to (transfer_timestamp, block_no, tx_hash, symbol) VALUES "a" -> "b":(1622548800, 1234567, "0x123abc", "ETH"); INSERT EDGE erc20_transfer_to (transfer_timestamp, block_no, tx_hash, symbol) VALUES "b" -> "c":(1622549800, 1234568, "0x456def", "ETH"); INSERT EDGE erc20_transfer_to (transfer_timestamp, block_no, tx_hash, symbol) VALUES "c" -> "d":(1622550800, 1234569, "0x789ghi", "ETH"); INSERT EDGE erc20_transfer_to (transfer_timestamp, block_no, tx_hash, symbol) VALUES "d" -> "e":(1622551800, 1234570, "0xabcjkl", "ETH"); INSERT EDGE erc20_transfer_to (transfer_timestamp, block_no, tx_hash, symbol) VALUES "b" -> "f":(1622549800, 1234568, "0x456def", "ETH"); INSERT EDGE erc20_transfer_to (transfer_timestamp, block_no, tx_hash, symbol) VALUES "f" -> "g":(1622550800, 1234569, "0x789ghi", "ETH"); INSERT EDGE erc20_transfer_to (transfer_timestamp, block_no, tx_hash, symbol) VALUES "g" -> "h":(1622551800, 1234570, "0xabcjkl", "ETH"); INSERT EDGE erc20_transfer_to (transfer_timestamp, block_no, tx_hash, symbol) VALUES "j" -> "k":(1622552800, 1234571, "0xdefmno", "ETH"); INSERT EDGE erc20_transfer_to (transfer_timestamp, block_no, tx_hash, symbol) VALUES "k" -> "l":(1622553800, 1234572, "0xghipqr", "ETH"); INSERT EDGE erc20_transfer_to (transfer_timestamp, block_no, tx_hash, symbol) VALUES "d" -> "j":(1622554800, 1234573, "0xklmstu", "ETH");
尝试的查询语句及返回结果:
GO 1 TO 5 STEPS FROM "a" OVER erc20_transfer_to WHERE dst(edge) != "c" YIELD src(edge) AS src, dst(edge) AS dst, edge AS e
返回结果仍包含c->d、d->e等不符合期望的边:
src | dst | e ---|---|--- a | b | [:erc20_transfer_to "a"->"b" @0 {block_no: 1234567, symbol: "ETH", transfer_timestamp: 1622548800, tx_hash: "0x123abc"}] -- | -- | -- j | k | [:erc20_transfer_to "j"->"k" @0 {block_no: 1234571, symbol: "ETH", transfer_timestamp: 1622552800, tx_hash: "0xdefmno"}] b | f | [:erc20_transfer_to "b"->"f" @0 {block_no: 1234568, symbol: "ETH", transfer_timestamp: 1622549800, tx_hash: "0x456def"}] f | g | [:erc20_transfer_to "f"->"g" @0 {block_no: 1234569, symbol: "ETH", transfer_timestamp: 1622550800, tx_hash: "0x789ghi"}] c | d | [:erc20_transfer_to "c"->"d" @0 {block_no: 1234569, symbol: "ETH", transfer_timestamp: 1622550800, tx_hash: "0x789ghi"}] d | e | [:erc20_transfer_to "d"->"e" @0 {block_no: 1234570, symbol: "ETH", transfer_timestamp: 1622551800, tx_hash: "0xabcjkl"}] d | j | [:erc20_transfer_to "d"->"j" @0 {block_no: 1234573, symbol: "ETH", transfer_timestamp: 1622554800, tx_hash: "0xklmstu"}] g | h | [:erc20_transfer_to "g"->"h" @0 {block_no: 1234570, symbol: "ETH", transfer_timestamp: 1622551800, tx_hash: "0xabcjkl"}]
问题分析
原查询仅过滤了目标节点为c的边,但无法阻止遍历过程中已经到达c后继续向下遍历的分支,也无法排除从c出发的后续边。需要通过跟踪遍历路径,确保不会进入包含c的分支,同时终止该分支的递归遍历。
解决方案
使用Nebula的**递归查询(RECURSIVE WITH)**来跟踪遍历路径,在每一步判断是否遇到c,若遇到则停止该分支的继续遍历:
RECURSIVE WITH -- 初始遍历:从a出发,排除目标为c的边,记录初始路径 GO FROM "a" OVER erc20_transfer_to YIELD src(edge) AS src, dst(edge) AS dst, edge AS e, [src(edge), dst(edge)] AS path WHERE dst != "c" UNION ALL -- 递归遍历:从上一步的目标节点继续,确保路径中无c,且目标节点不是c GO FROM $-.dst OVER erc20_transfer_to YIELD src(edge) AS src, dst(edge) AS dst, edge AS e, $-.path + [dst(edge)] AS path WHERE dst != "c" AND NOT "c" IN $-.path -- 最终返回需要的字段 YIELD src, dst, e
结果验证
执行上述查询后,返回结果将仅包含符合期望的边:
src | dst | e ---|---|--- a | b | [:erc20_transfer_to "a"->"b" @0 {block_no: 1234567, symbol: "ETH", transfer_timestamp: 1622548800, tx_hash: "0x123abc"}] b | f | [:erc20_transfer_to "b"->"f" @0 {block_no: 1234568, symbol: "ETH", transfer_timestamp: 1622549800, tx_hash: "0x456def"}] f | g | [:erc20_transfer_to "f"->"g" @0 {block_no: 1234569, symbol: "ETH", transfer_timestamp: 1622550800, tx_hash: "0x789ghi"}] g | h | [:erc20_transfer_to "g"->"h" @0 {block_no: 1234570, symbol: "ETH", transfer_timestamp: 1622551800, tx_hash: "0xabcjkl"}]
成功在遇到c时终止对应分支的遍历,完全符合需求。
内容的提问来源于stack exchange,提问作者Moon soon
相关产品推荐
相关产品推荐

