From 2c51d916df81d11a2f5022af9ae5c25e891ca43e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 15 Sep 2025 13:53:45 +0200 Subject: [PATCH 1/7] fix shortest queries --- .../ROOT/pages/patterns/shortest-paths.adoc | 69 +++++++++---------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/modules/ROOT/pages/patterns/shortest-paths.adoc b/modules/ROOT/pages/patterns/shortest-paths.adoc index e8d83f72e..56c8c59d4 100644 --- a/modules/ROOT/pages/patterns/shortest-paths.adoc +++ b/modules/ROOT/pages/patterns/shortest-paths.adoc @@ -572,7 +572,7 @@ Without `CALL`, the planner does not know how many targets there are per source ---- PROFILE MATCH p = ANY SHORTEST - (source:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--+ + (source:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--{2,} (target:N {trail: ["A", "B", "C", "A", "B", "C", "A", "B", "C"]}) RETURN length(p) AS pathLength ---- @@ -596,24 +596,24 @@ It, therefore, must exhaust all possible target nodes before determining the sho +-----------------------------------+----+----------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | +-----------------------------------+----+----------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | `length(p)` | 19612941 | 1 | 0 | 0 | 0/0 | 0.027 | | +| +ProduceResults | 0 | pathLength | 19612941 | 1 | 0 | 0 | 0/0 | 0.228 | | | | +----+----------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +Projection | 1 | length((source)-[anon_7*]-(target)) AS `length(p)` | 19612941 | 1 | 17 | | 9/0 | 0.036 | | +| +Projection | 1 | length((source)-[anon_7*]-(target)) AS pathLength | 19612941 | 1 | 17 | | 9/0 | 1.405 | | | | +----+----------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +StatefulShortestPath(All, Trail) | 2 | SHORTEST 1 (source) ((`anon_3`)-[`anon_4`]-(`anon_5`)){1, } (target) | 19612941 | 1 | 354292 | 64720328 | 85358/0 | 139.138 | In Pipeline 1 | +| +StatefulShortestPath(All, Trail) | 2 | SHORTEST 1 (source) ((`anon_3`)-[`anon_4`]-(`anon_5`)){2, } (target) | 19612941 | 1 | 354300 | 65608340 | 85662/0 | 290.001 | In Pipeline 1 | | | | | expanding from: source | | | | | | | | | | | | inlined predicates: target.trail = $autolist_1 | | | | | | | | | | | | target:N | | | | | | | | | | +----+----------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ | +Filter | 3 | source.trail = $autolist_0 | 4429 | 1 | 177146 | | | | | | | +----+----------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| +NodeByLabelScan | 4 | source:N | 88573 | 88573 | 88574 | 376 | 2128/0 | 42.628 | Fused in Pipeline 0 | +| +NodeByLabelScan | 4 | source:N | 88573 | 88573 | 88574 | 376 | 1853/0 | 56.193 | Fused in Pipeline 0 | +-----------------------------------+----+----------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 620029, total allocated memory: 64720664 +Total database accesses: 620037, total allocated memory: 65608676 1 row -ready to start consuming query after 59 ms, results consumed after another 183 ms +ready to start consuming query after 83 ms, results consumed after another 351 ms ---- However, since each `trail` property is unique, rewriting the query to use a `CALL` subquery yields a more efficient plan. @@ -626,43 +626,43 @@ PROFILE MATCH (start:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]}), (end:N {trail: ["A", "B", "C", "A", "B", "C", "A", "B", "C"]}) CALL (start, end) { - MATCH p = ANY SHORTEST (start)--+(end) + MATCH p = ANY SHORTEST (start)--{2,}(end) RETURN p } RETURN length(p) AS pathLength ---- -The result is a significantly faster query (down from 59 to 9 milliseconds): +The result is a less resource intensive query: .Query Plan [source, role="queryplan"] ---- -+------------------------------------+----+----------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------------------+----+----------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | `length(p)` | 19612941 | 1 | 0 | 0 | 0/0 | 0.019 | | -| | +----+----------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +Projection | 1 | length(p) AS `length(p)` | 19612941 | 1 | 0 | | 0/0 | 0.007 | | -| | +----+----------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +Projection | 2 | (start)-[anon_14*]-(end) AS p | 19612941 | 1 | 17 | | 9/0 | 0.073 | | -| | +----+----------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +StatefulShortestPath(Into, Trail) | 3 | ANY 1 (start) ((`anon_10`)-[`anon_11`]-(`anon_12`)){1, } (end) | 19612941 | 1 | 1936 | 990280 | 205/0 | 1.135 | In Pipeline 3 | -| | +----+----------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +CartesianProduct | 4 | | 19612941 | 1 | 0 | 9040 | | 0.131 | In Pipeline 2 | -| |\ +----+----------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| | +Filter | 5 | end.trail = $autolist_1 | 22143 | 1 | 177146 | | | | | -| | | +----+----------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| | +NodeByLabelScan | 6 | end:N | 442865 | 88573 | 88574 | 392 | 2128/0 | 29.822 | Fused in Pipeline 1 | -| | +----+----------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +Filter | 7 | start.trail = $autolist_0 | 4429 | 1 | 177146 | | | | | -| | +----+----------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| +NodeByLabelScan | 8 | start:N | 88573 | 88573 | 88574 | 376 | 2128/0 | 40.743 | Fused in Pipeline 0 | -+------------------------------------+----+----------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 533393, total allocated memory: 999592 ++------------------------------------+----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------------+----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | pathLength | 19612941 | 1 | 0 | 0 | 0/0 | 0.116 | | +| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | +| +Projection | 1 | length(p) AS pathLength | 19612941 | 1 | 0 | | 0/0 | 0.012 | | +| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | +| +Projection | 2 | (start)-[anon_7*]-(end) AS p | 19612941 | 1 | 17 | | 9/0 | 0.122 | | +| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | +| +StatefulShortestPath(Into, Trail) | 3 | SHORTEST 1 (start) ((`anon_3`)-[`anon_4`]-(`anon_5`)){2, } (end) | 19612941 | 1 | 1952 | 1337600 | 209/0 | 2.786 | In Pipeline 3 | +| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| +CartesianProduct | 4 | | 19612941 | 1 | 0 | 9040 | | 0.053 | In Pipeline 2 | +| |\ +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| | +Filter | 5 | end.trail = $autolist_1 | 22143 | 1 | 177146 | | | | | +| | | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | +| | +NodeByLabelScan | 6 | end:N | 442865 | 88573 | 88574 | 392 | 1853/0 | 37.853 | Fused in Pipeline 1 | +| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| +Filter | 7 | start.trail = $autolist_0 | 4429 | 1 | 177146 | | | | | +| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | +| +NodeByLabelScan | 8 | start:N | 88573 | 88573 | 88574 | 376 | 1853/0 | 67.672 | Fused in Pipeline 0 | ++------------------------------------+----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 533409, total allocated memory: 1346912 1 row -ready to start consuming query after 9 ms, results consumed after another 73 ms +ready to start consuming query after 78 ms, results consumed after another 120 ms ---- ====== @@ -845,8 +845,7 @@ Or, when the estimated cardinality of the source and target nodes in a shortest * Selector is one of: `SHORTEST 1`, `ANY`, `ANY SHORTEST`, `ALL SHORTEST`, `SHORTEST GROUP`, or `SHORTEST 1 GROUP`. * There is only one relationship pattern. * If the pattern is a xref:patterns/variable-length-patterns.adoc#quantified-path-patterns[quantified path pattern] in the form of `\(()-[]-())`, or is a xref:patterns/variable-length-patterns.adoc#quantified-relationships[quantified-relationship], and it uses a filter that can be applied directly to the relationship. -* If the pattern is a quantified path pattern or a quantified relationship and the relationship pattern is directed, the lower bound of the xref:patterns/reference.adoc#quantifiers[quantifier] is 0 or 1. -If the relationship pattern is directed, the lower bound is 0. +* If the pattern is a quantified path pattern or a quantified relationship, the lower bound of the xref:patterns/reference.adoc#quantifiers[quantifier] is 0 or 1. * In the case of a quantified path pattern, there are no node variables declared inside the quantified path pattern that are referenced elsewhere. | `StatefulShortestPath(Into)` From a06e2f54b663758aed166605f8023fbfeba109c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 15 Sep 2025 13:55:27 +0200 Subject: [PATCH 2/7] grammar --- modules/ROOT/pages/patterns/shortest-paths.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/patterns/shortest-paths.adoc b/modules/ROOT/pages/patterns/shortest-paths.adoc index 56c8c59d4..a958d419a 100644 --- a/modules/ROOT/pages/patterns/shortest-paths.adoc +++ b/modules/ROOT/pages/patterns/shortest-paths.adoc @@ -845,7 +845,7 @@ Or, when the estimated cardinality of the source and target nodes in a shortest * Selector is one of: `SHORTEST 1`, `ANY`, `ANY SHORTEST`, `ALL SHORTEST`, `SHORTEST GROUP`, or `SHORTEST 1 GROUP`. * There is only one relationship pattern. * If the pattern is a xref:patterns/variable-length-patterns.adoc#quantified-path-patterns[quantified path pattern] in the form of `\(()-[]-())`, or is a xref:patterns/variable-length-patterns.adoc#quantified-relationships[quantified-relationship], and it uses a filter that can be applied directly to the relationship. -* If the pattern is a quantified path pattern or a quantified relationship, the lower bound of the xref:patterns/reference.adoc#quantifiers[quantifier] is 0 or 1. +* If the pattern is a quantified path pattern or a quantified relationship and the lower bound of the xref:patterns/reference.adoc#quantifiers[quantifier] is 0 or 1. * In the case of a quantified path pattern, there are no node variables declared inside the quantified path pattern that are referenced elsewhere. | `StatefulShortestPath(Into)` From 14573b0d6b71f2e7d29d1840a50412cb05d178cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:09:29 +0200 Subject: [PATCH 3/7] constraint example --- modules/ROOT/pages/patterns/shortest-paths.adoc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/ROOT/pages/patterns/shortest-paths.adoc b/modules/ROOT/pages/patterns/shortest-paths.adoc index a958d419a..2733f6f63 100644 --- a/modules/ROOT/pages/patterns/shortest-paths.adoc +++ b/modules/ROOT/pages/patterns/shortest-paths.adoc @@ -690,7 +690,7 @@ As a result, the simpler shortest path query (without a `CALL` subquery) will no ---- PROFILE MATCH p = ANY SHORTEST - (source:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--+ + (source:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--{2,} (target:N {trail: ["A", "B", "C", "A", "B", "C", "A", "B", "C"]}) RETURN length(p) AS pathLength ---- @@ -701,13 +701,13 @@ RETURN length(p) AS pathLength +------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | +------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | `length(p)` | 1 | 1 | 0 | 0 | 0/0 | 0.034 | | +| +ProduceResults | 0 | pathLength | 1 | 1 | 0 | 0 | 0/0 | 0.104 | | | | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Projection | 1 | length((source)-[anon_19*]-(target)) AS `length(p)` | 1 | 1 | 17 | | 9/0 | 0.057 | | +| +Projection | 1 | length((source)-[anon_23*]-(target)) AS pathLength | 1 | 1 | 17 | | 9/0 | 0.133 | | | | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +StatefulShortestPath(Into, Trail) | 2 | SHORTEST 1 (source) ((`anon_15`)-[`anon_16`]-(`anon_17`)){1, } (target) | 1 | 1 | 1936 | 990288 | 205/0 | 1.658 | In Pipeline 1 | +| +StatefulShortestPath(Into, Trail) | 2 | SHORTEST 1 (source) ((`anon_19`)-[`anon_20`]-(`anon_21`)){2, } (target) | 1 | 1 | 1952 | 1337584 | 209/0 | 2.274 | In Pipeline 1 | | | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +MultiNodeIndexSeek | 3 | UNIQUE source:N(trail) WHERE trail = $autolist_0, UNIQUE target:N(trail) WHERE trail = $autolist_1 | 1 | 1 | 4 | 376 | 4/2 | 0.332 | In Pipeline 0 | +| +MultiNodeIndexSeek | 3 | UNIQUE source:N(trail) WHERE trail = $autolist_0, UNIQUE target:N(trail) WHERE trail = $autolist_1 | 1 | 1 | 4 | 376 | 1/5 | 3.688 | In Pipeline 0 | +------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ Total database accesses: 1957, total allocated memory: 990608 From 9239c49f3cfe768ec8261402dbec5d2c099baeef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:21:29 +0200 Subject: [PATCH 4/7] limitations example updates --- modules/ROOT/pages/patterns/shortest-paths.adoc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/ROOT/pages/patterns/shortest-paths.adoc b/modules/ROOT/pages/patterns/shortest-paths.adoc index 2733f6f63..ef89f6a77 100644 --- a/modules/ROOT/pages/patterns/shortest-paths.adoc +++ b/modules/ROOT/pages/patterns/shortest-paths.adoc @@ -736,7 +736,7 @@ Consider the following query, which does not specify a unique target node and ge ---- PROFILE MATCH p = ANY SHORTEST - (start:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--+ + (start:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--{2,} (end:N {level: 9}) RETURN count(*) AS pathCount ---- @@ -759,22 +759,22 @@ Due to the existence of multiple target nodes without a specified, unique proper +-----------------------------------+----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | +-----------------------------------+----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | `count(*)` | 1 | 1 | 0 | 0 | 0/0 | 0.015 | | +| +ProduceResults | 0 | pathCount | 1 | 1 | 0 | 0 | 0/0 | 0.199 | | | | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +EagerAggregation | 1 | count(*) AS `count(*)` | 1 | 1 | 0 | 40 | 0/0 | 0.097 | In Pipeline 2 | +| +EagerAggregation | 1 | count(*) AS pathCount | 1 | 1 | 0 | 40 | 0/0 | 2.975 | In Pipeline 2 | | | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ -| +StatefulShortestPath(All, Trail) | 2 | SHORTEST 1 (start) ((`anon_3`)-[`anon_4`]-(`anon_5`)){1, } (end) | 8052 | 19682 | 373974 | 81274328 | 65235/0 | 330.475 | In Pipeline 1 | +| +StatefulShortestPath(All, Trail) | 2 | SHORTEST 1 (start) ((`anon_3`)-[`anon_4`]-(`anon_5`)){2, } (end) | 8052 | 19682 | 373982 | 82162292 | 65705/0 | 586.334 | In Pipeline 1 | | | | | expanding from: start | | | | | | | | | | | | inlined predicates: end.level = $autoint_1 | | | | | | | | | | | | end:N | | | | | | | | | | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ -| +NodeUniqueIndexSeek | 3 | UNIQUE start:N(trail) WHERE trail = $autolist_0 | 1 | 1 | 2 | 376 | 3/0 | 0.106 | In Pipeline 0 | +| +NodeUniqueIndexSeek | 3 | UNIQUE start:N(trail) WHERE trail = $autolist_0 | 1 | 1 | 2 | 376 | 3/0 | 0.232 | In Pipeline 0 | +-----------------------------------+----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ -Total database accesses: 373976, total allocated memory: 81274688 +Total database accesses: 373984, total allocated memory: 82162652 1 row -ready to start consuming query after 40 ms, results consumed after another 331 ms +ready to start consuming query after 83 ms, results consumed after another 592 ms ---- If the query is rewritten with a `CALL` subquery the planner will use `StatefulShortestPath(Into)` which performs separate traversals for each individual source-target node pairs. From d6a6c558b0d70b48aa76ca7999ba8f61e957881b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 15 Sep 2025 14:30:59 +0200 Subject: [PATCH 5/7] fix --- modules/ROOT/pages/patterns/shortest-paths.adoc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/ROOT/pages/patterns/shortest-paths.adoc b/modules/ROOT/pages/patterns/shortest-paths.adoc index ef89f6a77..9a2fbcf09 100644 --- a/modules/ROOT/pages/patterns/shortest-paths.adoc +++ b/modules/ROOT/pages/patterns/shortest-paths.adoc @@ -786,7 +786,7 @@ PROFILE MATCH (start:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]}), (end:N {level: 9}) CALL (start, end) { - MATCH p = ANY SHORTEST (start)--+(end) + MATCH p = ANY SHORTEST (start)--{2,}(end) RETURN p } RETURN count(*) AS pathCount @@ -798,21 +798,21 @@ RETURN count(*) AS pathCount +------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | +------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | `count(*)` | 1 | 1 | 0 | 0 | 0/0 | 0.120 | | +| +ProduceResults | 0 | pathCount | 1 | 1 | 0 | 0 | 0/0 | 0.333 | | | | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+ | -| +EagerAggregation | 1 | count(*) AS `count(*)` | 1 | 1 | 0 | 40 | 0/0 | 0.172 | In Pipeline 2 | +| +EagerAggregation | 1 | count(*) AS pathCount | 1 | 1 | 0 | 40 | 0/0 | 0.909 | In Pipeline 2 | | | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ -| +Projection | 2 | (start)-[anon_7*]-(end) AS p | 8052 | 19682 | 314930 | | 184197/0 | 35.430 | | +| +Projection | 2 | (start)-[anon_7*]-(end) AS p | 8052 | 19682 | 314930 | | 189543/0 | 56.101 | | | | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+ | -| +StatefulShortestPath(Into, Trail) | 3 | SHORTEST 1 (start) ((`anon_3`)-[`anon_4`]-(`anon_5`)){1, } (end) | 8052 | 19682 | 32672226 | 157866776 | 3588500/0 | 14200.424 | In Pipeline 1 | +| +StatefulShortestPath(Into, Trail) | 3 | SHORTEST 1 (start) ((`anon_3`)-[`anon_4`]-(`anon_5`)){2, } (end) | 8052 | 19682 | 32986690 | 221423992 | 3775866/0 | 22775.150 | In Pipeline 1 | | | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ -| +MultiNodeIndexSeek | 4 | UNIQUE start:N(trail) WHERE trail = $autolist_0, RANGE INDEX end:N(level) WHERE level = $autoint_1 | 8052 | 19683 | 19686 | 376 | 108/0 | 4.014 | In Pipeline 0 | +| +MultiNodeIndexSeek | 4 | UNIQUE start:N(trail) WHERE trail = $autolist_0, RANGE INDEX end:N(level) WHERE level = $autoint_1 | 8052 | 19683 | 19686 | 376 | 3/54 | 23.649 | In Pipeline 0 | +------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ -Total database accesses: 33006842, total allocated memory: 157867272 +Total database accesses: 33321306, total allocated memory: 221424488 1 row -ready to start consuming query after 32 ms, results consumed after another 14244 ms +ready to start consuming query after 57 ms, results consumed after another 22870 ms ---- As the plan shows, in this scenario it is not more efficient to enforce a single source-target node pair. From cbc50058abdbc40163b83bf5749046e6ee6494d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:27:42 +0200 Subject: [PATCH 6/7] examples using legacy --- .../ROOT/pages/patterns/shortest-paths.adoc | 176 +++++++++--------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/modules/ROOT/pages/patterns/shortest-paths.adoc b/modules/ROOT/pages/patterns/shortest-paths.adoc index 9a2fbcf09..1a317c14f 100644 --- a/modules/ROOT/pages/patterns/shortest-paths.adoc +++ b/modules/ROOT/pages/patterns/shortest-paths.adoc @@ -572,7 +572,7 @@ Without `CALL`, the planner does not know how many targets there are per source ---- PROFILE MATCH p = ANY SHORTEST - (source:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--{2,} + (source:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--+ (target:N {trail: ["A", "B", "C", "A", "B", "C", "A", "B", "C"]}) RETURN length(p) AS pathLength ---- @@ -593,22 +593,22 @@ It, therefore, must exhaust all possible target nodes before determining the sho .Query Plan [source, role="queryplan"] ---- -+-----------------------------------+----+----------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------------------+----+----------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | pathLength | 19612941 | 1 | 0 | 0 | 0/0 | 0.228 | | -| | +----+----------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +Projection | 1 | length((source)-[anon_7*]-(target)) AS pathLength | 19612941 | 1 | 17 | | 9/0 | 1.405 | | -| | +----+----------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +StatefulShortestPath(All, Trail) | 2 | SHORTEST 1 (source) ((`anon_3`)-[`anon_4`]-(`anon_5`)){2, } (target) | 19612941 | 1 | 354300 | 65608340 | 85662/0 | 290.001 | In Pipeline 1 | -| | | | expanding from: source | | | | | | | | -| | | | inlined predicates: target.trail = $autolist_1 | | | | | | | | -| | | | target:N | | | | | | | | -| | +----+----------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +Filter | 3 | source.trail = $autolist_0 | 4429 | 1 | 177146 | | | | | -| | +----+----------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| +NodeByLabelScan | 4 | source:N | 88573 | 88573 | 88574 | 376 | 1853/0 | 56.193 | Fused in Pipeline 0 | -+-----------------------------------+----+----------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------------------------+----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------------+----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | pathLength | 19612941 | 1 | 0 | 0 | 0/0 | 0.262 | | +| | +----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | +| +Projection | 1 | length((source)-[anon_34*]-(target)) AS pathLength | 19612941 | 1 | 17 | | 9/0 | 0.178 | | +| | +----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | +| +StatefulShortestPath(All, Trail) | 2 | SHORTEST 1 (source) ((`anon_30`)-[`anon_31`]-(`anon_32`)){1, } (target) | 19612941 | 1 | 354292 | 64720336 | 85658/0 | 192.441 | In Pipeline 1 | +| | | | expanding from: source | | | | | | | | +| | | | inlined predicates: target.trail = $autolist_1 | | | | | | | | +| | | | target:N | | | | | | | | +| | +----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| +Filter | 3 | source.trail = $autolist_0 | 4429 | 1 | 177146 | | | | | +| | +----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | +| +NodeByLabelScan | 4 | source:N | 88573 | 88573 | 88574 | 376 | 1853/0 | 52.782 | Fused in Pipeline 0 | ++-----------------------------------+----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 620037, total allocated memory: 65608676 @@ -617,7 +617,7 @@ ready to start consuming query after 83 ms, results consumed after another 351 m ---- However, since each `trail` property is unique, rewriting the query to use a `CALL` subquery yields a more efficient plan. -This is because it forces the planner to use the `StatefulShortestPath(Into)` operator, which expands from the source node until it finds its specific target node, and ensures that `ANY SHORTEST` is executed once per source-target pair. +This is because it forces the planner to use the `ShortestPath` operator, which expands from the source node until it finds its specific target node, and ensures that `ANY SHORTEST` is executed once per source-target pair. .Shortest path query rewritten with a `CALL` subquery [source, cypher] @@ -626,7 +626,7 @@ PROFILE MATCH (start:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]}), (end:N {trail: ["A", "B", "C", "A", "B", "C", "A", "B", "C"]}) CALL (start, end) { - MATCH p = ANY SHORTEST (start)--{2,}(end) + MATCH p = ANY SHORTEST (start)--+(end) RETURN p } RETURN length(p) AS pathLength @@ -637,32 +637,32 @@ The result is a less resource intensive query: .Query Plan [source, role="queryplan"] ---- -+------------------------------------+----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------------------+----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | pathLength | 19612941 | 1 | 0 | 0 | 0/0 | 0.116 | | -| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +Projection | 1 | length(p) AS pathLength | 19612941 | 1 | 0 | | 0/0 | 0.012 | | -| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +Projection | 2 | (start)-[anon_7*]-(end) AS p | 19612941 | 1 | 17 | | 9/0 | 0.122 | | -| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +StatefulShortestPath(Into, Trail) | 3 | SHORTEST 1 (start) ((`anon_3`)-[`anon_4`]-(`anon_5`)){2, } (end) | 19612941 | 1 | 1952 | 1337600 | 209/0 | 2.786 | In Pipeline 3 | -| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +CartesianProduct | 4 | | 19612941 | 1 | 0 | 9040 | | 0.053 | In Pipeline 2 | -| |\ +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| | +Filter | 5 | end.trail = $autolist_1 | 22143 | 1 | 177146 | | | | | -| | | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| | +NodeByLabelScan | 6 | end:N | 442865 | 88573 | 88574 | 392 | 1853/0 | 37.853 | Fused in Pipeline 1 | -| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +Filter | 7 | start.trail = $autolist_0 | 4429 | 1 | 177146 | | | | | -| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| +NodeByLabelScan | 8 | start:N | 88573 | 88573 | 88574 | 376 | 1853/0 | 67.672 | Fused in Pipeline 0 | -+------------------------------------+----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 533409, total allocated memory: 1346912 ++--------------------+----+------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------+----+------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | pathLength | 19612941 | 1 | 0 | 0 | | | | +| | +----+------------------------------------+----------------+-------+---------+----------------+ | | | +| +Projection | 1 | length(p) AS pathLength | 19612941 | 1 | 0 | | | | | +| | +----+------------------------------------+----------------+-------+---------+----------------+ | | | +| +Projection | 2 | (start)-[anon_27*]-(end) AS p | 19612941 | 1 | 17 | | | | | +| | +----+------------------------------------+----------------+-------+---------+----------------+ | | | +| +ShortestPath | 3 | anon_32 = (start)-[anon_27*]-(end) | 19612941 | 1 | 1544 | 31808 | 385/0 | 0.596 | Fused in Pipeline 3 | +| | +----+------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| +CartesianProduct | 4 | | 19612941 | 1 | 0 | 9040 | | 0.073 | In Pipeline 2 | +| |\ +----+------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| | +Filter | 5 | end.trail = $autolist_1 | 22143 | 1 | 177146 | | | | | +| | | +----+------------------------------------+----------------+-------+---------+----------------+ | | | +| | +NodeByLabelScan | 6 | end:N | 442865 | 88573 | 88574 | 392 | 1853/0 | 58.491 | Fused in Pipeline 1 | +| | +----+------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| +Filter | 7 | start.trail = $autolist_0 | 4429 | 1 | 177146 | | | | | +| | +----+------------------------------------+----------------+-------+---------+----------------+ | | | +| +NodeByLabelScan | 8 | start:N | 88573 | 88573 | 88574 | 376 | 1853/0 | 62.571 | Fused in Pipeline 0 | ++--------------------+----+------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 533001, total allocated memory: 41120 1 row -ready to start consuming query after 78 ms, results consumed after another 120 ms +ready to start consuming query after 65 ms, results consumed after another 124 ms ---- ====== @@ -683,14 +683,14 @@ CREATE CONSTRAINT unique_trail FOR (n:N) REQUIRE n.trail IS UNIQUE ---- This constraint will inform the planner of the uniqueness of `trail` values up front. -As a result, the simpler shortest path query (without a `CALL` subquery) will now generate a faster plan (using the `StatefulShortestPath(Into)`) operator with a cardinality of 1 for both the source and target nodes of the shortest path. +As a result, the simpler shortest path query (without a `CALL` subquery) will now generate a faster plan (using the `ShortestPath` operator) with a cardinality of 1 for both the source and target nodes of the shortest path. .Find a shortest path between two nodes [source, cypher] ---- PROFILE MATCH p = ANY SHORTEST - (source:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--{2,} + (source:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--+ (target:N {trail: ["A", "B", "C", "A", "B", "C", "A", "B", "C"]}) RETURN length(p) AS pathLength ---- @@ -698,17 +698,17 @@ RETURN length(p) AS pathLength .Query Plan [source, role="queryplan"] ---- -+------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | pathLength | 1 | 1 | 0 | 0 | 0/0 | 0.104 | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Projection | 1 | length((source)-[anon_23*]-(target)) AS pathLength | 1 | 1 | 17 | | 9/0 | 0.133 | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +StatefulShortestPath(Into, Trail) | 2 | SHORTEST 1 (source) ((`anon_19`)-[`anon_20`]-(`anon_21`)){2, } (target) | 1 | 1 | 1952 | 1337584 | 209/0 | 2.274 | In Pipeline 1 | -| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +MultiNodeIndexSeek | 3 | UNIQUE source:N(trail) WHERE trail = $autolist_0, UNIQUE target:N(trail) WHERE trail = $autolist_1 | 1 | 1 | 4 | 376 | 1/5 | 3.688 | In Pipeline 0 | -+------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ ++---------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | pathLength | 1 | 1 | 0 | 0 | | | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | length((source)-[anon_46*]-(target)) AS pathLength | 1 | 1 | 17 | | | | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +ShortestPath | 2 | anon_56 = (source)-[anon_46*]-(target) | 1 | 1 | 1544 | 31680 | | | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +MultiNodeIndexSeek | 3 | UNIQUE source:N(trail) WHERE trail = $autolist_0, UNIQUE target:N(trail) WHERE trail = $autolist_1 | 1 | 0 | 0 | 248 | 387/5 | 1.207 | Fused in Pipeline 0 | ++---------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 1957, total allocated memory: 990608 @@ -722,7 +722,7 @@ ready to start consuming query after 48 ms, results consumed after another 3 ms === Limitations of enforcing a single source-target node pair Enforcing a single source-target node pair is not always preferable. -With one source and many targets, rewriting a shortest path query using a `CALL` subquery forces the planner to use `StatefulShortestPath(Into)`, which runs once per target node. +With one source and many targets, rewriting a shortest path query using a `CALL` subquery forces the planner to use either the `ShortestPath` or `StatefulShortestPath(Into)` operators, which runs once per target node. While this is efficient for a single pair, it can become slower as the number of targets increases because it forces the planner to traverse the graph for each individual pair of source-target nodes. In such cases, it may be more efficient to let the planner use `StatefulShortestPath(All)`, which expands across the graph once and returns all matches. @@ -736,7 +736,7 @@ Consider the following query, which does not specify a unique target node and ge ---- PROFILE MATCH p = ANY SHORTEST - (start:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--{2,} + (start:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--+ (end:N {level: 9}) RETURN count(*) AS pathCount ---- @@ -756,20 +756,20 @@ Due to the existence of multiple target nodes without a specified, unique proper .Query Plan [source, role="queryplan"] ---- -+-----------------------------------+----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------------------+----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | pathCount | 1 | 1 | 0 | 0 | 0/0 | 0.199 | | -| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +EagerAggregation | 1 | count(*) AS pathCount | 1 | 1 | 0 | 40 | 0/0 | 2.975 | In Pipeline 2 | -| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ -| +StatefulShortestPath(All, Trail) | 2 | SHORTEST 1 (start) ((`anon_3`)-[`anon_4`]-(`anon_5`)){2, } (end) | 8052 | 19682 | 373982 | 82162292 | 65705/0 | 586.334 | In Pipeline 1 | -| | | | expanding from: start | | | | | | | | -| | | | inlined predicates: end.level = $autoint_1 | | | | | | | | -| | | | end:N | | | | | | | | -| | +----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ -| +NodeUniqueIndexSeek | 3 | UNIQUE start:N(trail) WHERE trail = $autolist_0 | 1 | 1 | 2 | 376 | 3/0 | 0.232 | In Pipeline 0 | -+-----------------------------------+----+------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ ++-----------------------------------+----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------------+----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | pathCount | 1 | 1 | 0 | 0 | 0/0 | 0.097 | | +| | +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | +| +EagerAggregation | 1 | count(*) AS pathCount | 1 | 1 | 0 | 40 | 0/0 | 1.168 | In Pipeline 2 | +| | +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ +| +StatefulShortestPath(All, Trail) | 2 | SHORTEST 1 (start) ((`anon_27`)-[`anon_28`]-(`anon_29`)){1, } (end) | 8052 | 19682 | 373974 | 81274328 | 65701/0 | 469.668 | In Pipeline 1 | +| | | | expanding from: start | | | | | | | | +| | | | inlined predicates: end.level = $autoint_1 | | | | | | | | +| | | | end:N | | | | | | | | +| | +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ +| +NodeUniqueIndexSeek | 3 | UNIQUE start:N(trail) WHERE trail = $autolist_0 | 1 | 1 | 2 | 376 | 3/0 | 0.248 | In Pipeline 0 | ++-----------------------------------+----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ Total database accesses: 373984, total allocated memory: 82162652 @@ -777,7 +777,7 @@ Total database accesses: 373984, total allocated memory: 82162652 ready to start consuming query after 83 ms, results consumed after another 592 ms ---- -If the query is rewritten with a `CALL` subquery the planner will use `StatefulShortestPath(Into)` which performs separate traversals for each individual source-target node pairs. +If the query is rewritten with a `CALL` subquery the planner will use the `ShortestPath` operator which performs separate traversals for each individual source-target node pairs. .Multi-target shortest path query rewritten with a `CALL` subquery [source, cypher] @@ -786,7 +786,7 @@ PROFILE MATCH (start:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]}), (end:N {level: 9}) CALL (start, end) { - MATCH p = ANY SHORTEST (start)--{2,}(end) + MATCH p = ANY SHORTEST (start)--+(end) RETURN p } RETURN count(*) AS pathCount @@ -795,19 +795,19 @@ RETURN count(*) AS pathCount .Query Plan [source, role="queryplan"] ---- -+------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | pathCount | 1 | 1 | 0 | 0 | 0/0 | 0.333 | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+ | -| +EagerAggregation | 1 | count(*) AS pathCount | 1 | 1 | 0 | 40 | 0/0 | 0.909 | In Pipeline 2 | -| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ -| +Projection | 2 | (start)-[anon_7*]-(end) AS p | 8052 | 19682 | 314930 | | 189543/0 | 56.101 | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+ | -| +StatefulShortestPath(Into, Trail) | 3 | SHORTEST 1 (start) ((`anon_3`)-[`anon_4`]-(`anon_5`)){2, } (end) | 8052 | 19682 | 32986690 | 221423992 | 3775866/0 | 22775.150 | In Pipeline 1 | -| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ -| +MultiNodeIndexSeek | 4 | UNIQUE start:N(trail) WHERE trail = $autolist_0, RANGE INDEX end:N(level) WHERE level = $autoint_1 | 8052 | 19683 | 19686 | 376 | 3/54 | 23.649 | In Pipeline 0 | -+------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ ++---------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | pathCount | 1 | 1 | 0 | 0 | 0/0 | 0.130 | In Pipeline 1 | +| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------------+ +| +EagerAggregation | 1 | count(*) AS pathCount | 1 | 1 | 0 | 40 | | | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+ | | | +| +Projection | 2 | (start)-[anon_17*]-(end) AS p | 8052 | 19682 | 314930 | | | | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+ | | | +| +ShortestPath | 3 | anon_22 = (start)-[anon_17*]-(end) | 8052 | 19682 | 25496225 | 9699624 | | | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+ | | | +| +MultiNodeIndexSeek | 4 | UNIQUE start:N(trail) WHERE trail = $autolist_0, RANGE INDEX end:N(level) WHERE level = $autoint_1 | 8052 | 19682 | 18658 | 376 | 6428257/0 | 2141.770 | Fused in Pipeline 0 | ++---------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------------+ Total database accesses: 33321306, total allocated memory: 221424488 @@ -816,7 +816,7 @@ ready to start consuming query after 57 ms, results consumed after another 22870 ---- As the plan shows, in this scenario it is not more efficient to enforce a single source-target node pair. -On the contrary, doing so ensures that `StatefulShortestPath(Into)` is executed `19682` times, once for each source-target node pair, thereby generating a more expensive query. +On the contrary, doing so ensures that the `ShortestPath` operator is executed `19682` times, once for each source-target node pair, thereby generating a more expensive query. ===== From 6705f09968327549b1659fccbcf168f0f03342e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 15 Sep 2025 16:28:51 +0200 Subject: [PATCH 7/7] revert to ssp(into) examples --- .../ROOT/pages/patterns/shortest-paths.adoc | 144 +++++++++--------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/modules/ROOT/pages/patterns/shortest-paths.adoc b/modules/ROOT/pages/patterns/shortest-paths.adoc index 1a317c14f..9bab3cfd5 100644 --- a/modules/ROOT/pages/patterns/shortest-paths.adoc +++ b/modules/ROOT/pages/patterns/shortest-paths.adoc @@ -572,7 +572,7 @@ Without `CALL`, the planner does not know how many targets there are per source ---- PROFILE MATCH p = ANY SHORTEST - (source:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--+ + (source:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--{2,} (target:N {trail: ["A", "B", "C", "A", "B", "C", "A", "B", "C"]}) RETURN length(p) AS pathLength ---- @@ -596,28 +596,28 @@ It, therefore, must exhaust all possible target nodes before determining the sho +-----------------------------------+----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | +-----------------------------------+----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | pathLength | 19612941 | 1 | 0 | 0 | 0/0 | 0.262 | | +| +ProduceResults | 0 | pathLength | 19612941 | 1 | 0 | 0 | 0/0 | 0.088 | | | | +----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +Projection | 1 | length((source)-[anon_34*]-(target)) AS pathLength | 19612941 | 1 | 17 | | 9/0 | 0.178 | | +| +Projection | 1 | length((source)-[anon_39*]-(target)) AS pathLength | 19612941 | 1 | 17 | | 9/0 | 0.091 | | | | +----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +StatefulShortestPath(All, Trail) | 2 | SHORTEST 1 (source) ((`anon_30`)-[`anon_31`]-(`anon_32`)){1, } (target) | 19612941 | 1 | 354292 | 64720336 | 85658/0 | 192.441 | In Pipeline 1 | +| +StatefulShortestPath(All, Trail) | 2 | SHORTEST 1 (source) ((`anon_35`)-[`anon_36`]-(`anon_37`)){2, } (target) | 19612941 | 1 | 354300 | 65608284 | 85662/0 | 158.664 | In Pipeline 1 | | | | | expanding from: source | | | | | | | | | | | | inlined predicates: target.trail = $autolist_1 | | | | | | | | | | | | target:N | | | | | | | | | | +----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ | +Filter | 3 | source.trail = $autolist_0 | 4429 | 1 | 177146 | | | | | | | +----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | -| +NodeByLabelScan | 4 | source:N | 88573 | 88573 | 88574 | 376 | 1853/0 | 52.782 | Fused in Pipeline 0 | +| +NodeByLabelScan | 4 | source:N | 88573 | 88573 | 88574 | 376 | 1853/0 | 53.311 | Fused in Pipeline 0 | +-----------------------------------+----+-------------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 620037, total allocated memory: 65608676 +Total database accesses: 620037, total allocated memory: 65608620 1 row -ready to start consuming query after 83 ms, results consumed after another 351 ms +ready to start consuming query after 45 ms, results consumed after another 214 ms ---- However, since each `trail` property is unique, rewriting the query to use a `CALL` subquery yields a more efficient plan. -This is because it forces the planner to use the `ShortestPath` operator, which expands from the source node until it finds its specific target node, and ensures that `ANY SHORTEST` is executed once per source-target pair. +This is because it forces the planner to use the `StatefulShortestPath(Into)` operator, which expands from the source node until it finds its specific target node, and ensures that `ANY SHORTEST` is executed once per source-target pair. .Shortest path query rewritten with a `CALL` subquery [source, cypher] @@ -626,43 +626,43 @@ PROFILE MATCH (start:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]}), (end:N {trail: ["A", "B", "C", "A", "B", "C", "A", "B", "C"]}) CALL (start, end) { - MATCH p = ANY SHORTEST (start)--+(end) + MATCH p = ANY SHORTEST (start)--{2,}(end) RETURN p } RETURN length(p) AS pathLength ---- -The result is a less resource intensive query: +The result is a significantly faster query (down from 45 to 4 milliseconds): .Query Plan [source, role="queryplan"] ---- -+--------------------+----+------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------+----+------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | pathLength | 19612941 | 1 | 0 | 0 | | | | -| | +----+------------------------------------+----------------+-------+---------+----------------+ | | | -| +Projection | 1 | length(p) AS pathLength | 19612941 | 1 | 0 | | | | | -| | +----+------------------------------------+----------------+-------+---------+----------------+ | | | -| +Projection | 2 | (start)-[anon_27*]-(end) AS p | 19612941 | 1 | 17 | | | | | -| | +----+------------------------------------+----------------+-------+---------+----------------+ | | | -| +ShortestPath | 3 | anon_32 = (start)-[anon_27*]-(end) | 19612941 | 1 | 1544 | 31808 | 385/0 | 0.596 | Fused in Pipeline 3 | -| | +----+------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +CartesianProduct | 4 | | 19612941 | 1 | 0 | 9040 | | 0.073 | In Pipeline 2 | -| |\ +----+------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| | +Filter | 5 | end.trail = $autolist_1 | 22143 | 1 | 177146 | | | | | -| | | +----+------------------------------------+----------------+-------+---------+----------------+ | | | -| | +NodeByLabelScan | 6 | end:N | 442865 | 88573 | 88574 | 392 | 1853/0 | 58.491 | Fused in Pipeline 1 | -| | +----+------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ -| +Filter | 7 | start.trail = $autolist_0 | 4429 | 1 | 177146 | | | | | -| | +----+------------------------------------+----------------+-------+---------+----------------+ | | | -| +NodeByLabelScan | 8 | start:N | 88573 | 88573 | 88574 | 376 | 1853/0 | 62.571 | Fused in Pipeline 0 | -+--------------------+----+------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 533001, total allocated memory: 41120 ++------------------------------------+----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------------+----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | pathLength | 19612941 | 1 | 0 | 0 | 0/0 | 0.080 | | +| | +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | +| +Projection | 1 | length(p) AS pathLength | 19612941 | 1 | 0 | | 0/0 | 0.023 | | +| | +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | +| +Projection | 2 | (start)-[anon_15*]-(end) AS p | 19612941 | 1 | 17 | | 9/0 | 0.063 | | +| | +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | +| +StatefulShortestPath(Into, Trail) | 3 | SHORTEST 1 (start) ((`anon_11`)-[`anon_12`]-(`anon_13`)){2, } (end) | 19612941 | 1 | 1952 | 1337576 | 209/0 | 1.938 | In Pipeline 3 | +| | +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| +CartesianProduct | 4 | | 19612941 | 1 | 0 | 9040 | | 0.054 | In Pipeline 2 | +| |\ +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| | +Filter | 5 | end.trail = $autolist_1 | 22143 | 1 | 177146 | | | | | +| | | +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | +| | +NodeByLabelScan | 6 | end:N | 442865 | 88573 | 88574 | 392 | 1853/0 | 44.047 | Fused in Pipeline 1 | +| | +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ +| +Filter | 7 | start.trail = $autolist_0 | 4429 | 1 | 177146 | | | | | +| | +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+ | | | +| +NodeByLabelScan | 8 | start:N | 88573 | 88573 | 88574 | 376 | 1853/0 | 57.818 | Fused in Pipeline 0 | ++------------------------------------+----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 533409, total allocated memory: 1346888 1 row -ready to start consuming query after 65 ms, results consumed after another 124 ms +ready to start consuming query after 4 ms, results consumed after another 106 ms ---- ====== @@ -683,14 +683,14 @@ CREATE CONSTRAINT unique_trail FOR (n:N) REQUIRE n.trail IS UNIQUE ---- This constraint will inform the planner of the uniqueness of `trail` values up front. -As a result, the simpler shortest path query (without a `CALL` subquery) will now generate a faster plan (using the `ShortestPath` operator) with a cardinality of 1 for both the source and target nodes of the shortest path. +As a result, the simpler shortest path query (without a `CALL` subquery) will now generate a faster plan (using the `StatefulShortestPath(Into)` operator) with a cardinality of 1 for both the source and target nodes of the shortest path. .Find a shortest path between two nodes [source, cypher] ---- PROFILE MATCH p = ANY SHORTEST - (source:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--+ + (source:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--{2,} (target:N {trail: ["A", "B", "C", "A", "B", "C", "A", "B", "C"]}) RETURN length(p) AS pathLength ---- @@ -698,17 +698,17 @@ RETURN length(p) AS pathLength .Query Plan [source, role="queryplan"] ---- -+---------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | pathLength | 1 | 1 | 0 | 0 | | | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Projection | 1 | length((source)-[anon_46*]-(target)) AS pathLength | 1 | 1 | 17 | | | | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +ShortestPath | 2 | anon_56 = (source)-[anon_46*]-(target) | 1 | 1 | 1544 | 31680 | | | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +MultiNodeIndexSeek | 3 | UNIQUE source:N(trail) WHERE trail = $autolist_0, UNIQUE target:N(trail) WHERE trail = $autolist_1 | 1 | 0 | 0 | 248 | 387/5 | 1.207 | Fused in Pipeline 0 | -+---------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | pathLength | 1 | 1 | 0 | 0 | 0/0 | 0.058 | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Projection | 1 | length((source)-[anon_55*]-(target)) AS pathLength | 1 | 1 | 17 | | 9/0 | 0.081 | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +StatefulShortestPath(Into, Trail) | 2 | SHORTEST 1 (source) ((`anon_51`)-[`anon_52`]-(`anon_53`)){2, } (target) | 1 | 1 | 1952 | 1337568 | 209/0 | 2.213 | In Pipeline 1 | +| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +MultiNodeIndexSeek | 3 | UNIQUE source:N(trail) WHERE trail = $autolist_0, UNIQUE target:N(trail) WHERE trail = $autolist_1 | 1 | 1 | 4 | 376 | 1/5 | 0.400 | In Pipeline 0 | ++------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ Total database accesses: 1957, total allocated memory: 990608 @@ -722,7 +722,7 @@ ready to start consuming query after 48 ms, results consumed after another 3 ms === Limitations of enforcing a single source-target node pair Enforcing a single source-target node pair is not always preferable. -With one source and many targets, rewriting a shortest path query using a `CALL` subquery forces the planner to use either the `ShortestPath` or `StatefulShortestPath(Into)` operators, which runs once per target node. +With one source and many targets, rewriting a shortest path query using a `CALL` subquery forces the planner to use either the `ShortestPath` or `StatefulShortestPath(Into)` operator, which runs once per target node. While this is efficient for a single pair, it can become slower as the number of targets increases because it forces the planner to traverse the graph for each individual pair of source-target nodes. In such cases, it may be more efficient to let the planner use `StatefulShortestPath(All)`, which expands across the graph once and returns all matches. @@ -736,7 +736,7 @@ Consider the following query, which does not specify a unique target node and ge ---- PROFILE MATCH p = ANY SHORTEST - (start:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--+ + (start:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]})--{2,} (end:N {level: 9}) RETURN count(*) AS pathCount ---- @@ -759,16 +759,16 @@ Due to the existence of multiple target nodes without a specified, unique proper +-----------------------------------+----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | +-----------------------------------+----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | pathCount | 1 | 1 | 0 | 0 | 0/0 | 0.097 | | +| +ProduceResults | 0 | pathCount | 1 | 1 | 0 | 0 | 0/0 | 0.082 | | | | +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+ | -| +EagerAggregation | 1 | count(*) AS pathCount | 1 | 1 | 0 | 40 | 0/0 | 1.168 | In Pipeline 2 | +| +EagerAggregation | 1 | count(*) AS pathCount | 1 | 1 | 0 | 40 | 0/0 | 1.151 | In Pipeline 2 | | | +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ -| +StatefulShortestPath(All, Trail) | 2 | SHORTEST 1 (start) ((`anon_27`)-[`anon_28`]-(`anon_29`)){1, } (end) | 8052 | 19682 | 373974 | 81274328 | 65701/0 | 469.668 | In Pipeline 1 | +| +StatefulShortestPath(All, Trail) | 2 | SHORTEST 1 (start) ((`anon_19`)-[`anon_20`]-(`anon_21`)){2, } (end) | 8052 | 19682 | 373982 | 82162292 | 65705/0 | 455.921 | In Pipeline 1 | | | | | expanding from: start | | | | | | | | | | | | inlined predicates: end.level = $autoint_1 | | | | | | | | | | | | end:N | | | | | | | | | | +----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ -| +NodeUniqueIndexSeek | 3 | UNIQUE start:N(trail) WHERE trail = $autolist_0 | 1 | 1 | 2 | 376 | 3/0 | 0.248 | In Pipeline 0 | +| +NodeUniqueIndexSeek | 3 | UNIQUE start:N(trail) WHERE trail = $autolist_0 | 1 | 1 | 2 | 376 | 3/0 | 0.509 | In Pipeline 0 | +-----------------------------------+----+---------------------------------------------------------------------+----------------+-------+---------+----------------+------------------------+-----------+---------------+ Total database accesses: 373984, total allocated memory: 82162652 @@ -777,7 +777,7 @@ Total database accesses: 373984, total allocated memory: 82162652 ready to start consuming query after 83 ms, results consumed after another 592 ms ---- -If the query is rewritten with a `CALL` subquery the planner will use the `ShortestPath` operator which performs separate traversals for each individual source-target node pairs. +If the query is rewritten with a `CALL` subquery the planner will use the `StatefulShortestPath(Into)` operator which performs separate traversals for each individual source-target node pairs. .Multi-target shortest path query rewritten with a `CALL` subquery [source, cypher] @@ -786,7 +786,7 @@ PROFILE MATCH (start:N {trail: ["C", "C", "A", "C", "A", "B", "B", "B", "A"]}), (end:N {level: 9}) CALL (start, end) { - MATCH p = ANY SHORTEST (start)--+(end) + MATCH p = ANY SHORTEST (start)--{2,}(end) RETURN p } RETURN count(*) AS pathCount @@ -795,28 +795,28 @@ RETURN count(*) AS pathCount .Query Plan [source, role="queryplan"] ---- -+---------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | pathCount | 1 | 1 | 0 | 0 | 0/0 | 0.130 | In Pipeline 1 | -| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------------+ -| +EagerAggregation | 1 | count(*) AS pathCount | 1 | 1 | 0 | 40 | | | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+ | | | -| +Projection | 2 | (start)-[anon_17*]-(end) AS p | 8052 | 19682 | 314930 | | | | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+ | | | -| +ShortestPath | 3 | anon_22 = (start)-[anon_17*]-(end) | 8052 | 19682 | 25496225 | 9699624 | | | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+ | | | -| +MultiNodeIndexSeek | 4 | UNIQUE start:N(trail) WHERE trail = $autolist_0, RANGE INDEX end:N(level) WHERE level = $autoint_1 | 8052 | 19682 | 18658 | 376 | 6428257/0 | 2141.770 | Fused in Pipeline 0 | -+---------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 33321306, total allocated memory: 221424488 ++------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | pathCount | 1 | 1 | 0 | 0 | 0/0 | 0.185 | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+ | +| +EagerAggregation | 1 | count(*) AS pathCount | 1 | 1 | 0 | 40 | 0/0 | 1.571 | In Pipeline 2 | +| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ +| +Projection | 2 | (start)-[anon_15*]-(end) AS p | 8052 | 19682 | 314930 | | 189543/0 | 67.007 | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+ | +| +StatefulShortestPath(Into, Trail) | 3 | SHORTEST 1 (start) ((`anon_11`)-[`anon_12`]-(`anon_13`)){2, } (end) | 8052 | 19682 | 32986690 | 221424672 | 3775866/0 | 22680.410 | In Pipeline 1 | +| | +----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ +| +MultiNodeIndexSeek | 4 | UNIQUE start:N(trail) WHERE trail = $autolist_0, RANGE INDEX end:N(level) WHERE level = $autoint_1 | 8052 | 19683 | 19686 | 376 | 57/0 | 12.979 | In Pipeline 0 | ++------------------------------------+----+----------------------------------------------------------------------------------------------------+----------------+-------+----------+----------------+------------------------+-----------+---------------+ + +Total database accesses: 33321306, total allocated memory: 221425168 1 row -ready to start consuming query after 57 ms, results consumed after another 22870 ms +ready to start consuming query after 49 ms, results consumed after another 22774 ms ---- As the plan shows, in this scenario it is not more efficient to enforce a single source-target node pair. -On the contrary, doing so ensures that the `ShortestPath` operator is executed `19682` times, once for each source-target node pair, thereby generating a more expensive query. +On the contrary, doing so ensures that the `StatefulShortestPath(Into)` operator is executed `19682` times, once for each source-target node pair, thereby generating a more expensive query. =====