小练习:等价的多种表达的代价
这是一个用户提的一个问题:如何表达两点之间是否存在直连边?
对于这么简单的表达,大家应该直接能想到应该有好多方法表达:
- GO
- FIND PATH
- MATCH
- FETCH PROP
这里,利用 FETCH PROP
表达双向边,似乎是两个查询,我们先省略掉,对于剩下的三个表达,哪一种更适合呢?我们先写出来看看
GO FROM "player100" over * BIDIRECT WHERE id($$) == "player101" YIELD edge AS e
FIND ALL PATH FROM "player100" TO "player101" OVER * BIDIRECT UPTO 1 STEPS YIELD path AS p
MATCH (n)--(m) WHERE id(n) == "player100" AND id(m) == "player101" RETURN count(*)
GO/MATCH 与 FIND PATH
它们都能获得我们想要的信息,感觉上,MATCH
和 GO
会比 FIND PATH
的查询更快一些,因为它们都是从单一起点拓展,而 FIND PATH
是从两端同时查询,在一跳的情况下,这其实是冗余的动作。验证一下的话,我们可以看到
(root@nebula) [basketballplayer]> explain FIND ALL PATH FROM "player100" TO "player101" OVER * BIDIRECT UPTO 1 STEPS YIELD path AS p
Execution succeeded (time spent 818/16537 us)
Execution Plan (optimize time 229 us)
-----+-----------------+--------------+----------------+------------------------------------
| id | name | dependencies | profiling data | operator info |
-----+-----------------+--------------+----------------+------------------------------------
| 9 | DataCollect | 8 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "p" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__DataCollect_9" |
| | | | | } |
| | | | | inputVar: [ |
| | | | | { |
| | | | | "colNames": [ |
| | | | | "_path" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__ProduceAllPaths_5" |
| | | | | } |
| | | | | ] |
| | | | | distinct: false |
| | | | | kind: ALL PATHS |
-----+-----------------+--------------+----------------+------------------------------------
| 8 | Loop | 7 | | outputVar: { |
| | | | | "colNames": [], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Loop_8" |
| | | | | } |
| | | | | inputVar: __VAR_1 |
| | | | | condition: (++($__VAR_2)<=1) |
| | | | | loopBody: 5 |
-----+-----------------+--------------+----------------+------------------------------------
| 5 | ProduceAllPaths | 3,4 | | branch: true, nodeId: 8 |
| | | | | |
| | | | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "_path" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__ProduceAllPaths_5" |
| | | | | } |
| | | | | inputVar: { |
| | | | | "rightVar": "__GetNeighbors_4", |
| | | | | "leftVar": "__GetNeighbors_3" |
| | | | | } |
| | | | | LeftNextVidVar: "__VAR_0" |
| | | | | RightNextVidVar: "__VAR_1" |
| | | | | noloop : false |
| | | | | steps: 1 |
-----+-----------------+--------------+----------------+------------------------------------
| 3 | GetNeighbors | 2 | | outputVar: { |
| | | | | "colNames": [], |
| | | | | "type": "DATASET", |
| | | | | "name": "__GetNeighbors_3" |
| | | | | } |
| | | | | inputVar: __VAR_0 |
| | | | | space: 49 |
| | | | | dedup: true |
| | | | | limit: -1 |
| | | | | filter: |
| | | | | orderBy: [] |
| | | | | src: COLUMN[0] |
| | | | | edgeTypes: [] |
| | | | | edgeDirection: OUT_EDGE |
| | | | | vertexProps: |
| | | | | edgeProps: [ |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -52 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -53 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -67 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -68 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -69 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -70 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -71 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 52 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 53 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 67 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 68 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 69 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 70 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 71 |
| | | | | } |
| | | | | ] |
| | | | | statProps: |
| | | | | exprs: |
| | | | | random: false |
-----+-----------------+--------------+----------------+------------------------------------
| 2 | PassThrough | 1 | | outputVar: { |
| | | | | "colNames": [], |
| | | | | "type": "DATASET", |
| | | | | "name": "__PassThrough_2" |
| | | | | } |
| | | | | inputVar: __Start_1 |
-----+-----------------+--------------+----------------+------------------------------------
| 1 | Start | | | outputVar: { |
| | | | | "colNames": [], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Start_1" |
| | | | | } |
-----+-----------------+--------------+----------------+------------------------------------
| 4 | GetNeighbors | 2 | | outputVar: { |
| | | | | "colNames": [], |
| | | | | "type": "DATASET", |
| | | | | "name": "__GetNeighbors_4" |
| | | | | } |
| | | | | inputVar: __VAR_1 |
| | | | | space: 49 |
| | | | | dedup: true |
| | | | | limit: -1 |
| | | | | filter: |
| | | | | orderBy: [] |
| | | | | src: COLUMN[0] |
| | | | | edgeTypes: [] |
| | | | | edgeDirection: OUT_EDGE |
| | | | | vertexProps: |
| | | | | edgeProps: [ |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 52 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 53 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 67 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 68 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 69 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 70 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": 71 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -52 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -53 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -67 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -68 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -69 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -70 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_type", |
| | | | | "_rank" |
| | | | | ], |
| | | | | "type": -71 |
| | | | | } |
| | | | | ] |
| | | | | statProps: |
| | | | | exprs: |
| | | | | random: false |
-----+-----------------+--------------+----------------+------------------------------------
| 7 | Project | 6 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "_vid" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__VAR_1" |
| | | | | } |
| | | | | inputVar: __VAR_1 |
| | | | | columns: [ |
| | | | | "COLUMN[0] AS _vid" |
| | | | | ] |
-----+-----------------+--------------+----------------+------------------------------------
| 6 | Project | 0 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "_vid" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__VAR_0" |
| | | | | } |
| | | | | inputVar: __VAR_0 |
| | | | | columns: [ |
| | | | | "COLUMN[0] AS _vid" |
| | | | | ] |
-----+-----------------+--------------+----------------+------------------------------------
| 0 | Start | | | outputVar: { |
| | | | | "colNames": [], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Start_0" |
| | | | | } |
-----+-----------------+--------------+----------------+------------------------------------
Tue, 13 Sep 2022 15:05:36 CST
FIND PATH
中果然有两个 GetNeighbors
算子。
GO 与 MATCH
一般来说,如果表达能力等价,GO
都是更推荐的查询。因为在 MATCH
查询中,做边拓展的算子 Traverse
默认不只取边上的信息,还会同时取两端的信息。我们可以通过 explain
和 profile
来对比两个查询:
GO FROM "player100" over * BIDIRECT WHERE id($$) == "player101" YIELD edge AS e
MATCH (n)--(m) WHERE id(n) == "player100" AND id(m) == "player101" RETURN count(*)
(root@nebula) [basketballplayer]> explain MATCH (n)--(m) WHERE id(n) == "player100" AND id(m) == "player101" RETURN count(*)
Execution succeeded (time spent 1340/8307 us)
Execution Plan (optimize time 424 us)
-----+----------------+--------------+----------------+-------------------------------------------------------------------
| id | name | dependencies | profiling data | operator info |
-----+----------------+--------------+----------------+-------------------------------------------------------------------
| 8 | Aggregate | 10 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "count(*)" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Aggregate_8" |
| | | | | } |
| | | | | inputVar: __Filter_7 |
| | | | | groupKeys: [] |
| | | | | groupItems: [ |
| | | | | { |
| | | | | "expr": "count(*)" |
| | | | | } |
| | | | | ] |
-----+----------------+--------------+----------------+-------------------------------------------------------------------
| 10 | Project | 9 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "n", |
| | | | | "m" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Filter_7" |
| | | | | } |
| | | | | inputVar: __Filter_9 |
| | | | | columns: [ |
| | | | | "$-.n AS n", |
| | | | | "$-.m AS m" |
| | | | | ] |
-----+----------------+--------------+----------------+-------------------------------------------------------------------
| 9 | Filter | 5 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "n", |
| | | | | "__VAR_0", |
| | | | | "m" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Filter_9" |
| | | | | } |
| | | | | inputVar: __AppendVertices_5 |
| | | | | condition: ((id($-.n)=="player100") AND (id($-.m)=="player101")) |
| | | | | isStable: false |
-----+----------------+--------------+----------------+-------------------------------------------------------------------
| 5 | AppendVertices | 4 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "n", |
| | | | | "__VAR_0", |
| | | | | "m" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__AppendVertices_5" |
| | | | | } |
| | | | | inputVar: __Traverse_4 |
| | | | | space: 49 |
| | | | | dedup: true |
| | | | | limit: -1 |
| | | | | filter: |
| | | | | orderBy: [] |
| | | | | src: none_direct_dst($-.__VAR_0) |
| | | | | props: [ |
| | | | | { |
| | | | | "props": [ |
| | | | | "name", |
| | | | | "age", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 59 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "geo", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 60 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "name", |
| | | | | "age", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 50 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "name", |
| | | | | "email", |
| | | | | "phone_num", |
| | | | | "birthday", |
| | | | | "address", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 61 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "name", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 51 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "address", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 62 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "uuid", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 63 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 64 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 65 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 66 |
| | | | | } |
| | | | | ] |
| | | | | exprs: |
| | | | | vertex_filter: |
| | | | | if_track_previous_path: true |
-----+----------------+--------------+----------------+-------------------------------------------------------------------
| 4 | Traverse | 2 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "n", |
| | | | | "__VAR_0" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Traverse_4" |
| | | | | } |
| | | | | inputVar: __Dedup_2 |
| | | | | space: 49 |
| | | | | dedup: true |
| | | | | limit: -1 |
| | | | | filter: |
| | | | | orderBy: [] |
| | | | | src: $-._vid |
| | | | | edgeTypes: [] |
| | | | | edgeDirection: BOTH |
| | | | | vertexProps: [ |
| | | | | { |
| | | | | "props": [ |
| | | | | "name", |
| | | | | "age", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 59 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "geo", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 60 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "name", |
| | | | | "age", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 50 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "name", |
| | | | | "email", |
| | | | | "phone_num", |
| | | | | "birthday", |
| | | | | "address", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 61 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "name", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 51 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "address", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 62 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "uuid", |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 63 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 64 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 65 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 66 |
| | | | | } |
| | | | | ] |
| | | | | edgeProps: [ |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": -71 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": 71 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": -70 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": 70 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": -53 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": 53 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": -68 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": 68 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": -52 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": 52 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": -67 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": 67 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": -69 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_type", |
| | | | | "_src" |
| | | | | ], |
| | | | | "type": 69 |
| | | | | } |
| | | | | ] |
| | | | | statProps: |
| | | | | exprs: |
| | | | | random: false |
| | | | | steps: |
| | | | | vertex filter: |
| | | | | edge filter: |
| | | | | if_track_previous_path: false |
| | | | | first step filter: |
-----+----------------+--------------+----------------+-------------------------------------------------------------------
| 2 | Dedup | 1 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "_vid" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Dedup_2" |
| | | | | } |
| | | | | inputVar: __VAR_1 |
-----+----------------+--------------+----------------+-------------------------------------------------------------------
| 1 | PassThrough | 3 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "_vid" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__VAR_1" |
| | | | | } |
| | | | | inputVar: |
-----+----------------+--------------+----------------+-------------------------------------------------------------------
| 3 | Start | | | outputVar: { |
| | | | | "colNames": [], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Start_3" |
| | | | | } |
-----+----------------+--------------+----------------+-------------------------------------------------------------------
可以看到,尽管我们只想返回 count(*)
,在查询计划中,Traverse
算子仍然包涵了所有的点属性获取(vertexProps
)。
(root@nebula) [basketballplayer]> explain GO FROM "player100" over * BIDIRECT WHERE id($$) == "player101" YIELD edge AS e
Execution succeeded (time spent 1549/16916 us)
Execution Plan (optimize time 751 us)
-----+--------------+--------------+----------------+-----------------------------------
| id | name | dependencies | profiling data | operator info |
-----+--------------+--------------+----------------+-----------------------------------
| 7 | Project | 6 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "e" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Project_7" |
| | | | | } |
| | | | | inputVar: __Filter_6 |
| | | | | columns: [ |
| | | | | "EDGE AS e" |
| | | | | ] |
-----+--------------+--------------+----------------+-----------------------------------
| 6 | Filter | 5 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "EDGE", |
| | | | | "JOIN_DST_VID", |
| | | | | "$$", |
| | | | | "DST_VID" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Filter_6" |
| | | | | } |
| | | | | inputVar: __LeftJoin_5 |
| | | | | condition: (id($$)=="player101") |
| | | | | isStable: false |
-----+--------------+--------------+----------------+-----------------------------------
| 5 | LeftJoin | 4 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "EDGE", |
| | | | | "JOIN_DST_VID", |
| | | | | "$$", |
| | | | | "DST_VID" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__LeftJoin_5" |
| | | | | } |
| | | | | inputVar: { |
| | | | | "rightVar": { |
| | | | | "__Project_4": 0 |
| | | | | }, |
| | | | | "leftVar": { |
| | | | | "__Project_2": 0 |
| | | | | } |
| | | | | } |
| | | | | hashKeys: [ |
| | | | | "COLUMN[-1]" |
| | | | | ] |
| | | | | probeKeys: [ |
| | | | | "COLUMN[-1]" |
| | | | | ] |
| | | | | kind: LeftJoin |
-----+--------------+--------------+----------------+-----------------------------------
| 4 | Project | 3 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "$$", |
| | | | | "DST_VID" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Project_4" |
| | | | | } |
| | | | | inputVar: __GetVertices_3 |
| | | | | columns: [ |
| | | | | "$$ AS $$", |
| | | | | "COLUMN[0] AS DST_VID" |
| | | | | ] |
-----+--------------+--------------+----------------+-----------------------------------
| 3 | GetVertices | 2 | | outputVar: { |
| | | | | "colNames": [], |
| | | | | "type": "DATASET", |
| | | | | "name": "__GetVertices_3" |
| | | | | } |
| | | | | inputVar: __Project_2 |
| | | | | space: 49 |
| | | | | dedup: true |
| | | | | limit: 9223372036854775807 |
| | | | | filter: |
| | | | | orderBy: [] |
| | | | | src: COLUMN[-1] |
| | | | | props: [ |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 66 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 65 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag" |
| | | | | ], |
| | | | | "tagId": 64 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag", |
| | | | | "uuid" |
| | | | | ], |
| | | | | "tagId": 63 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag", |
| | | | | "geo" |
| | | | | ], |
| | | | | "tagId": 60 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag", |
| | | | | "age", |
| | | | | "name" |
| | | | | ], |
| | | | | "tagId": 59 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag", |
| | | | | "age", |
| | | | | "name" |
| | | | | ], |
| | | | | "tagId": 50 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag", |
| | | | | "address", |
| | | | | "birthday", |
| | | | | "email", |
| | | | | "name", |
| | | | | "phone_num" |
| | | | | ], |
| | | | | "tagId": 61 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag", |
| | | | | "address" |
| | | | | ], |
| | | | | "tagId": 62 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_tag", |
| | | | | "name" |
| | | | | ], |
| | | | | "tagId": 51 |
| | | | | } |
| | | | | ] |
| | | | | exprs: |
-----+--------------+--------------+----------------+-----------------------------------
| 2 | Project | 1 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "EDGE", |
| | | | | "JOIN_DST_VID" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Project_2" |
| | | | | } |
| | | | | inputVar: __GetNeighbors_1 |
| | | | | columns: [ |
| | | | | "EDGE AS EDGE", |
| | | | | "*._dst AS JOIN_DST_VID" |
| | | | | ] |
-----+--------------+--------------+----------------+-----------------------------------
| 1 | GetNeighbors | 0 | | outputVar: { |
| | | | | "colNames": [], |
| | | | | "type": "DATASET", |
| | | | | "name": "__GetNeighbors_1" |
| | | | | } |
| | | | | inputVar: __VAR_0 |
| | | | | space: 49 |
| | | | | dedup: false |
| | | | | limit: -1 |
| | | | | filter: |
| | | | | orderBy: [] |
| | | | | src: COLUMN[0] |
| | | | | edgeTypes: [] |
| | | | | edgeDirection: OUT_EDGE |
| | | | | vertexProps: |
| | | | | edgeProps: [ |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "end_year", |
| | | | | "start_year" |
| | | | | ], |
| | | | | "type": -52 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "degree" |
| | | | | ], |
| | | | | "type": -53 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "time" |
| | | | | ], |
| | | | | "type": -67 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "time" |
| | | | | ], |
| | | | | "type": -68 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type" |
| | | | | ], |
| | | | | "type": -69 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type" |
| | | | | ], |
| | | | | "type": -70 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type" |
| | | | | ], |
| | | | | "type": -71 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "end_year", |
| | | | | "start_year" |
| | | | | ], |
| | | | | "type": 52 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "degree" |
| | | | | ], |
| | | | | "type": 53 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "time" |
| | | | | ], |
| | | | | "type": 67 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "time" |
| | | | | ], |
| | | | | "type": 68 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type" |
| | | | | ], |
| | | | | "type": 69 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type" |
| | | | | ], |
| | | | | "type": 70 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type" |
| | | | | ], |
| | | | | "type": 71 |
| | | | | } |
| | | | | ] |
| | | | | statProps: |
| | | | | exprs: |
| | | | | random: false |
-----+--------------+--------------+----------------+-----------------------------------
| 0 | Start | | | outputVar: { |
| | | | | "colNames": [], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Start_0" |
| | | | | } |
-----+--------------+--------------+----------------+-----------------------------------
Tue, 13 Sep 2022 15:13:28 CST
反观 GO
的查询,GetNeighbors
算子只需要沿着起点进行边边扫描就好。然而,这个查询的计划里居然还有 GetVertices
算子取获取点的属性,这看起来完全是不必要的。
更优的 GO 表达
下面,我们看看还有没有优化的余地,这里,我们用到了 $$
引用属性,它是一个很好的语义表达,方便我们取点信息。但因为 $$
可以用来获取点的属性(比如 $$.player.age
),所以优化规则中对 $$
的函数 id()
会引入了 GetVertices
算子。因此,在没有优化计划让表达语句做出更优执行计划之前,我们可以按照”减少模糊,增加确定“的思路,把查询语句潜在涉及取点属性的表达绕过。
在架构设计文章告诉我们,一条边存储了起点、终点的 ID 信息,当我们只关心边是否存在,不关心对端顶点属性时,这个查询只扫边就可以了。
所以,要绕开 id($$)
这个表达的一个方法就是用 edge
。在双向探索的情况下,id($$)
等价的表达是 dst(edge) == "player101" OR src(edge) == "player101"
。所以,这句查询可以改写成:
- GO FROM "player100" over * BIDIRECT WHERE id($$) == "player101" YIELD edge as e
+ GO FROM "player100" over * BIDIRECT WHERE dst(edge) == "player101" OR src(edge) == "player101" YIELD edge AS e
我们看看优化之后的查询计划:GetNeighbors->Filter->Project
,显然,它是最优的了!
(root@nebula) [basketballplayer]> explain GO FROM "player100" over * BIDIRECT WHERE dst(edge) == "player101" OR src(edge) == "player101" YIELD edge AS e
Execution succeeded (time spent 1060/6717 us)
Execution Plan (optimize time 402 us)
-----+--------------+--------------+----------------+--------------------------------------------------------------------
| id | name | dependencies | profiling data | operator info |
-----+--------------+--------------+----------------+--------------------------------------------------------------------
| 3 | Project | 2 | | outputVar: { |
| | | | | "colNames": [ |
| | | | | "e" |
| | | | | ], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Project_3" |
| | | | | } |
| | | | | inputVar: __Filter_2 |
| | | | | columns: [ |
| | | | | "EDGE AS e" |
| | | | | ] |
-----+--------------+--------------+----------------+--------------------------------------------------------------------
| 2 | Filter | 1 | | outputVar: { |
| | | | | "colNames": [], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Filter_2" |
| | | | | } |
| | | | | inputVar: __GetNeighbors_1 |
| | | | | condition: ((dst(EDGE)=="player101") OR (src(EDGE)=="player101")) |
| | | | | isStable: false |
-----+--------------+--------------+----------------+--------------------------------------------------------------------
| 1 | GetNeighbors | 0 | | outputVar: { |
| | | | | "colNames": [], |
| | | | | "type": "DATASET", |
| | | | | "name": "__GetNeighbors_1" |
| | | | | } |
| | | | | inputVar: __VAR_0 |
| | | | | space: 49 |
| | | | | dedup: false |
| | | | | limit: -1 |
| | | | | filter: |
| | | | | orderBy: [] |
| | | | | src: COLUMN[0] |
| | | | | edgeTypes: [] |
| | | | | edgeDirection: OUT_EDGE |
| | | | | vertexProps: |
| | | | | edgeProps: [ |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "end_year", |
| | | | | "start_year" |
| | | | | ], |
| | | | | "type": -52 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "degree" |
| | | | | ], |
| | | | | "type": -53 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "time" |
| | | | | ], |
| | | | | "type": -67 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "time" |
| | | | | ], |
| | | | | "type": -68 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type" |
| | | | | ], |
| | | | | "type": -69 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type" |
| | | | | ], |
| | | | | "type": -70 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type" |
| | | | | ], |
| | | | | "type": -71 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "end_year", |
| | | | | "start_year" |
| | | | | ], |
| | | | | "type": 52 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "degree" |
| | | | | ], |
| | | | | "type": 53 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "time" |
| | | | | ], |
| | | | | "type": 67 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type", |
| | | | | "time" |
| | | | | ], |
| | | | | "type": 68 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type" |
| | | | | ], |
| | | | | "type": 69 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type" |
| | | | | ], |
| | | | | "type": 70 |
| | | | | }, |
| | | | | { |
| | | | | "props": [ |
| | | | | "_dst", |
| | | | | "_rank", |
| | | | | "_src", |
| | | | | "_type" |
| | | | | ], |
| | | | | "type": 71 |
| | | | | } |
| | | | | ] |
| | | | | statProps: |
| | | | | exprs: |
| | | | | random: false |
-----+--------------+--------------+----------------+--------------------------------------------------------------------
| 0 | Start | | | outputVar: { |
| | | | | "colNames": [], |
| | | | | "type": "DATASET", |
| | | | | "name": "__Start_0" |
| | | | | } |
-----+--------------+--------------+----------------+--------------------------------------------------------------------