- 需求原因 / 使用场景
树查询 - 需求描述
根据边的条件,从开始节点不断递归遍历出整个树结构
数据集:
数据集中的节点TAG为:
"CREATE TAG node
(
name
string NULL,
p
int64 NULL
)
对name建了索引
边为:
"CREATE EDGE `link` (
`v` double NULL
)
对v建了索引
整个数据集为n棵树.他们的节点分别为 {name:“N-0”,p:随机数},{name:“N-1”,p:随机数},{name:“N-2”,p:随机数},......(个数随机)
{name:“N-0”,p:随机数}的子节点为 {name:“N-0-0”,p:随机数},{name:“N-0-1”,p:随机数},{name:“N-0-2”,p:随机数},......(个数随机)
依次类推,所有的节点都这样繁衍生息.到了一定程度随机灭绝,不再有子节点.
目前数据集中最深的树为 12 层.
从其中一个叶子节点往回查
第1个查询:
> MATCH p=(v1)-[e:link*0..15]->(v2:node{name:"N-0-0-0-0-0-0-0-0-0-0-1-0"}) RETURN v1
> ... ...
> Got 12 rows (time spent 11202/18881 us)
耗时很令人喜悦
从根节点往下查到某个具体的叶子节点
第2个查询:
> MATCH p=(:node{name: "N-0"})-[e1:link{v:11}]->()-[e2:link{v:12}]->()-[e3:link{v:8}]->()-[e4:link{v:4}]->()-[e5:link{v:13}]->()-[e6:link{v:4}]->()-[e7:link{v:9}]->()-[e8:link{v:7}]->()-[e9:link{v:2}]->()-[e10:link{v:8}]->()-[e11:link{v:11}]->() WITH nodes(p) AS n UNWIND n AS r RETURN r;
> ... ...
> Got 12 rows (time spent 25934/31933 us)
耗时慢人点,但能接受.
做个粗暴查询,从根节点开始,往下遍历所有子节点.
第3个查询
> MATCH p=(v1:node{name:"N-0"})-[e*..12]->(v2) RETURN v2;
> ......
> Got 696387 rows (time spent 59440143/70847983 us)
这个耗时这么久可以理解,毕竟遍历这么多节点.数量摆在这里.
但在业务中,我可能遍历的和上面 [第2个查询]有点类似,是要对边做一些筛选.比如限定边属性 v <=1.
第4个查询
> MATCH p=(v1:node{name:"N-0"})-[e*..12]->(v2) WHERE e.v <= 1 RETURN p;
> +---+
> | p |
> +---+
> +---+
> Empty set (time spent 339762371/339679276 us)
惊讶,查询出结果为空.为什么要这么慢.从根节点开始.第一跳就没后续了,就可以结束循环.难道和go语句一样.要把指定跳数内所有线路都遍历一下吗?不管前面的步骤能不能持续下去.
这耗时339762371 远远比没指定过滤条件的第3个查询的耗时 59440143 大多了.这多出来的这么多时间是干什么去了.这是我的第一个疑问
再做个查询确定下和go是不是一样的[遍历到前面的步骤如果不满足条件,还会继续遍历后面的步骤,不会提前退出循环.]
从 第2个查询 可以知道,边上有个v属性值为2.算是最小的属性值.那就换下条件再筛选
第5个查询
> MATCH p=(v1:node{name:"N-0"})-[e*..12]->(v2) WHERE e.v <= 2 RETURN p;
> +---+
> | p |
> +---+
> +---+
> Empty set (time spent 303019113/302944554 us)
显然,也是没结果.后续即使有满足条件的也不会拾取(幸亏是这样,这是我想要的).耗时上和第4个查询倒是不差上下. 所有我的第二个疑问来了,为什么不提前结束循环
另外,再补一个疑问.
从studio的节点扩展的功能里可以查看到相应的ngql语句.
它为什么要这样查询.直觉上,他是从整个数据集里筛选出满足条件的所有的边.难道以节点上的边为基数来过滤不香吗,数据量上可能要少很多.
我也拿他的这个试运行了下
第6个查询
MATCH p=(v1:node{name:"N-0"})-[e*..12]->(v2) WHERE ALL(l IN e WHERE l.v <= 1) RETURN p;
+---+
| p |
+---+
+---+
Empty set (time spent 337851985/337768806 us)
耗时上,和 第4个查询 差不多.所以对他的查询模式有个推测"为什么没有提前终止,他是要把筛选出来满足条件的所有数据都遍历一遍,不管模式上是不是匹配"
也因此对 第4个查询 耗时339762371 远远比没指定WHERE过滤条件的第3个查询 的耗时 59440143 大那么多.有一个推测"有where条件的边数很多,没筛选条件的,是从根节点遍历完所有的子节点,不管模式会不会匹配都继续下去,直到没有后续子节点"
所有:
这几个疑问我觉得可能是官方ngql查询可以优化的方面.不然长路径查询起来很慢.
另外也请都下各位大佬,这样的查询场景,目前有没有比较优化的ngql写法.