diff --git a/modules/ROOT/pages/planning-and-tuning/execution-plans.adoc b/modules/ROOT/pages/planning-and-tuning/execution-plans.adoc index 022232e42..031723c36 100644 --- a/modules/ROOT/pages/planning-and-tuning/execution-plans.adoc +++ b/modules/ROOT/pages/planning-and-tuning/execution-plans.adoc @@ -71,7 +71,7 @@ image::patterns_qpp_solutions.svg[width="700",role="middle"] For the purposes of understanding Cypher execution plans, however, the query result is less interesting than the planning that produces it. -[[runtimes-reading-execution-plans]] +[[reading-execution-plans]] == Reading execution plans The Cypher planner produces logical plans which describe how a particular query is going to be executed. diff --git a/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc b/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc index 4f9d332ed..fb0284afc 100644 --- a/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc +++ b/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc @@ -2,11 +2,26 @@ :page-aliases: execution-plans/operators.adoc = Operators in detail -This page contains details and an example query for all operators available in Cypher. -The operators are grouped by the similarity of their characteristics. +Understanding the roles of different operators in an execution plan can be an important step in making queries more efficient. +This page contains details and examples for each of the operators used by the Cypher planner. +For an overview of how the Cypher planner uses operators, see xref:planning-and-tuning/execution-plans.adoc#reading-execution-plans[ Understanding execution plans -> Reading execution plans]. + +The operators are grouped into categories based on the role they fulfill in executing a Cypher query: + +* xref:planning-and-tuning/operators/operators-detail.adoc#leaf-operators[Leaf operators (scans and seeks)] +* xref:planning-and-tuning/operators/operators-detail.adoc#nested-loops-join-operators[Nested loops and join operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#traversal-operators[Traversal operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#union-operators[Union operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#aggregation-operators[Aggregation operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#filter-order-projection-operators[Filter, order, and projection operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#sort-limit-operators[Sort and limit operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#data-modification-operators[Data modification operators] +* xref:planning-and-tuning/operators/operators-detail.adoc#schema-system-operators[Schema and system operators] + +[NOTE] +Certain operators are only used by particular xref:planning-and-tuning/runtimes/concepts.adoc[runtime]. +If so, the example queries will be prefixed with a runtime selection. -Certain operators are only used by a subset of the xref::planning-and-tuning/runtimes/concepts.adoc[runtimes] that Cypher can choose from. -If that is the case, the example queries will be prefixed with an option to choose one of these runtimes. //// [source, cypher, role=test-setup] @@ -81,9 +96,16 @@ CREATE CONSTRAINT constraint_WORKS_IN_badgeNumber_unique IF NOT EXISTS FOR ()-[r //// +[[leaf-operators]] +== Leaf operators (scans and seeks) + +Leaf operators are the initial operations in a query execution plan that directly access data. They are called "leaf" because they are at the outermost level of the query execution tree, with no operations below them. Once these operators retrieve the necessary data, the results are passed to subsequent operators that perform more complex operations. + +Seek operators target specific data items (indexed or otherwise) while scan operators iterate through the data (indexed or otherwise) to find matches. + + [[query-plan-all-nodes-scan]] -== All Nodes Scan -// AllNodesScan +=== All Nodes Scan The `AllNodesScan` operator reads all nodes from the node store. The variable that will contain the nodes is seen in the arguments. @@ -127,9 +149,7 @@ Total database accesses: 36, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-all-nodes-scan]] -== Partitioned All Nodes Scan -// PartitionedAllNodesScan -// New in 5.17 +=== Partitioned All Nodes Scan The `PartitionedAllNodesScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-all-nodes-scan[`AllNodesScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. It allows the store to be partitioned into different segments where each segment can be scanned independently in parallel. @@ -171,30 +191,22 @@ Total database accesses: 106 ====== -[[query-plan-directed-relationship-index-scan]] -== Directed Relationship Index Scan -// DirectedRelationshipIndexScan - -//// -[source, cypher, role=test-setup] ----- -CREATE RANGE INDEX range_worksin_title FOR ()-[r:WORKS_IN]->() ON (r.title) ----- -//// +[[query-plan-node-by-label-scan]] +=== Node By Label Scan +// NodeByLabelScan -The `DirectedRelationshipIndexScan` operator examines all values stored in an index, returning all relationships and their start and end nodes with a particular relationship type and a specified property. +The `NodeByLabelScan` operator fetches all nodes with a specific label from the node label index. -.DirectedRelationshipIndexScan +.NodeByLabelScan ====== .Query [source, cypher] ---- PROFILE -MATCH ()-[r: WORKS_IN]->() -WHERE r.title IS NOT NULL -RETURN r +MATCH (person:Person) +RETURN person ---- .Query Plan @@ -208,29 +220,28 @@ Runtime version {neo4j-version-minor} Batch size 128 -+--------------------------------+----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------------------+----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 15 | 15 | 0 | | | | | -| | +----------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexScan | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title IS NOT NULL | 15 | 15 | 16 | 120 | 3/1 | 2.464 | Fused in Pipeline 0 | -+--------------------------------+----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | person | 14 | 14 | 0 | | | | | +| | +---------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | person:Person | 14 | 14 | 15 | 120 | 2/1 | 0.522 | Fused in Pipeline 0 | ++------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 16, total allocated memory: 184 +Total database accesses: 15, total allocated memory: 184 ---- ====== [role=label--new-5.17] -[[query-plan-partitioned-directed-relationship-index-scan]] -== Partitioned Directed Relationship Index Scan -// PartitionedDirectedRelationshipIndexScan -// New in 5.17 +[[query-plan-partitioned-node-by-label-scan]] +=== Partitioned Node By Label Scan -The `PartitionedDirectedRelationshipIndexScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-index-scan[`DirectedRelationshipIndexScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +The `PartitionedNodeByLabelScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-by-label-scan[`NodeByLabelScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. -.PartitionedDirectedRelationshipIndexScan + +.PartitionedNodeByLabelScan ====== .Query @@ -238,9 +249,8 @@ It allows the index to be partitioned into different segments where each segment ---- CYPHER runtime=parallel PROFILE -MATCH ()-[r: WORKS_IN]->() -WHERE r.title IS NOT NULL -RETURN r +MATCH (person:Person) +RETURN person ---- .Query Plan @@ -254,36 +264,33 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------------------------------+----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------------------------+----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | r | 15 | 15 | 70 | 1/0 | 2.865 | In Pipeline 1 | -| | +----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +PartitionedDirectedRelationshipIndexScan | 1 | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title IS NOT NULL | 15 | 15 | 16 | 2/0 | 0.527 | In Pipeline 0 | -+-------------------------------------------+----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ ++-----------------------------+----+---------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------+----+---------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | person | 14 | 14 | 28 | 2/0 | 0.623 | In Pipeline 1 | +| | +----+---------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedNodeByLabelScan | 1 | person:Person | 14 | 14 | 15 | 1/0 | 0.094 | In Pipeline 0 | ++-----------------------------+----+---------------+----------------+------+---------+------------------------+-----------+---------------+ -Total database accesses: 86 +Total database accesses: 43 ---- ====== -[[query-plan-undirected-relationship-index-scan]] -== Undirected Relationship Index Scan -// UndirectedRelationshipIndexScan - -The `UndirectedRelationshipIndexScan` operator examines all values stored in an index, returning all relationships and their start and end nodes with a particular relationship type and a specified property. +[role=label--new-5.5] +[[query-plan-intersection-node-by-labels-scan]] +=== Intersection Node By Labels Scan +The `IntersectionNodeByLabelsScan` operator fetches all nodes that have all of the provided labels from the node label index. -.UndirectedRelationshipIndexScan ====== .Query -[source, cypher] +[source,cypher] ---- PROFILE -MATCH ()-[r: WORKS_IN]-() -WHERE r.title IS NOT NULL -RETURN r +MATCH (countryAndLocation:Country&Location) +RETURN countryAndLocation ---- .Query Plan @@ -297,39 +304,36 @@ Runtime version {neo4j-version-minor} Batch size 128 -+----------------------------------+---------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------------+---------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 30 | 30 | 0 | | | | | -| | +---------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipIndexScan | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title IS NOT NULL | 30 | 30 | 16 | 120 | 3/1 | 1.266 | Fused in Pipeline 0 | -+----------------------------------+---------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 16, total allocated memory: 184 ++-------------------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | countryAndLocation | 10 | 0 | 0 | | | | | +| | +----+-------------------------------------+----------------+------+---------+----------------+ | | | +| +IntersectionNodeByLabelsScan | 1 | countryAndLocation:Country&Location | 10 | 0 | 0 | 120 | 0/0 | 1.011 | Fused in Pipeline 0 | ++-------------------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 13, total allocated memory: 184 ---- ====== [role=label--new-5.17] -[[query-plan-partitioned-undirected-relationship-index-scan]] -== Partitioned Undirected Relationship Index Scan -// PartitionedUndirectedRelationshipIndexScan -// New in 5.17 +[[query-plan-partitioned-intersection-node-by-labels-scan]] +=== Partitioned Intersection Node By Labels Scan -The `PartitionedUndirectedRelationshipIndexScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-relationship-index-scan[`UndirectedRelationshipIndexScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +The `PartitionedIntersectionNodeByLabelsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-intersection-node-by-labels-scan[`IntersectionNodeByLabelsScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. -.PartitionedUndirectedRelationshipIndexScan ====== .Query -[source, cypher] +[source,cypher] ---- CYPHER runtime=parallel PROFILE -MATCH ()-[r: WORKS_IN]-() -WHERE r.title IS NOT NULL -RETURN r +MATCH (countryAndLocation:Country&Location) +RETURN countryAndLocation ---- .Query Plan @@ -343,38 +347,36 @@ Runtime version {neo4j-version-minor} Batch size 128 -+---------------------------------------------+----+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------------------------+----+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | r | 30 | 30 | 140 | 1/0 | 3.088 | In Pipeline 1 | -| | +----+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +PartitionedUndirectedRelationshipIndexScan | 1 | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title IS NOT NULL | 30 | 30 | 16 | 2/0 | 0.572 | In Pipeline 0 | -+---------------------------------------------+----+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -Total database accesses: 156 +------------------------------------------+----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------------------+----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | countryAndLocation | 3 | 0 | 0 | 0/0 | 0.018 | In Pipeline 1 | +| | +----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedIntersectionNodeByLabelsScan | 1 | countryAndLocation:Country&Location | 3 | 0 | 13 | 2/0 | 0.770 | In Pipeline 0 | ++------------------------------------------+----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ + +Total database accesses: 13 ---- ====== -[[query-plan-directed-relationship-index-seek]] -== Directed Relationship Index Seek -// DirectedRelationshipIndexSeek - -The `DirectedRelationshipIndexSeek` operator finds relationships and their start and end nodes using an index seek. -The relationship variable and the index used are shown in the arguments of the operator. +[role=label--new-5.21] +[[query-plan-subtraction-node-by-labels-scan]] +=== Subtraction Node By Labels Scan +The `SubtractionNodeByLabelsScan` operator fetches all nodes that have all of the first set of provided labels and none of the second provided set of labels from the node label index. +In the example below, `SubtractionNodeByLabelsScan` retrieves all nodes that have the `Location` label but do not have the `Country` label. -.DirectedRelationshipIndexSeek ====== .Query -[source, cypher] +[source,cypher] ---- PROFILE -MATCH (candidate)-[r:WORKS_IN]->() -WHERE r.title = 'chief architect' -RETURN candidate +MATCH (locationNotCountry:Location&!Country) +RETURN locationNotCountry ---- .Query Plan @@ -388,39 +390,35 @@ Runtime version {neo4j-version-minor} Batch size 128 -+--------------------------------+-----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------------------+-----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | candidate | 2 | 1 | 0 | | | | | -| | +-----------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexSeek | RANGE INDEX (candidate)-[r:WORKS_IN(title)]->(anon_0) WHERE title = $autostring_0 | 2 | 1 | 2 | 120 | 3/1 | 0.591 | Fused in Pipeline 0 | -+--------------------------------+-----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | locationNotCountry | 7 | 10 | 0 | 0 | | | | +| | +----+--------------------------------------+----------------+------+---------+----------------+ | | | +| +SubtractionNodeByLabelsScan | 1 | locationNotCountry:Location&!Country | 7 | 10 | 13 | 248 | 2/0 | 3.081 | Fused in Pipeline 0 | ++------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 2, total allocated memory: 184 +Total database accesses: 13, total allocated memory: 312 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-directed-relationship-index-seek]] -== Partitioned Directed Relationship Index Seek -// PartitionedDirectedRelationshipIndexSeek -// New in 5.17 +[role=label--new-5.21] +[[query-plan-partitioned-subtraction-node-by-labels-scan]] +=== Partitioned Subtraction Node By Labels Scan -The `PartitionedDirectedRelationshipIndexSeek` is a variant of the the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-index-seek[`DirectedRelationshipIndexSeek`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +The `PartitionedSubtractionNodeByLabelsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-subtraction-node-by-labels-scan[`SubtractionNodeByLabelsScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. -.PartitionedDirectedRelationshipIndexSeek ====== .Query -[source, cypher] +[source,cypher] ---- CYPHER runtime=parallel PROFILE -MATCH (candidate)-[r:WORKS_IN]->() -WHERE r.title = 'chief architect' -RETURN candidate +MATCH (locationNotCountry:Location&!Country) +RETURN locationNotCountry ---- .Query Plan @@ -434,38 +432,32 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------------------------------+----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------------------------+----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | candidate | 2 | 1 | 2 | 2/0 | 0.284 | In Pipeline 1 | -| | +----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +PartitionedDirectedRelationshipIndexSeek | 1 | RANGE INDEX (candidate)-[r:WORKS_IN(title)]->(anon_0) WHERE title = $autostring_0 | 2 | 1 | 2 | 2/0 | 0.148 | In Pipeline 0 | -+-------------------------------------------+----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ ++-----------------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | locationNotCountry | 7 | 10 | 0 | 136 | 0/0 | 0.614 | In Pipeline 1 | +| | +----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +PartitionedSubtractionNodeByLabelsScan | 1 | locationNotCountry:Location&!Country | 7 | 10 | 13 | 120 | 2/0 | 5.173 | In Pipeline 0 | ++-----------------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -Total database accesses: 4 +Total database accesses: 13, total allocated memory: 262144 ---- ====== +[[query-plan-union-node-by-labels-scan]] +=== Union Node By Labels Scan -[[query-plan-undirected-relationship-index-seek]] -== Undirected Relationship Index Seek -// UndirectedRelationshipIndexSeek - -The `UndirectedRelationshipIndexSeek` operator finds relationships and their start and end nodes using an index seek. -The relationship variable and the index used are shown in the arguments of the operator. - +The `UnionNodeByLabelsScan` operator fetches all nodes that have at least one of the provided labels from the node label index. -.UndirectedRelationshipIndexSeek ====== .Query -[source, cypher] +[source,cypher] ---- PROFILE -MATCH (candidate)-[r:WORKS_IN]-() -WHERE r.title = 'chief architect' -RETURN candidate +MATCH (countryOrLocation:Country|Location) +RETURN countryOrLocation ---- .Query Plan @@ -479,38 +471,35 @@ Runtime version {neo4j-version-minor} Batch size 128 -+----------------------------------+----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------------+----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | candidate | 4 | 2 | 0 | | | | | -| | +----------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipIndexSeek | RANGE INDEX (candidate)-[r:WORKS_IN(title)]-(anon_0) WHERE title = $autostring_0 | 4 | 2 | 2 | 120 | 3/1 | 0.791 | Fused in Pipeline 0 | -+----------------------------------+----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| +ProduceResults | countryOrLocation | 17 | 11 | 0 | | | | | | +| | +------------------------------------+----------------+------+---------+----------------+ | | | | +| +UnionNodeByLabelsScan | countryOrLocation:Country|Location | 17 | 11 | 13 | 120 | 3/1 | 0.660 | countryOrLocation ASC | Fused in Pipeline 0 | ++------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -Total database accesses: 2, total allocated memory: 184 +Total database accesses: 13, total allocated memory: 184 ---- ====== -[[query-plan-partitioned-undirected-relationship-index-seek]] -== Partitioned Undirected Relationship Index Seek -// PartitionedUndirectedRelationshipIndexSeek - -The `PartitionedUndirectedRelationshipIndexSeek` operator finds relationships and their start and end nodes using an index seek. -The relationship variable and the index used are shown in the arguments of the operator. +[role=label--new-5.17] +[[query-plan-partitioned-union-node-by-labels-scan]] +=== Partitioned Union Node By Labels Scan +The `PartitionedUnionNodeByLabelsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-union-node-by-labels-scan[`UnionNodeByLabelsScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. -.PartitionedUndirectedRelationshipIndexSeek ====== .Query -[source, cypher] +[source,cypher] ---- CYPHER runtime=parallel PROFILE -MATCH (candidate)-[r:WORKS_IN]-() -WHERE r.title = 'chief architect' -RETURN candidate +MATCH (countryOrLocation:Country|Location) +RETURN countryOrLocation ---- .Query Plan @@ -524,37 +513,42 @@ Runtime version {neo4j-version-minor} Batch size 128 -+---------------------------------------------+----+----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------------------------+----+----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | candidate | 4 | 2 | 4 | 2/0 | 0.333 | In Pipeline 1 | -| | +----+----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +PartitionedUndirectedRelationshipIndexSeek | 1 | RANGE INDEX (candidate)-[r:WORKS_IN(title)]-(anon_0) WHERE title = $autostring_0 | 4 | 2 | 2 | 2/0 | 0.151 | In Pipeline 0 | -+---------------------------------------------+----+----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ - -Total database accesses: 6 ----- - -====== - ++-----------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | countryOrLocation | 17 | 11 | 22 | 2/0 | 1.548 | In Pipeline 1 | +| | +----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedUnionNodeByLabelsScan | 1 | countryOrLocation:Country|Location | 17 | 11 | 13 | 2/0 | 1.976 | In Pipeline 0 | ++-----------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -[[query-plan-directed-relationship-by-element-id-seek]] -== Directed Relationship By Element Id Seek -// DirectedRelationshipByElementIdSeek +Total database accesses: 35 +---- -The `DirectedRelationshipByElementIdSeek` operator reads one or more relationships by element id from the relationship store (specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]) and produces the relationship as well as the source and target node of the relationship. +====== +[[query-plan-node-index-contains-scan]] +=== Node Index Contains Scan -.DirectedRelationshipByElementIdSeek +//// +[source, cypher, role=test-setup] +---- +CREATE TEXT INDEX text_location_name FOR (l:Location) ON (l.name) +---- +//// + +The `NodeIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific `STRING`; for example, in queries including `CONTAINS`. +Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label scan using `NodeByLabelScan`, and a property store filter. + +.NodeIndexContainsScan ====== .Query [source, cypher] ---- PROFILE -MATCH (n1)-[r]->() -WHERE elementId(r) = 0 -RETURN r, n1 +MATCH (l:Location) +WHERE l.name CONTAINS 'al' +RETURN l ---- .Query Plan @@ -568,36 +562,37 @@ Runtime version {neo4j-version-minor} Batch size 128 -+--------------------------------------+----+----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------------------------+----+----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | r, n1 | 1 | 0 | 0 | 0 | 0/0 | 0.314 | | -| | +----+----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +DirectedRelationshipByElementIdSeek | 1 | (n1)-[r]->(anon_0) WHERE elementId(r) = $autoint_0 | 1 | 0 | 0 | 248 | 0/0 | 2.337 | In Pipeline 0 | -+--------------------------------------+----+----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ ++------------------------+---------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------+---------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | l | 0 | 2 | 0 | | | | | +| | +---------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexContainsScan | TEXT INDEX l:Location(name) WHERE name CONTAINS $autostring_0 | 0 | 2 | 3 | 120 | 2/0 | 1.305 | Fused in Pipeline 0 | ++------------------------+---------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 0, total allocated memory: 312 +Total database accesses: 3, total allocated memory: 184 ---- ====== -[[query-plan-directed-relationship-by-id-seek]] -== Directed Relationship By Id Seek -// DirectedRelationshipByIdSeek -The `DirectedRelationshipByIdSeek` operator reads one or more relationships by id from the relationship store, and produces the relationship as well as the source and target node of the relationship. +[[query-plan-node-index-ends-with-scan]] +=== Node Index Ends With Scan + +The `NodeIndexEndsWithScan` operator examines all values stored in an index, searching for entries ending in a specific `STRING`; for example, in queries containing `ENDS WITH`. +Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label scan using `NodeByLabelScan`, and a property store filter. -.DirectedRelationshipByIdSeek +.NodeIndexEndsWithScan ====== .Query [source, cypher] ---- PROFILE -MATCH (n1)-[r]->() -WHERE id(r) = 0 -RETURN r, n1 +MATCH (l:Location) +WHERE l.name ENDS WITH 'al' +RETURN l ---- .Query Plan @@ -611,37 +606,43 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------------------+----+---------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------------+----+---------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | r, n1 | 1 | 1 | 7 | 0 | | | | -| | +----+---------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipByIdSeek | 1 | (n1)-[r]->(anon_0) WHERE id(r) = $autoint_0 | 1 | 1 | 1 | 248 | 3/0 | 0.483 | Fused in Pipeline 0 | -+-------------------------------+----+---------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | l | 0 | 0 | 0 | | | | | +| | +----------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexEndsWithScan | TEXT INDEX l:Location(name) WHERE name ENDS WITH $autostring_0 | 0 | 0 | 1 | 120 | 0/0 | 4.409 | Fused in Pipeline 0 | ++------------------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 8, total allocated memory: 312 +Total database accesses: 1, total allocated memory: 184 ---- ====== -[[query-plan-undirected-relationship-by-element-id-seek]] -== Undirected Relationship By Element Id Seek -// UndirectedRelationshipByElementIdSeek -The `UndirectedRelationshipByElementIdSeek` operator reads one or more relationships by element id from the relationship store (specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]). -As the direction is unspecified, two rows are produced for each relationship as a result of alternating the combination of the start and end node. +[[query-plan-node-index-scan]] +=== Node Index Scan -.UndirectedRelationshipByElementIdSeek +The `NodeIndexScan` operator examines all values stored in an index, returning all nodes with a particular label and a specified property. + +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX range_location_name FOR (l:Location) ON (l.name) +---- +//// + +.NodeIndexScan ====== .Query [source, cypher] ---- PROFILE -MATCH (n1)-[r]-() -WHERE elementId(r) = 1 -RETURN r, n1 +MATCH (l:Location) +WHERE l.name IS NOT NULL +RETURN l ---- .Query Plan @@ -655,37 +656,37 @@ Runtime version {neo4j-version-minor} Batch size 128 -+---------------------------------------+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------------------+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r, n1 | 2 | 2 | 0 | | | | | -| | +--------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipByElementIdSeek| (n1)-[r]-(anon_0) WHERE elementId(r) = $autoint_0 | 2 | 2 | 1 | 120 | 4/0 | 0.332 | Fused in Pipeline 0 | -+---------------------------------+--------------------------------------------+-----+---------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | l | 10 | 10 | 0 | | | | | +| | +-----------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexScan | RANGE INDEX l:Location(name) WHERE name IS NOT NULL | 10 | 10 | 11 | 120 | 2/1 | 0.557 | Fused in Pipeline 0 | ++-----------------+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 1, total allocated memory: 184 +Total database accesses: 11, total allocated memory: 184 ---- ====== -[[query-plan-undirected-relationship-by-id-seek]] -== Undirected Relationship By Id Seek -// UndirectedRelationshipByIdSeek - -The `UndirectedRelationshipByIdSeek` operator reads one or more relationships by id from the relationship store (specified via the function xref::functions/scalar.adoc#functions-id[Id()]). -As the direction is unspecified, two rows are produced for each relationship as a result of alternating the combination of the start and end node. +[role=label--new-5.17] +[[query-plan-partitioned-node-index-scan]] +=== Partitioned Node Index Scan +The `PartitionedNodeIndexScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-scan[`NodeIndexScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. -.UndirectedRelationshipByIdSeek +.PartitionedNodeIndexScan ====== .Query [source, cypher] ---- +CYPHER runtime=parallel PROFILE -MATCH (n1)-[r]-() -WHERE id(r) = 1 -RETURN r, n1 +MATCH (l:Location) +WHERE l.name IS NOT NULL +RETURN l ---- .Query Plan @@ -693,51 +694,42 @@ RETURN r, n1 ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL Runtime version {neo4j-version-minor} Batch size 128 -+---------------------------------+----+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------------+----+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | r, n1 | 2 | 2 | 14 | 0 | | | | -| | +----+--------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipByIdSeek | 1 | (n1)-[r]-(anon_0) WHERE id(r) = $autoint_0 | 2 | 2 | 1 | 248 | 3/0 | 1.005 | Fused in Pipeline 0 | -+---------------------------------+----+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------------+----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------+----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | l | 1 | 10 | 20 | 2/0 | 0.472 | In Pipeline 1 | +| | +----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedNodeIndexScan | 1 | RANGE INDEX l:Location(name) WHERE name IS NOT NULL | 1 | 10 | 11 | 1/0 | 0.187 | In Pipeline 0 | ++---------------------------+----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -Total database accesses: 15, total allocated memory: 312 +Total database accesses: 31 ---- ====== -[[query-plan-directed-relationship-index-contains-scan]] -== Directed Relationship Index Contains Scan -// DirectedRelationshipIndexContainsScan - -//// -[source, cypher, role=test-setup] ----- -CREATE TEXT INDEX text_worksin_title FOR ()-[r:WORKS_IN]->() ON (r.title) ----- -//// - -The `DirectedRelationshipIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific `STRING`; for example, in queries including `CONTAINS`. -Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a type scan using `DirectedRelationshipTypeScan`, and a property store filter. +[role=label--new-5.3] +[[query-plan-node-by-elementid-seek]] +=== Node By ElementId Seek +The `NodeByElementIdSeek` operator reads one or more nodes by ID from the node store, specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]. -.DirectedRelationshipIndexContainsScan +.NodeByElementIdSeek ====== .Query [source, cypher] ---- PROFILE -MATCH ()-[r: WORKS_IN]->() -WHERE r.title CONTAINS 'senior' -RETURN r +MATCH (n) +WHERE elementId(n) = 0 +RETURN n ---- .Query Plan @@ -751,38 +743,35 @@ Runtime version {neo4j-version-minor} Batch size 128 -+----------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 0 | 4 | 0 | | | | | -| | +--------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexContainsScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title CONTAINS $autostring_0 | 0 | 4 | 5 | 120 | 3/0 | 1.051 | Fused in Pipeline 0 | -+----------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | n | 1 | 1 | 0 | | | | | +| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------- | | | +| +NodeByElementIdSeek | n WHERE elementId(n) = $autoint_0 | 1 | 1 | 1 | 120 | 3/0 | 2.108 | Fused in Pipeline 0 | ++------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 5, total allocated memory: 184 +Total database accesses: 1, total allocated memory: 184 ---- ====== +[[query-plan-node-by-id-seek]] +=== Node By Id Seek -[[query-plan-undirected-relationship-index-contains-scan]] -== Undirected Relationship Index Contains Scan -// UndirectedRelationshipIndexContainsScan - -The `UndirectedRelationshipIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific `STRING`; for example, in queries including `CONTAINS`. -Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a type scan using `DirectedRelationshipTypeScan`, and a property store filter. +The `NodeByIdSeek` operator reads one or more nodes by id from the node store, specified via the function xref::functions/scalar.adoc#functions-id[id()]. -.UndirectedRelationshipIndexContainsScan +.NodeByIdSeek ====== .Query [source, cypher] ---- PROFILE -MATCH ()-[r: WORKS_IN]-() -WHERE r.title CONTAINS 'senior' -RETURN r +MATCH (n) +WHERE id(n) = 0 +RETURN n ---- .Query Plan @@ -796,38 +785,38 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 0 | 8 | 0 | | | | | -| | +-------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipIndexContainsScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title CONTAINS $autostring_0 | 0 | 8 | 5 | 120 | 3/0 | 2.684 | Fused in Pipeline 0 | -+------------------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | n | 1 | 1 | 2 | 0 | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +NodeByIdSeek | 1 | n WHERE id(n) = $autoint_0 | 1 | 1 | 1 | 248 | 2/0 | 1.109 | Fused in Pipeline 0 | ++-----------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 3, total allocated memory: 312 -Total database accesses: 5, total allocated memory: 184 ---- ====== -[[query-plan-directed-relationship-index-ends-with-scan]] -== Directed Relationship Index Ends With Scan -// DirectedRelationshipIndexEndsWithScan +[[query-plan-node-index-seek]] +=== Node Index Seek -The `DirectedRelationshipIndexEndsWithScan` operator examines all values stored in an index, searching for entries ending in a specific `STRING`; for example, in queries containing `ENDS WITH`. -Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label scan using `NodeByLabelScan`, and a property store filter. +The `NodeIndexSeek` operator finds nodes using an index seek. +The node variable and the index used are shown in the arguments of the operator. +If the index is backs up a xref:constraints/managing-constraints.adoc#create-property-uniqueness-constraints[property uniqueness constraint], the operator is instead called xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-unique-index-seek[NodeUniqueIndexSeek]. -.DirectedRelationshipIndexEndsWithScan +.NodeIndexSeek ====== .Query [source, cypher] ---- PROFILE -MATCH ()-[r: WORKS_IN]->() -WHERE r.title ENDS WITH 'developer' -RETURN r +MATCH (location:Location {name: 'Malmo'}) +RETURN location ---- .Query Plan @@ -841,38 +830,37 @@ Runtime version {neo4j-version-minor} Batch size 128 -+----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 0 | 8 | 0 | | | | | -| | +---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexEndsWithScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title ENDS WITH $autostring_0 | 0 | 8 | 9 | 120 | 3/0 | 1.887 | Fused in Pipeline 0 | -+----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | location | 1 | 1 | 0 | | | | | +| | +----------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | RANGE INDEX location:Location(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 2/1 | 0.401 | Fused in Pipeline 0 | ++-----------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 9, total allocated memory: 184 +Total database accesses: 2, total allocated memory: 184 ---- ====== +[role=label--new-5.17] +[[query-plan-partitioned-node-index-seek]] +=== Partitioned Node Index Seek -[[query-plan-undirected-relationship-index-ends-with-scan]] -== Undirected Relationship Index Ends With Scan -// UndirectedRelationshipIndexEndsWithScan +The `PartitionedNodeIndexSeek` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-seek[`NodeIndexSeek`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. -The `UndirectedRelationshipIndexEndsWithScan` operator examines all values stored in an index, searching for entries ending in a specific `STRING`; for example, in queries containing `ENDS WITH`. -Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label scan using `NodeByLabelScan`, and a property store filter. - -.UndirectedRelationshipIndexEndsWithScan -====== +.PartitionedNodeIndexSeek +====== .Query [source, cypher] ---- +CYPHER runtime=parallel PROFILE -MATCH ()-[r: WORKS_IN]-() -WHERE r.title ENDS WITH 'developer' -RETURN r +MATCH (location:Location {name: 'Malmo'}) +RETURN location ---- .Query Plan @@ -880,51 +868,51 @@ RETURN r ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL Runtime version {neo4j-version-minor} Batch size 128 -+------------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | r | 0 | 16 | 0 | | | | | -| | +--------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipIndexEndsWithScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title ENDS WITH $autostring_0 | 0 | 16 | 9 | 120 | 3/0 | 1.465 | Fused in Pipeline 0 | -+------------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------------+----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------+----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | location | 0 | 1 | 2 | 2/0 | 0.179 | In Pipeline 1 | +| | +----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedNodeIndexSeek | 1 | RANGE INDEX location:Location(name) WHERE name = $autostring_0 | 0 | 1 | 2 | 1/0 | 0.167 | In Pipeline 0 | ++---------------------------+----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -Total database accesses: 9, total allocated memory: 184 +Total database accesses: 4 ---- ====== - -[[query-plan-directed-relationship-index-seek-by-range]] -== Directed Relationship Index Seek By Range -// DirectedRelationshipIndexSeekByRange +[[query-plan-node-unique-index-seek]] +=== Node Unique Index Seek //// [source, cypher, role=test-setup] ---- -CREATE RANGE INDEX range_worksin_duration FOR ()-[r:WORKS_IN]->() ON (r.duration) +CREATE CONSTRAINT team_name IF NOT EXISTS FOR (t:Team) REQUIRE (t.name) IS UNIQUE ---- //// -The `DirectedRelationshipIndexSeekByRange` operator finds relationships and their start and end nodes using an index seek where the value of the property matches a given prefix `STRING`. -`DirectedRelationshipIndexSeekByRange` can be used for `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+` and `+>=+`. +The `NodeUniqueIndexSeek` operator finds nodes using an index seek within an index backing up a xref:constraints/managing-constraints.adoc#create-property-uniqueness-constraints[property uniqueness constraint]. +The node variable and the index used are shown in the arguments of the operator. +If the index does not back up a property uniqueness constraint, the operator is instead called xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-seek[NodeIndexSeek]. +If the index seek is used to solve a xref::clauses/merge.adoc[MERGE] clause, it will also be marked with `(Locking)`. +This makes it clear that any nodes returned from the index will be locked in order to prevent concurrent conflicting updates. -.DirectedRelationshipIndexSeekByRange +.NodeUniqueIndexSeek ====== .Query [source, cypher] ---- PROFILE -MATCH (candidate: Person)-[r:WORKS_IN]->(location) -WHERE r.duration > 100 -RETURN candidate +MATCH (t:Team {name: 'Malmo'}) +RETURN t ---- .Query Plan @@ -938,41 +926,50 @@ Runtime version {neo4j-version-minor} Batch size 128 -+---------------------------------------+----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------------------+----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | candidate | 4 | 15 | 0 | | | | | -| | +----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | candidate:Person | 4 | 15 | 30 | | | | | -| | +----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DirectedRelationshipIndexSeekByRange | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]->(location) WHERE duration > $autoint_0 | 4 | 15 | 16 | 120 | 4/1 | 0.703 | Fused in Pipeline 0 | -+---------------------------------------+----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++----------------------+------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------+------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | t | 1 | 0 | 0 | | | | | +| | +------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeUniqueIndexSeek | UNIQUE t:Team(name) WHERE name = $autostring_0 | 1 | 0 | 1 | 120 | 0/1 | 0.280 | Fused in Pipeline 0 | ++----------------------+------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 46, total allocated memory: 184 +Total database accesses: 1, total allocated memory: 184 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-directed-relationship-index-seek-by-range]] -== Partitioned Directed Relationship Index Seek By Range -// PartitionedDirectedRelationshipIndexSeekByRange -// New in 5.17 -The `PartitionedDirectedRelationshipIndexSeekByRange` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-index-seek-by-range[`DirectedRelationshipIndexSeekByRange`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. -It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. +[[query-plan-multi-node-index-seek]] +=== Multi Node Index Seek -.PartitionedDirectedRelationshipIndexSeekByRange +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX range_person_name FOR (p:Person) ON (p.name) +---- +//// + +The `MultiNodeIndexSeek` operator finds nodes using multiple index seeks. +It supports using multiple distinct indexes for different nodes in the query. +The node variables and the indexes used are shown in the arguments of the operator. + +The operator yields a cartesian product of all index seeks. +For example, if the operator does two seeks and the first seek finds the nodes `a1, a2` and the second `b1, b2, b3`, the `MultiNodeIndexSeek` will yield the rows `(a1, b1), (a1, b2), (a1, b3), (a2, b1), (a2, b2), (a2, b3)`. + + +.MultiNodeIndexSeek ====== .Query [source, cypher] ---- -CYPHER runtime=parallel PROFILE -MATCH (candidate: Person)-[r:WORKS_IN]->(location) -WHERE r.duration > 100 -RETURN candidate +CYPHER runtime=pipelined +MATCH + (location:Location {name: 'Malmo'}), + (person:Person {name: 'Bob'}) +RETURN location, person ---- .Query Plan @@ -980,46 +977,50 @@ RETURN candidate ---- Planner COST -Runtime PARALLEL +Runtime PIPELINED Runtime version {neo4j-version-minor} Batch size 128 -+--------------------------------------------------+----+----------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------------------------------------+----+----------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | candidate | 4 | 15 | 30 | 1/0 | 1.031 | In Pipeline 1 | -| | +----+----------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| +Filter | 1 | candidate:Person | 4 | 15 | 30 | | | | -| | +----+----------------------------------------------------------------------------------------+----------------+------+---------+ | | | -| +PartitionedDirectedRelationshipIndexSeekByRange | 2 | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]->(location) WHERE duration > $autoint_0 | 4 | 15 | 16 | 3/0 | 0.203 | Fused in Pipeline 0 | -+--------------------------------------------------+----+----------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ ++---------------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | location, person | 1 | 1 | 0 | | | | | +| | +-----------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +MultiNodeIndexSeek | RANGE INDEX location:Location(name) WHERE name = $autostring_0, | 1 | 0 | 0 | 120 | 2/2 | 1.910 | Fused in Pipeline 0 | +| | RANGE INDEX person:Person(name) WHERE name = $autostring_1 | | | | | | | | ++---------------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 76 +Total database accesses: 0, total allocated memory: 184 ---- ====== -[[query-plan-undirected-relationship-index-seek-by-range]] -== Undirected Relationship Index Seek By Range -// UndirectedRelationshipIndexSeekByRange +[[query-plan-asserting-multi-node-index-seek]] +=== Asserting Multi Node Index Seek -The `UndirectedRelationshipIndexSeekByRange` operator finds relationships and their start and end nodes using an index seek where the value of the property matches a given prefix `STRING`. -`UndirectedRelationshipIndexSeekByRange` can be used for `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+` and `+>=+`. +//// +[source, cypher, role=test-setup] +---- +CREATE CONSTRAINT team_id IF NOT EXISTS FOR (t:Team) REQUIRE (t.id) IS UNIQUE +---- +//// +The `AssertingMultiNodeIndexSeek` operator is used to ensure that no xref:constraints/managing-constraints.adoc#create-property-uniqueness-constraints[property uniqueness constraints] are violated. +The example looks for the presence of a team with the supplied name and id, and if one does not exist, it will be created. +Owing to the existence of two property uniqueness constraints on `:Team(name)` and `:Team(id)`, any node that would be found by the `UniqueIndexSeek` operator must be the very same node or the constraints would be violated. -.UndirectedRelationshipIndexSeekByRange + +.AssertingMultiNodeIndexSeek ====== .Query [source, cypher] ---- PROFILE -MATCH (candidate: Person)-[r:WORKS_IN]-(location) -WHERE r.duration > 100 -RETURN candidate +MERGE (t:Team {name: 'Engineering', id: 42}) ---- .Query Plan @@ -1033,41 +1034,42 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | candidate | 5 | 15 | 0 | | | | | -| | +---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | candidate:Person | 5 | 15 | 60 | | | | | -| | +---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +UndirectedRelationshipIndexSeekByRange | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]-(location) WHERE duration > $autoint_0 | 8 | 30 | 16 | 120 | 4/1 | 1.214 | Fused in Pipeline 0 | -+-----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------------------+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | | 1 | 0 | 0 | | | | | +| | +-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +EmptyResult | | 1 | 0 | 0 | | | | | +| | +-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Merge | CREATE (t:Team {name: $autostring_0, id: $autoint_1}) | 1 | 1 | 0 | | | | | +| | +-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +AssertingMultiNodeIndexSeek | UNIQUE t:Team(name) WHERE name = $autostring_0, UNIQUE t:Team(id) WHERE id = $autoint_1 | 0 | 2 | 4 | 120 | 0/2 | 1.584 | Fused in Pipeline 0 | ++------------------------------+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 76, total allocated memory: 184 +Total database accesses: 4, total allocated memory: 184 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-undirected-relationship-index-seek-by-range]] -== Partitioned Undirected Relationship Index Seek By Range -// PartitionedUndirectedRelationshipIndexSeekByRange -// New in 5.17 -The `PartitionedUndirectedRelationshipIndexSeekByRange` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-relationship-index-seek-by-range[`UndirectedRelationshipIndexSeekByRange`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. -It allows the store to be partitioned into different segments where each segment can be scanned independently in parallel. +[[query-plan-node-index-seek-by-range]] +=== Node Index Seek By Range -.PartitionedUndirectedRelationshipIndexSeekByRange +The `NodeIndexSeekByRange` operator finds nodes using an index seek where the value of the property matches a given prefix `STRING`. +`NodeIndexSeekByRange` can be used for `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+` and `+>=+`. +If the index is a unique index, the operator is instead called `NodeUniqueIndexSeekByRange`. + + +.NodeIndexSeekByRange ====== .Query [source, cypher] ---- -CYPHER runtime=parallel PROFILE -MATCH (candidate: Person)-[r:WORKS_IN]-(location) -WHERE r.duration > 100 -RETURN candidate +MATCH (l:Location) +WHERE l.name STARTS WITH 'Lon' +RETURN l ---- .Query Plan @@ -1075,42 +1077,44 @@ RETURN candidate ---- Planner COST -Runtime PARALLEL +Runtime PIPELINED Runtime version {neo4j-version-minor} Batch size 128 -+----------------------------------------------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------------------------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | candidate | 5 | 15 | 30 | 1/0 | 0.918 | In Pipeline 1 | -| | +----+---------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| +Filter | 1 | candidate:Person | 5 | 15 | 60 | | | | -| | +----+---------------------------------------------------------------------------------------+----------------+------+---------+ | | | -| +PartitionedUndirectedRelationshipIndexSeekByRange | 2 | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]-(location) WHERE duration > $autoint_0 | 8 | 30 | 16 | 3/0 | 0.413 | Fused in Pipeline 0 | -+----------------------------------------------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ ++-----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | l | 2 | 1 | 0 | | | | | +| | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeekByRange | RANGE INDEX l:Location(name) WHERE name STARTS WITH $autostring_0 | 2 | 1 | 2 | 120 | 3/0 | 0.825 | Fused in Pipeline 0 | ++-----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 106 +Total database accesses: 2, total allocated memory: 184 ---- ====== +[role=label--new-5.17] +[[query-plan-partitioned-node-index-seek-by-range]] +=== Partitioned Node Index Seek By Range -[[query-plan-union-node-by-labels-scan]] -== Union Node By Labels Scan -// UnionNodeByLabelsScan -// New in 5.0 +The `PartitionedNodeIndexSeekByRange` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-seek-by-range[`NodeIndexSeekByRange`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. -The `UnionNodeByLabelsScan` operator fetches all nodes that have at least one of the provided labels from the node label index. + +.PartitionedNodeIndexSeekByRange ====== .Query -[source,cypher] +[source, cypher] ---- +CYPHER runtime=parallel PROFILE -MATCH (countryOrLocation:Country|Location) -RETURN countryOrLocation +MATCH (l:Location) +WHERE l.name STARTS WITH 'Lon' +RETURN l ---- .Query Plan @@ -1118,42 +1122,44 @@ RETURN countryOrLocation ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL Runtime version {neo4j-version-minor} Batch size 128 -+------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| +ProduceResults | countryOrLocation | 17 | 11 | 0 | | | | | | -| | +------------------------------------+----------------+------+---------+----------------+ | | | | -| +UnionNodeByLabelsScan | countryOrLocation:Country|Location | 17 | 11 | 13 | 120 | 3/1 | 0.660 | countryOrLocation ASC | Fused in Pipeline 0 | -+------------------------+------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ ++----------------------------------+----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------+----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | l | 0 | 1 | 2 | 2/0 | 0.191 | In Pipeline 1 | +| | +----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedNodeIndexSeekByRange | 1 | RANGE INDEX l:Location(name) WHERE name STARTS WITH $autostring_0 | 0 | 1 | 2 | 1/0 | 0.087 | In Pipeline 0 | ++----------------------------------+----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -Total database accesses: 13, total allocated memory: 184 +Total database accesses: 4 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-union-node-by-labels-scan]] -== Partitioned Union Node By Labels Scan -// PartitionedUnionNodeByLabelsScan -// New in 5.17 -The `PartitionedUnionNodeByLabelsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-union-node-by-labels-scan[`UnionNodeByLabelsScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. -It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. +[[query-plan-node-unique-index-seek-by-range]] +=== Node Unique Index Seek By Range + +The `NodeUniqueIndexSeekByRange` operator finds nodes using an index seek within a unique index, where the value of the property matches a given prefix `STRING`. +`NodeUniqueIndexSeekByRange` is used by `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+`, and `+>=+`. +If the index is not unique, the operator is instead called `NodeIndexSeekByRange`. + + +.NodeUniqueIndexSeekByRange ====== .Query -[source,cypher] +[source, cypher] ---- -CYPHER runtime=parallel PROFILE -MATCH (countryOrLocation:Country|Location) -RETURN countryOrLocation +MATCH (t:Team) +WHERE t.name STARTS WITH 'Ma' +RETURN t ---- .Query Plan @@ -1161,199 +1167,27 @@ RETURN countryOrLocation ---- Planner COST -Runtime PARALLEL +Runtime PIPELINED Runtime version {neo4j-version-minor} Batch size 128 -+-----------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | countryOrLocation | 17 | 11 | 22 | 2/0 | 1.548 | In Pipeline 1 | -| | +----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +PartitionedUnionNodeByLabelsScan | 1 | countryOrLocation:Country|Location | 17 | 11 | 13 | 2/0 | 1.976 | In Pipeline 0 | -+-----------------------------------+----+------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ - -Total database accesses: 35 ----- - -====== - - -[role=label--new-5.5] -[[query-plan-intersection-node-by-labels-scan]] -== Intersection Node By Labels Scan -// IntersectionNodeByLabelsScan - -The `IntersectionNodeByLabelsScan` operator fetches all nodes that have all of the provided labels from the node label index. -====== - -.Query -[source,cypher] ----- -PROFILE -MATCH (countryAndLocation:Country&Location) -RETURN countryAndLocation ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - - -+-------------------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | countryAndLocation | 10 | 0 | 0 | | | | | -| | +----+-------------------------------------+----------------+------+---------+----------------+ | | | -| +IntersectionNodeByLabelsScan | 1 | countryAndLocation:Country&Location | 10 | 0 | 0 | 120 | 0/0 | 1.011 | Fused in Pipeline 0 | -+-------------------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 13, total allocated memory: 184 ----- - -====== - -[role=label--new-5.17] -[[query-plan-partitioned-intersection-node-by-labels-scan]] -== Partitioned Intersection Node By Labels Scan -// PartitionedIntersectionNodeByLabelsScan -// New in 5.17 - -The `PartitionedIntersectionNodeByLabelsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-intersection-node-by-labels-scan[`IntersectionNodeByLabelsScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. -It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. -====== - -.Query -[source,cypher] ----- -CYPHER runtime=parallel -PROFILE -MATCH (countryAndLocation:Country&Location) -RETURN countryAndLocation ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PARALLEL - -Runtime version {neo4j-version-minor} - -Batch size 128 - - -------------------------------------------+----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------------------------+----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | countryAndLocation | 3 | 0 | 0 | 0/0 | 0.018 | In Pipeline 1 | -| | +----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +PartitionedIntersectionNodeByLabelsScan | 1 | countryAndLocation:Country&Location | 3 | 0 | 13 | 2/0 | 0.770 | In Pipeline 0 | -+------------------------------------------+----+-------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ - -Total database accesses: 13 ----- - -====== - -[role=label--new-5.21] -[[query-plan-subtraction-node-by-labels-scan]] -== Subtraction Node By Labels Scan -// SubtractionNodeByLabelsScan -// New in 5.21 - -The `SubtractionNodeByLabelsScan` operator fetches all nodes that have all of the first set of provided labels and none of the second provided set of labels from the node label index. -In the example below, `SubtractionNodeByLabelsScan` retrieves all nodes that have the `Location` label but do not have the `Country` label. -====== - -.Query -[source,cypher] ----- -PROFILE -MATCH (locationNotCountry:Location&!Country) -RETURN locationNotCountry ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | locationNotCountry | 7 | 10 | 0 | 0 | | | | -| | +----+--------------------------------------+----------------+------+---------+----------------+ | | | -| +SubtractionNodeByLabelsScan | 1 | locationNotCountry:Location&!Country | 7 | 10 | 13 | 248 | 2/0 | 3.081 | Fused in Pipeline 0 | -+------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 13, total allocated memory: 312 ----- - -====== - -[role=label--new-5.21] -[[query-plan-partitioned-subtraction-node-by-labels-scan]] -== Partitioned Subtraction Node By Labels Scan -// PartitionedSubtractionNodeByLabelsScan -// New in 5.21 - -The `PartitionedSubtractionNodeByLabelsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-subtraction-node-by-labels-scan[`SubtractionNodeByLabelsScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. -It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. -====== - -.Query -[source,cypher] ----- -CYPHER runtime=parallel -PROFILE -MATCH (locationNotCountry:Location&!Country) -RETURN locationNotCountry ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PARALLEL - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-----------------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | locationNotCountry | 7 | 10 | 0 | 136 | 0/0 | 0.614 | In Pipeline 1 | -| | +----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +PartitionedSubtractionNodeByLabelsScan | 1 | locationNotCountry:Location&!Country | 7 | 10 | 13 | 120 | 2/0 | 5.173 | In Pipeline 0 | -+-----------------------------------------+----+--------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ ++-----------------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | t | 2 | 0 | 0 | | | | | +| | +----------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeUniqueIndexSeekByRange | UNIQUE t:Team(name) WHERE name STARTS WITH $autostring_0 | 2 | 0 | 1 | 120 | 1/0 | 0.623 | Fused in Pipeline 0 | ++-----------------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 13, total allocated memory: 262144 +Total database accesses: 1, total allocated memory: 184 ---- ====== [[query-plan-directed-all-relationships-scan]] -== Directed All Relationships Scan -//DirectedAllRelationshipsScan +=== Directed All Relationships Scan The `DirectedAllRelationshipsScan` operator fetches all relationships and their start and end nodes in the database. @@ -1392,8 +1226,7 @@ Total database accesses: 28, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-directed-all-relationships-scan]] -== Partitioned Directed All Relationships Scan -// New in 5.17 +=== Partitioned Directed All Relationships Scan The `PartitionedDirectedAllRelationshipsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-all-relationships-scan[`DirectedAllRelationshipsScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. It allows the store to be partitioned into different segments where each segment can be scanned independently in parallel. @@ -1432,8 +1265,8 @@ Total database accesses: 111 ====== [[query-plan-undirected-all-relationships-scan]] -== Undirected All Relationships Scan -//UndirectedAllRelationshipsScan +=== Undirected All Relationships Scan + The `UndirectedAllRelationshipsScan` operator fetches all relationships and their start and end nodes in the database. ====== @@ -1470,7 +1303,7 @@ Total database accesses: 28, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-undirected-all-relationships-scan]] -== Partitioned Undirected All Relationships Scan +=== Partitioned Undirected All Relationships Scan // New in 5.17 The `PartitionedUndirectedAllRelationshipsScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-all-relationships-scan[`UndirectedAllRelationshipsScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. @@ -1510,7 +1343,7 @@ Total database accesses: 194 ====== [[query-plan-directed-relationship-type-scan]] -== Directed Relationship Type Scan +=== Directed Relationship Type Scan // DirectedRelationshipTypeScan The `DirectedRelationshipTypeScan` operator fetches all relationships and their start and end nodes with a specific type from the relationship type index. @@ -1553,7 +1386,7 @@ Total database accesses: 13, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-directed-relationship-types-scan]] -== Partitioned Directed Relationship Type Scan +=== Partitioned Directed Relationship Type Scan // PartitionedDirectedRelationshipTypeScan // New in 5.17 @@ -1598,7 +1431,7 @@ Batch size 128 [[query-plan-undirected-relationship-type-scan]] -== Undirected Relationship Type Scan +=== Undirected Relationship Type Scan // UndirectedRelationshipTypeScan The `UndirectedRelationshipTypeScan` operator fetches all relationships and their start and end nodes with a specific type from the relationship type index. @@ -1639,7 +1472,7 @@ Total database accesses: 13, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-undirected-relationship-type-scan]] -== Partitioned Undirected Relationship Type Scan +=== Partitioned Undirected Relationship Type Scan // PartitionedUndirectedRelationshipTypeScan // New in 5.17 @@ -1683,7 +1516,7 @@ Total database accesses: 37 [[query-plan-directed-union-relationship-types-scan]] -== Directed Union Relationship Types Scan +=== Directed Union Relationship Types Scan // DirectedUnionRelationshipTypesScan The `DirectedUnionRelationshipTypesScan` operator fetches all relationships and their start and end nodes with at least one of the provided types from the relationship type index. @@ -1728,7 +1561,7 @@ Total database accesses: 14, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-directed-union-relationship-types-scan]] -== Partitioned Directed Union Relationship Types Scan +=== Partitioned Directed Union Relationship Types Scan // PartitionedDirectedUnionRelationshipTypesScan // New in 5.17 @@ -1776,7 +1609,7 @@ Total database accesses: 25 [[query-plan-undirected-union-relationship-types-scan]] -== Undirected Union Relationship Types Scan +=== Undirected Union Relationship Types Scan // UndirectedUnionRelationshipTypesScan The `UndirectedUnionRelationshipTypesScan` operator fetches all relationships and their start and end nodes with at least one of the provided types from the relationship type index. @@ -1821,9 +1654,7 @@ Total database accesses: 14, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-undirected-union-relationship-types-scan]] -== Partitioned Undirected Union Relationship Types Scan -// PartitionedUndirectedUnionRelationshipTypesScan -// New in 5.17 +=== Partitioned Undirected Union Relationship Types Scan The `PartitionedUndirectedUnionRelationshipTypeScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-union-relationship-types-scan[`UndirectedUnionRelationshipTypesScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. @@ -1866,23 +1697,31 @@ Total database accesses: 37 ---- ====== -[role=label--new-5.3] -[[query-plan-node-by-elementid-seek]] -== Node By ElementId Seek -// NodeByElementIdSeek -The `NodeByElementIdSeek` operator reads one or more nodes by ID from the node store, specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]. +[[query-plan-directed-relationship-index-scan]] +=== Directed Relationship Index Scan +// DirectedRelationshipIndexScan -.NodeByElementIdSeek +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX range_worksin_title FOR ()-[r:WORKS_IN]->() ON (r.title) +---- +//// + +The `DirectedRelationshipIndexScan` operator examines all values stored in an index, returning all relationships and their start and end nodes with a particular relationship type and a specified property. + + +.DirectedRelationshipIndexScan ====== .Query [source, cypher] ---- PROFILE -MATCH (n) -WHERE elementId(n) = 0 -RETURN n +MATCH ()-[r: WORKS_IN]->() +WHERE r.title IS NOT NULL +RETURN r ---- .Query Plan @@ -1896,36 +1735,37 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | n | 1 | 1 | 0 | | | | | -| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------- | | | -| +NodeByElementIdSeek | n WHERE elementId(n) = $autoint_0 | 1 | 1 | 1 | 120 | 3/0 | 2.108 | Fused in Pipeline 0 | -+------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++--------------------------------+----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------------------+----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 15 | 15 | 0 | | | | | +| | +----------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipIndexScan | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title IS NOT NULL | 15 | 15 | 16 | 120 | 3/1 | 2.464 | Fused in Pipeline 0 | ++--------------------------------+----------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 1, total allocated memory: 184 +Total database accesses: 16, total allocated memory: 184 ---- ====== -[[query-plan-node-by-id-seek]] -== Node By Id Seek -// NodeByIdSeek - -The `NodeByIdSeek` operator reads one or more nodes by id from the node store, specified via the function xref::functions/scalar.adoc#functions-id[id()]. +[role=label--new-5.17] +[[query-plan-partitioned-directed-relationship-index-scan]] +=== Partitioned Directed Relationship Index Scan +The `PartitionedDirectedRelationshipIndexScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-index-scan[`DirectedRelationshipIndexScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. -.NodeByIdSeek +.PartitionedDirectedRelationshipIndexScan ====== .Query [source, cypher] ---- +CYPHER runtime=parallel PROFILE -MATCH (n) -WHERE id(n) = 0 -RETURN n +MATCH ()-[r: WORKS_IN]->() +WHERE r.title IS NOT NULL +RETURN r ---- .Query Plan @@ -1933,44 +1773,41 @@ RETURN n ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | n | 1 | 1 | 2 | 0 | | | | -| | +----+----------------------------+----------------+------+---------+----------------+ | | | -| +NodeByIdSeek | 1 | n WHERE id(n) = $autoint_0 | 1 | 1 | 1 | 248 | 2/0 | 1.109 | Fused in Pipeline 0 | -+-----------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 3, total allocated memory: 312 ++-------------------------------------------+----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------------------------+----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | r | 15 | 15 | 70 | 1/0 | 2.865 | In Pipeline 1 | +| | +----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedDirectedRelationshipIndexScan | 1 | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title IS NOT NULL | 15 | 15 | 16 | 2/0 | 0.527 | In Pipeline 0 | ++-------------------------------------------+----+----------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -1 row +Total database accesses: 86 ---- ====== +[[query-plan-undirected-relationship-index-scan]] +=== Undirected Relationship Index Scan -[[query-plan-node-by-label-scan]] -== Node By Label Scan -// NodeByLabelScan - -The `NodeByLabelScan` operator fetches all nodes with a specific label from the node label index. +The `UndirectedRelationshipIndexScan` operator examines all values stored in an index, returning all relationships and their start and end nodes with a particular relationship type and a specified property. -.NodeByLabelScan +.UndirectedRelationshipIndexScan ====== .Query [source, cypher] ---- PROFILE -MATCH (person:Person) -RETURN person +MATCH ()-[r: WORKS_IN]-() +WHERE r.title IS NOT NULL +RETURN r ---- .Query Plan @@ -1984,30 +1821,27 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | person | 14 | 14 | 0 | | | | | -| | +---------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | person:Person | 14 | 14 | 15 | 120 | 2/1 | 0.522 | Fused in Pipeline 0 | -+------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++----------------------------------+---------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------+---------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 30 | 30 | 0 | | | | | +| | +---------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipIndexScan | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title IS NOT NULL | 30 | 30 | 16 | 120 | 3/1 | 1.266 | Fused in Pipeline 0 | ++----------------------------------+---------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 15, total allocated memory: 184 +Total database accesses: 16, total allocated memory: 184 ---- ====== [role=label--new-5.17] -[[query-plan-partitioned-node-by-label-scan]] -== Partitioned Node By Label Scan -// PartitionedNodeByLabelScan -// New in 5.17 +[[query-plan-partitioned-undirected-relationship-index-scan]] +=== Partitioned Undirected Relationship Index Scan -The `PartitionedNodeByLabelScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-by-label-scan[`NodeByLabelScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +The `PartitionedUndirectedRelationshipIndexScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-relationship-index-scan[`UndirectedRelationshipIndexScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. - -.PartitionedNodeByLabelScan +.PartitionedUndirectedRelationshipIndexScan ====== .Query @@ -2015,8 +1849,9 @@ It allows the index to be partitioned into different segments where each segment ---- CYPHER runtime=parallel PROFILE -MATCH (person:Person) -RETURN person +MATCH ()-[r: WORKS_IN]-() +WHERE r.title IS NOT NULL +RETURN r ---- .Query Plan @@ -2030,45 +1865,44 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------------------+----+---------------+----------------+------+---------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------------+----+---------------+----------------+------+---------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | person | 14 | 14 | 28 | 2/0 | 0.623 | In Pipeline 1 | -| | +----+---------------+----------------+------+---------+------------------------+-----------+---------------+ -| +PartitionedNodeByLabelScan | 1 | person:Person | 14 | 14 | 15 | 1/0 | 0.094 | In Pipeline 0 | -+-----------------------------+----+---------------+----------------+------+---------+------------------------+-----------+---------------+ ++---------------------------------------------+----+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------------------------+----+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | r | 30 | 30 | 140 | 1/0 | 3.088 | In Pipeline 1 | +| | +----+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedUndirectedRelationshipIndexScan | 1 | RANGE INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title IS NOT NULL | 30 | 30 | 16 | 2/0 | 0.572 | In Pipeline 0 | ++---------------------------------------------+----+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -Total database accesses: 43 +Total database accesses: 156 ---- ====== -[[query-plan-node-index-seek]] -== Node Index Seek -// NodeIndexSeek +[[query-plan-directed-relationship-index-contains-scan]] +=== Directed Relationship Index Contains Scan //// [source, cypher, role=test-setup] ---- -CREATE RANGE INDEX range_location_name FOR (l:Location) ON (l.name) +CREATE TEXT INDEX text_worksin_title FOR ()-[r:WORKS_IN]->() ON (r.title) ---- //// -The `NodeIndexSeek` operator finds nodes using an index seek. -The node variable and the index used are shown in the arguments of the operator. -If the index is a unique index, the operator is instead called xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-unique-index-seek[NodeUniqueIndexSeek]. +The `DirectedRelationshipIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific `STRING`; for example, in queries including `CONTAINS`. +Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a type scan using `DirectedRelationshipTypeScan`, and a property store filter. -.NodeIndexSeek +.DirectedRelationshipIndexContainsScan ====== .Query [source, cypher] ---- PROFILE -MATCH (location:Location {name: 'Malmo'}) -RETURN location +MATCH ()-[r: WORKS_IN]->() +WHERE r.title CONTAINS 'senior' +RETURN r ---- .Query Plan @@ -2082,39 +1916,37 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | location | 1 | 1 | 0 | | | | | -| | +----------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX location:Location(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 2/1 | 0.401 | Fused in Pipeline 0 | -+-----------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++----------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 0 | 4 | 0 | | | | | +| | +--------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipIndexContainsScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title CONTAINS $autostring_0 | 0 | 4 | 5 | 120 | 3/0 | 1.051 | Fused in Pipeline 0 | ++----------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 2, total allocated memory: 184 +Total database accesses: 5, total allocated memory: 184 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-node-index-seek]] -== Partitioned Node Index Seek -// PartitionedNodeIndexSeek -// New in 5.17 -The `PartitionedNodeIndexSeek` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-seek[`NodeIndexSeek`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. -It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. +[[query-plan-undirected-relationship-index-contains-scan]] +=== Undirected Relationship Index Contains Scan + +The `UndirectedRelationshipIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific `STRING`; for example, in queries including `CONTAINS`. +Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a type scan using `DirectedRelationshipTypeScan`, and a property store filter. -.PartitionedNodeIndexSeek +.UndirectedRelationshipIndexContainsScan ====== .Query [source, cypher] ---- -CYPHER runtime=parallel PROFILE -MATCH (location:Location {name: 'Malmo'}) -RETURN location +MATCH ()-[r: WORKS_IN]-() +WHERE r.title CONTAINS 'senior' +RETURN r ---- .Query Plan @@ -2122,53 +1954,43 @@ RETURN location ---- Planner COST -Runtime PARALLEL +Runtime PIPELINED Runtime version {neo4j-version-minor} Batch size 128 -+---------------------------+----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------+----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | location | 0 | 1 | 2 | 2/0 | 0.179 | In Pipeline 1 | -| | +----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +PartitionedNodeIndexSeek | 1 | RANGE INDEX location:Location(name) WHERE name = $autostring_0 | 0 | 1 | 2 | 1/0 | 0.167 | In Pipeline 0 | -+---------------------------+----+----------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ ++------------------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 0 | 8 | 0 | | | | | +| | +-------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipIndexContainsScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title CONTAINS $autostring_0 | 0 | 8 | 5 | 120 | 3/0 | 2.684 | Fused in Pipeline 0 | ++------------------------------------------+-------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 4 +Total database accesses: 5, total allocated memory: 184 ---- ====== -[[query-plan-node-unique-index-seek]] -== Node Unique Index Seek -// NodeUniqueIndexSeek - -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT team_name IF NOT EXISTS FOR (t:Team) REQUIRE (t.name) IS UNIQUE ----- -//// +[[query-plan-directed-relationship-index-ends-with-scan]] +=== Directed Relationship Index Ends With Scan -The `NodeUniqueIndexSeek` operator finds nodes using an index seek within a unique index. -The node variable and the index used are shown in the arguments of the operator. -If the index is not unique, the operator is instead called xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-seek[NodeIndexSeek]. -If the index seek is used to solve a xref::clauses/merge.adoc[MERGE] clause, it will also be marked with `(Locking)`. -This makes it clear that any nodes returned from the index will be locked in order to prevent concurrent conflicting updates. +The `DirectedRelationshipIndexEndsWithScan` operator examines all values stored in an index, searching for entries ending in a specific `STRING`; for example, in queries containing `ENDS WITH`. +Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label scan using `NodeByLabelScan`, and a property store filter. -.NodeUniqueIndexSeek +.DirectedRelationshipIndexEndsWithScan ====== .Query [source, cypher] ---- PROFILE -MATCH (t:Team {name: 'Malmo'}) -RETURN t +MATCH ()-[r: WORKS_IN]->() +WHERE r.title ENDS WITH 'developer' +RETURN r ---- .Query Plan @@ -2182,51 +2004,37 @@ Runtime version {neo4j-version-minor} Batch size 128 -+----------------------+------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------+------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | t | 1 | 0 | 0 | | | | | -| | +------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeUniqueIndexSeek | UNIQUE t:Team(name) WHERE name = $autostring_0 | 1 | 0 | 1 | 120 | 0/1 | 0.280 | Fused in Pipeline 0 | -+----------------------+------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 0 | 8 | 0 | | | | | +| | +---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipIndexEndsWithScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]->(anon_1) WHERE title ENDS WITH $autostring_0 | 0 | 8 | 9 | 120 | 3/0 | 1.887 | Fused in Pipeline 0 | ++----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 1, total allocated memory: 184 +Total database accesses: 9, total allocated memory: 184 ---- ====== -[[query-plan-multi-node-index-seek]] -== Multi Node Index Seek -// MultiNodeIndexSeek - -//// -[source, cypher, role=test-setup] ----- -CREATE RANGE INDEX range_person_name FOR (p:Person) ON (p.name) ----- -//// - -The `MultiNodeIndexSeek` operator finds nodes using multiple index seeks. -It supports using multiple distinct indexes for different nodes in the query. -The node variables and the indexes used are shown in the arguments of the operator. +[[query-plan-undirected-relationship-index-ends-with-scan]] +=== Undirected Relationship Index Ends With Scan -The operator yields a cartesian product of all index seeks. -For example, if the operator does two seeks and the first seek finds the nodes `a1, a2` and the second `b1, b2, b3`, the `MultiNodeIndexSeek` will yield the rows `(a1, b1), (a1, b2), (a1, b3), (a2, b1), (a2, b2), (a2, b3)`. +The `UndirectedRelationshipIndexEndsWithScan` operator examines all values stored in an index, searching for entries ending in a specific `STRING`; for example, in queries containing `ENDS WITH`. +Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label scan using `NodeByLabelScan`, and a property store filter. -.MultiNodeIndexSeek +.UndirectedRelationshipIndexEndsWithScan ====== .Query [source, cypher] ---- PROFILE -CYPHER runtime=pipelined -MATCH - (location:Location {name: 'Malmo'}), - (person:Person {name: 'Bob'}) -RETURN location, person +MATCH ()-[r: WORKS_IN]-() +WHERE r.title ENDS WITH 'developer' +RETURN r ---- .Query Plan @@ -2240,45 +2048,37 @@ Runtime version {neo4j-version-minor} Batch size 128 -+---------------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | location, person | 1 | 1 | 0 | | | | | -| | +-----------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +MultiNodeIndexSeek | RANGE INDEX location:Location(name) WHERE name = $autostring_0, | 1 | 0 | 0 | 120 | 2/2 | 1.910 | Fused in Pipeline 0 | -| | RANGE INDEX person:Person(name) WHERE name = $autostring_1 | | | | | | | | -+---------------------+-----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r | 0 | 16 | 0 | | | | | +| | +--------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipIndexEndsWithScan | TEXT INDEX (anon_0)-[r:WORKS_IN(title)]-(anon_1) WHERE title ENDS WITH $autostring_0 | 0 | 16 | 9 | 120 | 3/0 | 1.465 | Fused in Pipeline 0 | ++------------------------------------------+--------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 0, total allocated memory: 184 +Total database accesses: 9, total allocated memory: 184 ---- ====== -[[query-plan-asserting-multi-node-index-seek]] -== Asserting Multi Node Index Seek -// AssertingMultiNodeIndexSeek - -//// -[source, cypher, role=test-setup] ----- -CREATE CONSTRAINT team_id IF NOT EXISTS FOR (t:Team) REQUIRE (t.id) IS UNIQUE ----- -//// +[[query-plan-directed-relationship-index-seek]] +=== Directed Relationship Index Seek -The `AssertingMultiNodeIndexSeek` operator is used to ensure that no property uniqueness constraints are violated. -The example looks for the presence of a team with the supplied name and id, and if one does not exist, it will be created. -Owing to the existence of two property uniqueness constraints on `:Team(name)` and `:Team(id)`, any node that would be found by the `UniqueIndexSeek` operator must be the very same node or the constraints would be violated. +The `DirectedRelationshipIndexSeek` operator finds relationships and their start and end nodes using an index seek. +The relationship variable and the index used are shown in the arguments of the operator. -.AssertingMultiNodeIndexSeek +.DirectedRelationshipIndexSeek ====== .Query [source, cypher] ---- PROFILE -MERGE (t:Team {name: 'Engineering', id: 42}) +MATCH (candidate)-[r:WORKS_IN]->() +WHERE r.title = 'chief architect' +RETURN candidate ---- .Query Plan @@ -2292,44 +2092,37 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------------------+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------------+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | | 1 | 0 | 0 | | | | | -| | +-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +EmptyResult | | 1 | 0 | 0 | | | | | -| | +-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Merge | CREATE (t:Team {name: $autostring_0, id: $autoint_1}) | 1 | 1 | 0 | | | | | -| | +-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +AssertingMultiNodeIndexSeek | UNIQUE t:Team(name) WHERE name = $autostring_0, UNIQUE t:Team(id) WHERE id = $autoint_1 | 0 | 2 | 4 | 120 | 0/2 | 1.584 | Fused in Pipeline 0 | -+------------------------------+-----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++--------------------------------+-----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------------------+-----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | candidate | 2 | 1 | 0 | | | | | +| | +-----------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipIndexSeek | RANGE INDEX (candidate)-[r:WORKS_IN(title)]->(anon_0) WHERE title = $autostring_0 | 2 | 1 | 2 | 120 | 3/1 | 0.591 | Fused in Pipeline 0 | ++--------------------------------+-----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 4, total allocated memory: 184 +Total database accesses: 2, total allocated memory: 184 ---- ====== +[role=label--new-5.17] +[[query-plan-partitioned-directed-relationship-index-seek]] +=== Partitioned Directed Relationship Index Seek -[[query-plan-node-index-seek-by-range]] -== Node Index Seek By Range -// NodeIndexSeekByRange - - -The `NodeIndexSeekByRange` operator finds nodes using an index seek where the value of the property matches a given prefix `STRING`. -`NodeIndexSeekByRange` can be used for `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+` and `+>=+`. -If the index is a unique index, the operator is instead called `NodeUniqueIndexSeekByRange`. - +The `PartitionedDirectedRelationshipIndexSeek` is a variant of the the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-index-seek[`DirectedRelationshipIndexSeek`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. -.NodeIndexSeekByRange +.PartitionedDirectedRelationshipIndexSeek ====== .Query [source, cypher] ---- +CYPHER runtime=parallel PROFILE -MATCH (l:Location) -WHERE l.name STARTS WITH 'Lon' -RETURN l +MATCH (candidate)-[r:WORKS_IN]->() +WHERE r.title = 'chief architect' +RETURN candidate ---- .Query Plan @@ -2337,46 +2130,43 @@ RETURN l ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL Runtime version {neo4j-version-minor} Batch size 128 -+-----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | l | 2 | 1 | 0 | | | | | -| | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeekByRange | RANGE INDEX l:Location(name) WHERE name STARTS WITH $autostring_0 | 2 | 1 | 2 | 120 | 3/0 | 0.825 | Fused in Pipeline 0 | -+-----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------------------------------+----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------------------------+----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | candidate | 2 | 1 | 2 | 2/0 | 0.284 | In Pipeline 1 | +| | +----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedDirectedRelationshipIndexSeek | 1 | RANGE INDEX (candidate)-[r:WORKS_IN(title)]->(anon_0) WHERE title = $autostring_0 | 2 | 1 | 2 | 2/0 | 0.148 | In Pipeline 0 | ++-------------------------------------------+----+-----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -Total database accesses: 2, total allocated memory: 184 +Total database accesses: 4 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-node-index-seek-by-range]] -== Partitioned Node Index Seek By Range -// PartitionedNodeIndexSeekByRange -// New in 5.17 -The `PartitionedNodeIndexSeekByRange` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-seek-by-range[`NodeIndexSeekByRange`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. -It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. +[[query-plan-undirected-relationship-index-seek]] +=== Undirected Relationship Index Seek +The `UndirectedRelationshipIndexSeek` operator finds relationships and their start and end nodes using an index seek. +The relationship variable and the index used are shown in the arguments of the operator. -.PartitionedNodeIndexSeekByRange + +.UndirectedRelationshipIndexSeek ====== .Query [source, cypher] ---- -CYPHER runtime=parallel PROFILE -MATCH (l:Location) -WHERE l.name STARTS WITH 'Lon' -RETURN l +MATCH (candidate)-[r:WORKS_IN]-() +WHERE r.title = 'chief architect' +RETURN candidate ---- .Query Plan @@ -2384,45 +2174,43 @@ RETURN l ---- Planner COST -Runtime PARALLEL +Runtime PIPELINED Runtime version {neo4j-version-minor} Batch size 128 -+----------------------------------+----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------------+----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | l | 0 | 1 | 2 | 2/0 | 0.191 | In Pipeline 1 | -| | +----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +PartitionedNodeIndexSeekByRange | 1 | RANGE INDEX l:Location(name) WHERE name STARTS WITH $autostring_0 | 0 | 1 | 2 | 1/0 | 0.087 | In Pipeline 0 | -+----------------------------------+----+-------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ ++----------------------------------+----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------+----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | candidate | 4 | 2 | 0 | | | | | +| | +----------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipIndexSeek | RANGE INDEX (candidate)-[r:WORKS_IN(title)]-(anon_0) WHERE title = $autostring_0 | 4 | 2 | 2 | 120 | 3/1 | 0.791 | Fused in Pipeline 0 | ++----------------------------------+----------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 4 +Total database accesses: 2, total allocated memory: 184 ---- ====== +[[query-plan-partitioned-undirected-relationship-index-seek]] +=== Partitioned Undirected Relationship Index Seek -[[query-plan-node-unique-index-seek-by-range]] -== Node Unique Index Seek By Range -// NodeUniqueIndexSeekByRange - -The `NodeUniqueIndexSeekByRange` operator finds nodes using an index seek within a unique index, where the value of the property matches a given prefix `STRING`. -`NodeUniqueIndexSeekByRange` is used by `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+`, and `+>=+`. -If the index is not unique, the operator is instead called `NodeIndexSeekByRange`. +The `PartitionedUndirectedRelationshipIndexSeek` operator finds relationships and their start and end nodes using an index seek. +The relationship variable and the index used are shown in the arguments of the operator. -.NodeUniqueIndexSeekByRange +.PartitionedUndirectedRelationshipIndexSeek ====== .Query [source, cypher] ---- +CYPHER runtime=parallel PROFILE -MATCH (t:Team) -WHERE t.name STARTS WITH 'Ma' -RETURN t +MATCH (candidate)-[r:WORKS_IN]-() +WHERE r.title = 'chief architect' +RETURN candidate ---- .Query Plan @@ -2430,51 +2218,42 @@ RETURN t ---- Planner COST -Runtime PIPELINED +Runtime PARALLEL Runtime version {neo4j-version-minor} Batch size 128 -+-----------------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | t | 2 | 0 | 0 | | | | | -| | +----------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeUniqueIndexSeekByRange | UNIQUE t:Team(name) WHERE name STARTS WITH $autostring_0 | 2 | 0 | 1 | 120 | 1/0 | 0.623 | Fused in Pipeline 0 | -+-----------------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------------------------------+----+----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------------------------+----+----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | candidate | 4 | 2 | 4 | 2/0 | 0.333 | In Pipeline 1 | +| | +----+----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ +| +PartitionedUndirectedRelationshipIndexSeek | 1 | RANGE INDEX (candidate)-[r:WORKS_IN(title)]-(anon_0) WHERE title = $autostring_0 | 4 | 2 | 2 | 2/0 | 0.151 | In Pipeline 0 | ++---------------------------------------------+----+----------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -Total database accesses: 1, total allocated memory: 184 +Total database accesses: 6 ---- ====== -[[query-plan-node-index-contains-scan]] -== Node Index Contains Scan == -// NodeIndexContainsScan - -//// -[source, cypher, role=test-setup] ----- -CREATE TEXT INDEX text_location_name FOR (l:Location) ON (l.name) ----- -//// +[[query-plan-directed-relationship-by-element-id-seek]] +=== Directed Relationship By Element Id Seek -The `NodeIndexContainsScan` operator examines all values stored in an index, searching for entries containing a specific `STRING`; for example, in queries including `CONTAINS`. -Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label scan using `NodeByLabelScan`, and a property store filter. +The `DirectedRelationshipByElementIdSeek` operator reads one or more relationships by element id from the relationship store (specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]) and produces the relationship as well as the source and target node of the relationship. -.NodeIndexContainsScan +.DirectedRelationshipByElementIdSeek ====== .Query [source, cypher] ---- PROFILE -MATCH (l:Location) -WHERE l.name CONTAINS 'al' -RETURN l +MATCH (n1)-[r]->() +WHERE elementId(r) = 0 +RETURN r, n1 ---- .Query Plan @@ -2488,38 +2267,35 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------------+---------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------+---------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | l | 0 | 2 | 0 | | | | | -| | +---------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexContainsScan | TEXT INDEX l:Location(name) WHERE name CONTAINS $autostring_0 | 0 | 2 | 3 | 120 | 2/0 | 1.305 | Fused in Pipeline 0 | -+------------------------+---------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++--------------------------------------+----+----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------------------------+----+----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | r, n1 | 1 | 0 | 0 | 0 | 0/0 | 0.314 | | +| | +----+----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +DirectedRelationshipByElementIdSeek | 1 | (n1)-[r]->(anon_0) WHERE elementId(r) = $autoint_0 | 1 | 0 | 0 | 248 | 0/0 | 2.337 | In Pipeline 0 | ++--------------------------------------+----+----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -Total database accesses: 3, total allocated memory: 184 +Total database accesses: 0, total allocated memory: 312 ---- ====== +[[query-plan-directed-relationship-by-id-seek]] +=== Directed Relationship By Id Seek -[[query-plan-node-index-ends-with-scan]] -== Node Index Ends With Scan -// NodeIndexEndsWithScan - -The `NodeIndexEndsWithScan` operator examines all values stored in an index, searching for entries ending in a specific `STRING`; for example, in queries containing `ENDS WITH`. -Although this is slower than an index seek (since all entries need to be examined), it is still faster than the indirection resulting from a label scan using `NodeByLabelScan`, and a property store filter. +The `DirectedRelationshipByIdSeek` operator reads one or more relationships by id from the relationship store, and produces the relationship as well as the source and target node of the relationship. -.NodeIndexEndsWithScan +.DirectedRelationshipByIdSeek ====== .Query [source, cypher] ---- PROFILE -MATCH (l:Location) -WHERE l.name ENDS WITH 'al' -RETURN l +MATCH (n1)-[r]->() +WHERE id(r) = 0 +RETURN r, n1 ---- .Query Plan @@ -2533,37 +2309,36 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | l | 0 | 0 | 0 | | | | | -| | +----------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexEndsWithScan | TEXT INDEX l:Location(name) WHERE name ENDS WITH $autostring_0 | 0 | 0 | 1 | 120 | 0/0 | 4.409 | Fused in Pipeline 0 | -+------------------------+----------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------------------+----+---------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------------+----+---------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | r, n1 | 1 | 1 | 7 | 0 | | | | +| | +----+---------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipByIdSeek | 1 | (n1)-[r]->(anon_0) WHERE id(r) = $autoint_0 | 1 | 1 | 1 | 248 | 3/0 | 0.483 | Fused in Pipeline 0 | ++-------------------------------+----+---------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 1, total allocated memory: 184 +Total database accesses: 8, total allocated memory: 312 ---- ====== +[[query-plan-undirected-relationship-by-element-id-seek]] +=== Undirected Relationship By Element Id Seek -[[query-plan-node-index-scan]] -== Node Index Scan -// NodeIndexScan - -The `NodeIndexScan` operator examines all values stored in an index, returning all nodes with a particular label and a specified property. +The `UndirectedRelationshipByElementIdSeek` operator reads one or more relationships by element id from the relationship store (specified via the function xref::functions/scalar.adoc#functions-elementid[elementId()]). +As the direction is unspecified, two rows are produced for each relationship as a result of alternating the combination of the start and end node. -.NodeIndexScan +.UndirectedRelationshipByElementIdSeek ====== .Query [source, cypher] ---- PROFILE -MATCH (l:Location) -WHERE l.name IS NOT NULL -RETURN l +MATCH (n1)-[r]-() +WHERE elementId(r) = 1 +RETURN r, n1 ---- .Query Plan @@ -2577,40 +2352,36 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | l | 10 | 10 | 0 | | | | | -| | +-----------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexScan | RANGE INDEX l:Location(name) WHERE name IS NOT NULL | 10 | 10 | 11 | 120 | 2/1 | 0.557 | Fused in Pipeline 0 | -+-----------------+-----------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------------------------+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------------------+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | r, n1 | 2 | 2 | 0 | | | | | +| | +--------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipByElementIdSeek| (n1)-[r]-(anon_0) WHERE elementId(r) = $autoint_0 | 2 | 2 | 1 | 120 | 4/0 | 0.332 | Fused in Pipeline 0 | ++---------------------------------+--------------------------------------------+-----+---------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 11, total allocated memory: 184 +Total database accesses: 1, total allocated memory: 184 ---- ====== -[role=label--new-5.17] -[[query-plan-partitioned-node-index-scan]] -== Partitioned Node Index Scan -// PartitionedNodeIndexScan -// New in 5.17 +[[query-plan-undirected-relationship-by-id-seek]] +=== Undirected Relationship By Id Seek -The `PartitionedNodeIndexScan` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-node-index-scan[`NodeIndexScan`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. -It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. +The `UndirectedRelationshipByIdSeek` operator reads one or more relationships by id from the relationship store (specified via the function xref::functions/scalar.adoc#functions-id[Id()]). +As the direction is unspecified, two rows are produced for each relationship as a result of alternating the combination of the start and end node. -.PartitionedNodeIndexScan +.UndirectedRelationshipByIdSeek ====== .Query [source, cypher] ---- -CYPHER runtime=parallel PROFILE -MATCH (l:Location) -WHERE l.name IS NOT NULL -RETURN l +MATCH (n1)-[r]-() +WHERE id(r) = 1 +RETURN r, n1 ---- .Query Plan @@ -2618,47 +2389,50 @@ RETURN l ---- Planner COST -Runtime PARALLEL +Runtime PIPELINED Runtime version {neo4j-version-minor} Batch size 128 -+---------------------------+----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------------+----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | l | 1 | 10 | 20 | 2/0 | 0.472 | In Pipeline 1 | -| | +----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ -| +PartitionedNodeIndexScan | 1 | RANGE INDEX l:Location(name) WHERE name IS NOT NULL | 1 | 10 | 11 | 1/0 | 0.187 | In Pipeline 0 | -+---------------------------+----+-----------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------+ ++---------------------------------+----+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------------+----+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | r, n1 | 2 | 2 | 14 | 0 | | | | +| | +----+--------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipByIdSeek | 1 | (n1)-[r]-(anon_0) WHERE id(r) = $autoint_0 | 2 | 2 | 1 | 248 | 3/0 | 1.005 | Fused in Pipeline 0 | ++---------------------------------+----+--------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 31 +Total database accesses: 15, total allocated memory: 312 ---- ====== -// --- apply operators --- +[[query-plan-directed-relationship-index-seek-by-range]] +=== Directed Relationship Index Seek By Range -[[query-plan-apply]] -== Apply -// Apply +//// +[source, cypher, role=test-setup] +---- +CREATE RANGE INDEX range_worksin_duration FOR ()-[r:WORKS_IN]->() ON (r.duration) +---- +//// -All the different `Apply` operators (listed below) share the same basic functionality: they perform a nested loop by taking a single row from the left-hand side, and using the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-argument[`Argument`] operator on the right-hand side, execute the operator tree on the right-hand side. -The versions of the `Apply` operators differ in how the results are managed. -The `Apply` operator (i.e. the standard version) takes the row produced by the right-hand side -- which at this point contains data from both the left-hand and right-hand sides -- and yields it. +The `DirectedRelationshipIndexSeekByRange` operator finds relationships and their start and end nodes using an index seek where the value of the property matches a given prefix `STRING`. +`DirectedRelationshipIndexSeekByRange` can be used for `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+` and `+>=+`. -.Apply +.DirectedRelationshipIndexSeekByRange ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person {name: 'me'}) -MATCH (q:Person {name: p.secondName}) -RETURN p, q +MATCH (candidate: Person)-[r:WORKS_IN]->(location) +WHERE r.duration > 100 +RETURN candidate ---- .Query Plan @@ -2672,17 +2446,208 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p, q | 1 | 0 | 0 | | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Apply | | 1 | 0 | 0 | | | | | -| |\ +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +NodeIndexSeek | RANGE INDEX q:Person(name) WHERE name = p.secondName | 1 | 0 | 0 | 2152 | 0/0 | 0.219 | Fused in Pipeline 1 | -| | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 0/1 | 0.236 | In Pipeline 0 | -+------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------------------------+----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------------------------+----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | candidate | 4 | 15 | 0 | | | | | +| | +----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | candidate:Person | 4 | 15 | 30 | | | | | +| | +----------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DirectedRelationshipIndexSeekByRange | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]->(location) WHERE duration > $autoint_0 | 4 | 15 | 16 | 120 | 4/1 | 0.703 | Fused in Pipeline 0 | ++---------------------------------------+----------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 46, total allocated memory: 184 +---- + +====== + +[role=label--new-5.17] +[[query-plan-partitioned-directed-relationship-index-seek-by-range]] +=== Partitioned Directed Relationship Index Seek By Range + +The `PartitionedDirectedRelationshipIndexSeekByRange` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-index-seek-by-range[`DirectedRelationshipIndexSeekByRange`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. + +.PartitionedDirectedRelationshipIndexSeekByRange +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH (candidate: Person)-[r:WORKS_IN]->(location) +WHERE r.duration > 100 +RETURN candidate +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++--------------------------------------------------+----+----------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------------------------------------+----+----------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | candidate | 4 | 15 | 30 | 1/0 | 1.031 | In Pipeline 1 | +| | +----+----------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +Filter | 1 | candidate:Person | 4 | 15 | 30 | | | | +| | +----+----------------------------------------------------------------------------------------+----------------+------+---------+ | | | +| +PartitionedDirectedRelationshipIndexSeekByRange | 2 | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]->(location) WHERE duration > $autoint_0 | 4 | 15 | 16 | 3/0 | 0.203 | Fused in Pipeline 0 | ++--------------------------------------------------+----+----------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ + +Total database accesses: 76 +---- + +====== + + +[[query-plan-undirected-relationship-index-seek-by-range]] +=== Undirected Relationship Index Seek By Range + +The `UndirectedRelationshipIndexSeekByRange` operator finds relationships and their start and end nodes using an index seek where the value of the property matches a given prefix `STRING`. +`UndirectedRelationshipIndexSeekByRange` can be used for `STARTS WITH` and comparison operators such as `+<+`, `+>+`, `+<=+` and `+>=+`. + + +.UndirectedRelationshipIndexSeekByRange +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (candidate: Person)-[r:WORKS_IN]-(location) +WHERE r.duration > 100 +RETURN candidate +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | candidate | 5 | 15 | 0 | | | | | +| | +---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | candidate:Person | 5 | 15 | 60 | | | | | +| | +---------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +UndirectedRelationshipIndexSeekByRange | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]-(location) WHERE duration > $autoint_0 | 8 | 30 | 16 | 120 | 4/1 | 1.214 | Fused in Pipeline 0 | ++-----------------------------------------+---------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 76, total allocated memory: 184 +---- + +====== + +[role=label--new-5.17] +[[query-plan-partitioned-undirected-relationship-index-seek-by-range]] +=== Partitioned Undirected Relationship Index Seek By Range + +The `PartitionedUndirectedRelationshipIndexSeekByRange` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-undirected-relationship-index-seek-by-range[`UndirectedRelationshipIndexSeekByRange`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. +It allows the store to be partitioned into different segments where each segment can be scanned independently in parallel. + +.PartitionedUndirectedRelationshipIndexSeekByRange +====== + +.Query +[source, cypher] +---- +CYPHER runtime=parallel +PROFILE +MATCH (candidate: Person)-[r:WORKS_IN]-(location) +WHERE r.duration > 100 +RETURN candidate +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PARALLEL + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++----------------------------------------------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------------------------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | candidate | 5 | 15 | 30 | 1/0 | 0.918 | In Pipeline 1 | +| | +----+---------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +Filter | 1 | candidate:Person | 5 | 15 | 60 | | | | +| | +----+---------------------------------------------------------------------------------------+----------------+------+---------+ | | | +| +PartitionedUndirectedRelationshipIndexSeekByRange | 2 | RANGE INDEX (candidate)-[r:WORKS_IN(duration)]-(location) WHERE duration > $autoint_0 | 8 | 30 | 16 | 3/0 | 0.413 | Fused in Pipeline 0 | ++----------------------------------------------------+----+---------------------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ + +Total database accesses: 106 +---- + +====== + + +[[nested-loops-join-operators]] +== Nested loops and join operators + +Nested loop operators process data by iterating over the right-hand side (RHS) for each row from the left-hand side (LHS). +Each row from the LHS triggers the execution of the RHS, effectively creating a loop over the RHS for each LHS element. + +[[query-plan-apply]] +=== Apply + +All the different `Apply` operators (listed below) share the same basic functionality: they perform a nested loop by taking a single row from the left-hand side, and using the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-argument[`Argument`] operator on the right-hand side, execute the operator tree on the right-hand side. +The versions of the `Apply` operators differ in how the results are managed. +The `Apply` operator (i.e. the standard version) takes the row produced by the right-hand side -- which at this point contains data from both the left-hand and right-hand sides -- and yields it. + + +.Apply +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (p:Person {name: 'me'}) +MATCH (q:Person {name: p.secondName}) +RETURN p, q +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p, q | 1 | 0 | 0 | | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Apply | | 1 | 0 | 0 | | | | | +| |\ +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +NodeIndexSeek | RANGE INDEX q:Person(name) WHERE name = p.secondName | 1 | 0 | 0 | 2152 | 0/0 | 0.219 | Fused in Pipeline 1 | +| | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 0/1 | 0.236 | In Pipeline 0 | ++------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ Total database accesses: 2, total allocated memory: 2216 ---- @@ -2691,8 +2656,7 @@ Total database accesses: 2, total allocated memory: 2216 [[query-plan-semi-apply]] -== Semi Apply -// SemiApply +=== Semi Apply The `SemiApply` operator tests for the presence of a pattern predicate, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. If the right-hand side operator yields at least one row, the row from the left-hand side operator is yielded by the `SemiApply` operator. @@ -2746,7 +2710,7 @@ Total database accesses: 142, total allocated memory: 64 [[query-plan-anti-semi-apply]] -== Anti Semi Apply +=== Anti Semi Apply // AntiSemiApply The `AntiSemiApply` operator tests for the absence of a pattern, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. @@ -2802,29 +2766,28 @@ Total database accesses: 166, total allocated memory: 976 ====== -[[query-plan-transaction-apply]] -== TransactionApply -// TransactionApply -`TransactionApply` works like the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator but will commit the current transaction after a specified number of rows. -.TransactionApply -====== +[[query-plan-let-semi-apply]] +=== Let Semi Apply -[NOTE] -The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. -If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. +The `LetSemiApply` operator tests for the presence of a pattern predicate, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. +When a query contains multiple pattern predicates separated with `OR`, `LetSemiApply` will be used to evaluate the first of these. +It will record the result of evaluating the predicate but will leave any filtering to another operator. +In the example, `LetSemiApply` will be used to check for the presence of the `FRIENDS_WITH` relationship from each person. + + +.LetSemiApply +====== .Query [source, cypher] ---- PROFILE -LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line -CALL (line) { - CREATE (a: Artist {name: line[0]}) - RETURN a -} IN TRANSACTIONS OF 100 ROWS -RETURN a; +CYPHER runtime=slotted +MATCH (other:Person) +WHERE (other)-[:FRIENDS_WITH]->(:Person) OR (other)-[:WORKS_IN]->(:Location) +RETURN other.name ---- .Query Plan @@ -2832,154 +2795,35 @@ RETURN a; ---- Planner COST -Runtime PIPELINED +Runtime SLOTTED Runtime version {neo4j-version-minor} -Batch size 128 - -+-------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | a | 10 | 4 | 8 | 0 | | | | -| | +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | -| +TransactionApply | 1 | IN TRANSACTIONS OF $autoint_1 ROWS ON ERROR FAIL | 10 | 4 | 0 | 2152 | 0/0 | 2.036 | Fused in Pipeline 3 | -| |\ +----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Create | 2 | (a:Artist {name: line[$autoint_0]}) | 10 | 4 | 16 | | | | | -| | | +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | 3 | line | 10 | 4 | 0 | 3472 | 0/0 | 32.746 | Fused in Pipeline 2 | -| | +----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +LoadCSV | 4 | line | 10 | 4 | 0 | 328 | | | In Pipeline 1 | -+-------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 24, total allocated memory: 5472 ----- - -====== - - -[[query-plan-anti]] -== Anti -// Anti - -The `Anti` operator tests for the absence of a pattern. -If there are incoming rows, the `Anti` operator will yield no rows. -If there are no incoming rows, the `Anti` operator will yield a single row. - - -.Anti -====== - -.Query -[source, cypher] ----- -PROFILE -CYPHER runtime=pipelined -MATCH - (me:Person {name: 'me'}), - (other:Person) -WHERE NOT (me)-[:FRIENDS_WITH]->(other) -RETURN other.name ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | `other.name` | 4 | 12 | 0 | | 0/0 | 0.068 | | -| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Projection | other.name AS `other.name` | 4 | 12 | 24 | | 2/0 | 0.111 | In Pipeline 4 | -| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Apply | | 4 | 12 | 0 | | 0/0 | | | -| |\ +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Anti | | 4 | 12 | 0 | 1256 | 0/0 | 0.084 | In Pipeline 4 | -| | | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Limit | 1 | 11 | 2 | 0 | 752 | | | | -| | | +--------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(Into) | (me)-[anon_2:FRIENDS_WITH]->(other) | 1 | 2 | 81 | 2632 | | | | -| | | +--------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | me, other | 14 | 14 | 0 | 3192 | 1/0 | 0.904 | Fused in Pipeline 3 | -| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +CartesianProduct | | 14 | 14 | 0 | 3672 | | 1.466 | In Pipeline 2 | -| |\ +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +NodeByLabelScan| other:Person | 14 | 14 | 35 | | | | | -| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeIndexSeek | RANGE INDEX me:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 0/1 | 0.493 | In Pipeline 0 | -+-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 178, total allocated memory: 6744 ----- - -====== - - -[[query-plan-let-semi-apply]] -== Let Semi Apply -// LetSemiApply - -The `LetSemiApply` operator tests for the presence of a pattern predicate, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. -When a query contains multiple pattern predicates separated with `OR`, `LetSemiApply` will be used to evaluate the first of these. -It will record the result of evaluating the predicate but will leave any filtering to another operator. -In the example, `LetSemiApply` will be used to check for the presence of the `FRIENDS_WITH` relationship from each person. - - -.LetSemiApply -====== - -.Query -[source, cypher] ----- -PROFILE -CYPHER runtime=slotted -MATCH (other:Person) -WHERE (other)-[:FRIENDS_WITH]->(:Person) OR (other)-[:WORKS_IN]->(:Location) -RETURN other.name ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime SLOTTED - -Runtime version {neo4j-version-minor} - -+--------------------+-----------------------------------------+----------------+------+---------+------------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | -+--------------------+-----------------------------------------+----------------+------+---------+------------------------+ -| +ProduceResults | `other.name` | 13 | 14 | 0 | 0/0 | -| | +-----------------------------------------+----------------+------+---------+------------------------+ -| +Projection | other.name AS `other.name` | 13 | 14 | 14 | 1/0 | -| | +-----------------------------------------+----------------+------+---------+------------------------+ -| +SelectOrSemiApply | anon_9 | 14 | 14 | 0 | 0/0 | -| |\ +-----------------------------------------+----------------+------+---------+------------------------+ -| | +Filter | anon_7:Location | 14 | 0 | 4 | 0/0 | -| | | +-----------------------------------------+----------------+------+---------+------------------------+ -| | +Expand(All) | (other)-[anon_6:WORKS_IN]->(anon_7) | 14 | 4 | 15 | 8/0 | -| | | +-----------------------------------------+----------------+------+---------+------------------------+ -| | +Argument | other | 14 | 4 | 0 | 0/0 | -| | +-----------------------------------------+----------------+------+---------+------------------------+ -| +LetSemiApply | | 14 | 14 | 0 | 0/0 | -| |\ +-----------------------------------------+----------------+------+---------+------------------------+ -| | +Filter | anon_5:Person | 12 | 0 | 10 | 0/0 | -| | | +-----------------------------------------+----------------+------+---------+------------------------+ -| | +Expand(All) | (other)-[anon_4:FRIENDS_WITH]->(anon_5) | 12 | 10 | 51 | 28/0 | -| | | +-----------------------------------------+----------------+------+---------+------------------------+ -| | +Argument | other | 14 | 14 | 0 | 0/0 | -| | +-----------------------------------------+----------------+------+---------+------------------------+ -| +NodeByLabelScan | other:Person | 14 | 14 | 35 | 1/0 | -+--------------------+-----------------------------------------+----------------+------+---------+------------------------+ ++--------------------+-----------------------------------------+----------------+------+---------+------------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | ++--------------------+-----------------------------------------+----------------+------+---------+------------------------+ +| +ProduceResults | `other.name` | 13 | 14 | 0 | 0/0 | +| | +-----------------------------------------+----------------+------+---------+------------------------+ +| +Projection | other.name AS `other.name` | 13 | 14 | 14 | 1/0 | +| | +-----------------------------------------+----------------+------+---------+------------------------+ +| +SelectOrSemiApply | anon_9 | 14 | 14 | 0 | 0/0 | +| |\ +-----------------------------------------+----------------+------+---------+------------------------+ +| | +Filter | anon_7:Location | 14 | 0 | 4 | 0/0 | +| | | +-----------------------------------------+----------------+------+---------+------------------------+ +| | +Expand(All) | (other)-[anon_6:WORKS_IN]->(anon_7) | 14 | 4 | 15 | 8/0 | +| | | +-----------------------------------------+----------------+------+---------+------------------------+ +| | +Argument | other | 14 | 4 | 0 | 0/0 | +| | +-----------------------------------------+----------------+------+---------+------------------------+ +| +LetSemiApply | | 14 | 14 | 0 | 0/0 | +| |\ +-----------------------------------------+----------------+------+---------+------------------------+ +| | +Filter | anon_5:Person | 12 | 0 | 10 | 0/0 | +| | | +-----------------------------------------+----------------+------+---------+------------------------+ +| | +Expand(All) | (other)-[anon_4:FRIENDS_WITH]->(anon_5) | 12 | 10 | 51 | 28/0 | +| | | +-----------------------------------------+----------------+------+---------+------------------------+ +| | +Argument | other | 14 | 14 | 0 | 0/0 | +| | +-----------------------------------------+----------------+------+---------+------------------------+ +| +NodeByLabelScan | other:Person | 14 | 14 | 35 | 1/0 | ++--------------------+-----------------------------------------+----------------+------+---------+------------------------+ Total database accesses: 165, total allocated memory: 64 ---- @@ -2988,8 +2832,7 @@ Total database accesses: 165, total allocated memory: 64 [[query-plan-let-anti-semi-apply]] -== Let Anti Semi Apply -// LetAntiSemiApply +=== Let Anti Semi Apply The `LetAntiSemiApply` operator tests for the absence of a pattern, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. When a query contains multiple negated pattern predicates -- i.e. predicates separated with `OR`, where at least one predicate contains `NOT` -- `LetAntiSemiApply` will be used to evaluate the first of these. @@ -3051,8 +2894,7 @@ Total database accesses: 142, total allocated memory: 64 [[query-plan-select-or-semi-apply]] -== Select Or Semi Apply -// SelectOrSemiApply +=== Select Or Semi Apply The `SelectOrSemiApply` operator tests for the presence of a pattern predicate and evaluates a predicate, and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. @@ -3110,8 +2952,7 @@ Total database accesses: 148, total allocated memory: 2952 [[query-plan-select-or-anti-semi-apply]] -== Select Or Anti Semi Apply -// SelectOrAntiSemiApply +=== Select Or Anti Semi Apply The `SelectOrAntiSemiApply` operator is used to evaluate `OR` between a predicate and a negative pattern predicate (i.e. a pattern predicate preceded with `NOT`), and is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. If the predicate returns `true`, the pattern predicate is not tested. @@ -3170,8 +3011,7 @@ Total database accesses: 136, total allocated memory: 4208 [[query-plan-let-select-or-semi-apply]] -== Let Select Or Semi Apply -// LetSelectOrSemiApply +=== Let Select Or Semi Apply The `LetSelectOrSemiApply` operator is planned for pattern predicates that are combined with other predicates using `OR`. This is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. @@ -3232,8 +3072,7 @@ Total database accesses: 179, total allocated memory: 64 [[query-plan-let-select-or-anti-semi-apply]] -== Let Select Or Anti Semi Apply -// LetSelectOrAntiSemiApply +=== Let Select Or Anti Semi Apply The `LetSelectOrAntiSemiApply` operator is planned for negated pattern predicates -- i.e. pattern predicates preceded with `NOT` -- that are combined with other predicates using `OR`. This operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. @@ -3292,29 +3131,81 @@ Total database accesses: 208, total allocated memory: 64 ====== +[[query-plan-roll-up-apply]] +=== Roll Up Apply -[[query-plan-merge]] -== Merge -// ConditionalApply -- changed in 4.3 to Merge. -// AntiConditionalApply -- removed in 4.3 (by Merge). -// Merge +The `RollUpApply` operator is used to execute an expression which takes as input a pattern, and returns a list with content from the matched pattern; for example, when using a pattern expression or pattern comprehension in a query. +This operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. -The `Merge` operator will either read or create nodes and/or relationships. -If matches are found it will execute the provided `ON MATCH` operations foreach incoming row. -If no matches are found instead nodes and relationships are created and all `ON CREATE` operations are run. +.RollUpApply +====== + +.Query +[source, cypher] +---- +PROFILE +CYPHER runtime=slotted +MATCH (p:Person) +RETURN p.name, [(p)-[:WORKS_IN]->(location) | location.name] AS cities +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST +Runtime SLOTTED -.Merge +Runtime version {neo4j-version-minor} + ++-----------------+-----------------------------------+----------------+------+---------+------------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | ++-----------------+-----------------------------------+----------------+------+---------+------------------------+ +| +ProduceResults | `p.name`, cities | 14 | 14 | 0 | 0/0 | +| | +-----------------------------------+----------------+------+---------+------------------------+ +| +Projection | p.name AS `p.name` | 14 | 14 | 14 | 0/0 | +| | +-----------------------------------+----------------+------+---------+------------------------+ +| +RollUpApply | cities, anon_0 | 14 | 14 | 0 | 0/0 | +| |\ +-----------------------------------+----------------+------+---------+------------------------+ +| | +Projection | location.name AS anon_0 | 15 | 15 | 15 | 1/0 | +| | | +-----------------------------------+----------------+------+---------+------------------------+ +| | +Expand(All) | (p)-[anon_2:WORKS_IN]->(location) | 15 | 15 | 53 | 28/0 | +| | | +-----------------------------------+----------------+------+---------+------------------------+ +| | +Argument | p | 14 | 14 | 0 | 0/0 | +| | +-----------------------------------+----------------+------+---------+------------------------+ +| +NodeByLabelScan| p:Person | 14 | 14 | 35 | 1/0 | ++-----------------+-----------------------------------+----------------+------+---------+------------------------+ + +Total database accesses: 153, total allocated memory: 64 +---- + +====== + + + +[[query-plan-transaction-apply]] +=== TransactionApply + +`TransactionApply` works like the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator but will commit the current transaction after a specified number of rows. + +.TransactionApply ====== +[NOTE] +The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. +If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. + .Query [source, cypher] ---- PROFILE -MERGE (p:Person {name: 'Andy'}) -ON MATCH SET p.existed = true -ON CREATE SET p.existed = false +LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line +CALL (line) { + CREATE (a: Artist {name: line[0]}) + RETURN a +} IN TRANSACTIONS OF 100 ROWS +RETURN a; ---- .Query Plan @@ -3328,33 +3219,33 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | | 1 | 0 | 0 | | | | | -| | +-------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +EmptyResult | | 1 | 0 | 0 | | | | | -| | +-------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Merge | CREATE (p:Person {name: $autostring_0}), ON MATCH SET p.existed = true, | 1 | 1 | 2 | | | | | -| | | ON CREATE SET p.existed = false | | | | | | | | -| | +-------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 2/1 | 0.749 | Fused in Pipeline 0 | -+-----------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | a | 10 | 4 | 8 | 0 | | | | +| | +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | +| +TransactionApply | 1 | IN TRANSACTIONS OF $autoint_1 ROWS ON ERROR FAIL | 10 | 4 | 0 | 2152 | 0/0 | 2.036 | Fused in Pipeline 3 | +| |\ +----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Create | 2 | (a:Artist {name: line[$autoint_0]}) | 10 | 4 | 16 | | | | | +| | | +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 3 | line | 10 | 4 | 0 | 3472 | 0/0 | 32.746 | Fused in Pipeline 2 | +| | +----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +LoadCSV | 4 | line | 10 | 4 | 0 | 328 | | | In Pipeline 1 | ++-------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 4, total allocated memory: 184 +Total database accesses: 24, total allocated memory: 5472 ---- ====== -[[query-plan-locking-merge]] -== Locking Merge -// LockingMerge +[[query-plan-argument]] +=== Argument -The `LockingMerge` operator is similar to the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-merge[`Merge`] operator but will lock the start and end node when creating a relationship if necessary. +The `Argument` operator indicates the variable to be used as an argument to the right-hand side of an xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. -.LockingMerge +.Argument ====== .Query @@ -3399,125 +3290,18 @@ Total database accesses: 15, total allocated memory: 2232 ====== +[[query-plan-argument-tracker]] +=== Argument Tracker -[[query-plan-roll-up-apply]] -== Roll Up Apply -// RollUpApply - -The `RollUpApply` operator is used to execute an expression which takes as input a pattern, and returns a list with content from the matched pattern; for example, when using a pattern expression or pattern comprehension in a query. -This operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. - +The `ArgumentTracker` operator is used to ensure row-by-row semantics. +This restricts the xref:planning-and-tuning/runtimes/index.adoc[Cypher runtime] to not batch operations in larger chunks. -.RollUpApply +.ArgumentTracker ====== - -.Query -[source, cypher] ----- -PROFILE -CYPHER runtime=slotted -MATCH (p:Person) -RETURN p.name, [(p)-[:WORKS_IN]->(location) | location.name] AS cities ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime SLOTTED - -Runtime version {neo4j-version-minor} - -+-----------------+-----------------------------------+----------------+------+---------+------------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | -+-----------------+-----------------------------------+----------------+------+---------+------------------------+ -| +ProduceResults | `p.name`, cities | 14 | 14 | 0 | 0/0 | -| | +-----------------------------------+----------------+------+---------+------------------------+ -| +Projection | p.name AS `p.name` | 14 | 14 | 14 | 0/0 | -| | +-----------------------------------+----------------+------+---------+------------------------+ -| +RollUpApply | cities, anon_0 | 14 | 14 | 0 | 0/0 | -| |\ +-----------------------------------+----------------+------+---------+------------------------+ -| | +Projection | location.name AS anon_0 | 15 | 15 | 15 | 1/0 | -| | | +-----------------------------------+----------------+------+---------+------------------------+ -| | +Expand(All) | (p)-[anon_2:WORKS_IN]->(location) | 15 | 15 | 53 | 28/0 | -| | | +-----------------------------------+----------------+------+---------+------------------------+ -| | +Argument | p | 14 | 14 | 0 | 0/0 | -| | +-----------------------------------+----------------+------+---------+------------------------+ -| +NodeByLabelScan| p:Person | 14 | 14 | 35 | 1/0 | -+-----------------+-----------------------------------+----------------+------+---------+------------------------+ - -Total database accesses: 153, total allocated memory: 64 ----- - -====== - - -[[query-plan-argument]] -== Argument -// Argument - -The `Argument` operator indicates the variable to be used as an argument to the right-hand side of an xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator. - - -.Argument -====== - -.Query -[source, cypher] ----- -PROFILE -MATCH (s:Person {name: 'me'}) -MERGE (s)-[:FRIENDS_WITH]->(s) ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+-----------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | | 1 | 0 | 0 | | | | | -| | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +EmptyResult | 1 | | 1 | 0 | 0 | | | | | -| | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Apply | 2 | | 1 | 1 | 0 | | | | | -| |\ +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +LockingMerge | 3 | CREATE (s)-[anon_0:FRIENDS_WITH]->(s), LOCK(s) | 1 | 1 | 1 | | | | | -| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(Into) | 4 | (s)-[anon_0:FRIENDS_WITH]->(s) | 0 | 0 | 10 | 904 | | | | -| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | 5 | s | 1 | 3 | 0 | 2280 | 2/0 | 0.460 | Fused in Pipeline 1 | -| | +----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeIndexSeek | 6 | RANGE INDEX s:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 376 | 1/0 | 0.211 | In Pipeline 0 | -+-----------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 15, total allocated memory: 2232 ----- - -====== - -[[query-plan-argument-tracker]] -== Argument Tracker -// ArgumentTracker - -The `ArgumentTracker` operator is used to ensure row-by-row semantics. -This restricts the xref:planning-and-tuning/runtimes/index.adoc[Cypher runtime] to not batch operations in larger chunks. - -.ArgumentTracker -====== - -[NOTE] -The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. -If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. + +[NOTE] +The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. +If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. .Query [source, cypher] @@ -3566,24 +3350,24 @@ Total database accesses: 6, total allocated memory: 4136 ====== -// --- expand operators --- - -[[query-plan-expand-all]] -== Expand All -// Expand(All) +[[query-plan-cartesian-product]] +=== Cartesian Product -Given a start node, and depending on the pattern relationship, the `Expand(All)` operator will traverse incoming or outgoing relationships. +The `CartesianProduct` operator produces a cartesian product of the two inputs -- each row coming from the left child operator will be combined with all the rows from the right child operator. +`CartesianProduct` generally exhibits bad performance and ought to be avoided if possible. -.Expand(All) +.CartesianProduct ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person {name: 'me'})-[:FRIENDS_WITH]->(fof) -RETURN fof +MATCH + (p:Person), + (t:Team) +RETURN p, t ---- .Query Plan @@ -3597,40 +3381,51 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | fof | 1 | 2 | 0 | | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | (p)-[anon_0:FRIENDS_WITH]->(fof) | 1 | 2 | 5 | | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 4/1 | 1.137 | Fused in Pipeline 0 | -+-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++--------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p, t | 140 | 140 | 0 | | 2/0 | 1.917 | | +| | +----------+----------------+------+---------+----------------+------------------------+-----------+ | +| +CartesianProduct | | 140 | 140 | 0 | 1736 | | 1.209 | In Pipeline 2 | +| |\ +----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +NodeByLabelScan | t:Team | 10 | 10 | 11 | 136 | 1/0 | 1,145 | In Pipeline 1 | +| | +----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeByLabelScan | p:Person | 15 | 15 | 16 | 120 | 1/0 | 0,409 | In Pipeline 0 | ++--------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 7, total allocated memory: 184 +Total database accesses: 142, total allocated memory: 1816 ---- ====== -[[query-plan-expand-into]] -== Expand Into -// Expand(Into) +[[execution-plans-operators-hash-join-general]] +=== Hash joins in general -When both the start and end node have already been found, the `Expand(Into)` operator is used to find all relationships connecting the two nodes. -As both the start and end node of the relationship are already in scope, the node with the smallest degree will be used. -This can make a noticeable difference when dense nodes appear as end points. +Hash joins have two inputs: the build input and probe input. +The query planner assigns these roles so that the smaller of the two inputs is the build input. +The build input is pulled in eagerly, and is used to build a probe table. +Once this is complete, the probe table is checked for each row coming from the probe input side. + +In query plans, the build input is always the left operator, and the probe input the right operator. +[[query-plan-node-hash-join]] +=== Node Hash Join + +The `NodeHashJoin` operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#execution-plans-operators-hash-join-general[hash join]. +`NodeHashJoin` executes the hash join on node ids. +As primitive types and arrays can be used, it can be done very efficiently. -.Expand(Into) -====== +.NodeHashJoin +====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person {name: 'me'})-[:FRIENDS_WITH]->(fof)-->(p) -RETURN fof +MATCH (bob:Person {name: 'Bob'})-[:WORKS_IN]->(loc)<-[:WORKS_IN]-(matt:Person {name: 'Mattias'}) +USING JOIN ON loc +RETURN loc.name ---- .Query Plan @@ -3644,45 +3439,52 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | fof | 0 | 0 | 0 | | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | not anon_1 = anon_0 | 0 | 0 | 0 | | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Expand(Into) | (p)-[anon_0:FRIENDS_WITH]->(fof) | 0 | 0 | 6 | 896 | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | (p)<-[anon_1]-(fof) | 1 | 1 | 5 | | | | | -| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 4/1 | 0.546 | Fused in Pipeline 0 | -+-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | `loc.name` | 10 | 0 | 0 | | 0/0 | 0.000 | | +| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Projection | loc.name AS `loc.name` | 10 | 0 | 0 | | 0/0 | 0.000 | | +| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Filter | not anon_0 = anon_1 | 10 | 0 | 0 | | 0/0 | 0.000 | | +| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +NodeHashJoin | loc | 10 | 0 | 0 | 3688 | | 0.053 | In Pipeline 2 | +| |\ +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Expand(All) | (matt)-[anon_1:WORKS_IN]->(loc) | 19 | 0 | 0 | | | | | +| | | +----------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +NodeIndexSeek | RANGE INDEX matt:Person(name) WHERE name = $autostring_1 | 1 | 0 | 1 | 120 | 1/0 | 0.288 | Fused in Pipeline 1 | +| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Expand(All) | (bob)-[anon_0:WORKS_IN]->(loc) | 19 | 1 | 4 | | | | | +| | +----------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | RANGE INDEX bob:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 3/0 | 0.556 | Fused in Pipeline 0 | ++------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 13, total allocated memory: 976 +Total database accesses: 7, total allocated memory: 3888 ---- ====== -[[query-plan-optional-expand-all]] -== Optional Expand All -// OptionalExpand(All) +[[query-plan-value-hash-join]] +=== Value Hash Join -The `OptionalExpand(All)` operator is analogous to xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-expand-all[Expand(All)], apart from when no relationships match the direction, type and property predicates. -In this situation, `OptionalExpand(all)` will return a single row with the relationship and end node set to `null`. +The `ValueHashJoin` operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#execution-plans-operators-hash-join-general[hash join]. +This operator allows for arbitrary values to be used as the join key. +It is most frequently used to solve predicates of the form: `n.prop1 = m.prop2` (i.e. equality predicates between two property columns). -.OptionalExpand(All) +.ValueHashJoin ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -OPTIONAL MATCH (p)-[works_in:WORKS_IN]->(l) - WHERE works_in.duration > 180 -RETURN p, l +MATCH + (p:Person), + (q:Person) +WHERE p.age = q.age +RETURN p, q ---- .Query Plan @@ -3696,42 +3498,46 @@ Runtime version {neo4j-version-minor} Batch size 128 -+----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p, l | 14 | 15 | 1 | | | | | -| | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +OptionalExpand(All) | (p)-[works_in:WORKS_IN]->(l) WHERE works_in.duration > $autoint_0 | 14 | 15 | 53 | | | | | -| | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 5/0 | 1,233 | Fused in Pipeline 0 | -+----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p, q | 10 | 0 | 0 | | 0/0 | 0.000 | | +| | +---------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +ValueHashJoin | p.age = q.age| 10 | 0 | 0 | 344 | | | In Pipeline 2 | +| |\ +---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +NodeByLabelScan| q:Person | 15 | 0 | 0 | 120 | 0/0 | 0,000 | In Pipeline 1 | +| | +---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeByLabelScan | p:Person | 15 | 15 | 16 | 120 | 1/0 | 0,211 | In Pipeline 0 | ++-------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 125, total allocated memory: 184 +Total database accesses: 71, total allocated memory: 664 ---- ====== -[[query-plan-optional-expand-into]] -== Optional Expand Into -// OptionalExpand(Into) +[[query-plan-node-left-right-outer-hash-join]] +=== Node Left/Right Outer Hash Join -The `OptionalExpand(Into)` operator is analogous to xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-expand-into[Expand(Into)], apart from when no matching relationships are found. -In this situation, `OptionalExpand(Into)` will return a single row with the relationship and end node set to `null`. -As both the start and end node of the relationship are already in scope, the node with the smallest degree will be used. -This can make a noticeable difference when dense nodes appear as end points. + +The `NodeLeftOuterHashJoin` and `NodeRightOuterHashJoin` operators are variations of the xref::planning-and-tuning/operators/operators-detail.adoc#execution-plans-operators-hash-join-general[hash join]. +The query below can be planned with either a left or a right outer join. +The decision depends on the cardinalities of the left-hand and right-hand sides; i.e. how many rows would be returned, respectively, for `(a:Person)` and `(a)-->(b:Person)`. +If `(a:Person)` returns fewer results than `(a)-->(b:Person)`, a left outer join -- indicated by `NodeLeftOuterHashJoin` -- is planned. +On the other hand, if `(a:Person)` returns more results than `(a)-->(b:Person)`, a right outer join -- indicated by `NodeRightOuterHashJoin` -- is planned instead. -.OptionalExpand(Into) +.NodeRightOuterHashJoin ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person)-[works_in:WORKS_IN]->(l) -OPTIONAL MATCH (l)-->(p) -RETURN p +MATCH (a:Person) +OPTIONAL MATCH (a)-->(b:Person) +USING JOIN ON a +RETURN a.name, b.name ---- .Query Plan @@ -3745,40 +3551,58 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p | 15 | 15 | 0 | | | | | -| | +------------------------------+----------------+------+---------+----------------+ | | | -| +OptionalExpand(Into) | (l)-[anon_0]->(p) | 15 | 15 | 105 | 3360 | | | | -| | +------------------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | (p)-[works_in:WORKS_IN]->(l) | 15 | 15 | 39 | | | | | -| | +------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 7/0 | 3,925 | Fused in Pipeline 0 | -+-----------------------+--- --------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | `a.name`, `b.name` | 14 | 16 | 0 | | 0/0 | 0.102 | | +| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Projection | cache[a.name] AS `a.name`, cache[b.name] AS `b.name` | 14 | 16 | 8 | | 0/0 | 0.055 | | +| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +NodeRightOuterHashJoin | a | 14 | 16 | 0 | 4232 | | 0.269 | In Pipeline 2 | +| |\ +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +NodeByLabelScan | a:Person | 15 | 15 | 16 | 120 | 1/0 | 0,049 | In Pipeline 1 | +| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +CacheProperties | cache[b.name], cache[a.name] | 13 | 13 | 39 | | | | | +| | +------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | (b)<-[anon_0]-(a) | 13 | 13 | 55 | | | | | +| | +------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | b:Person | 15 | 15 | 16 | 120 | 5/0 | 1,150 | Fused in Pipeline 0 | ++-------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 215, total allocated memory: 3440 +Total database accesses: 211, total allocated memory: 4312 ---- ====== -[[query-plan-varlength-expand-all]] -== VarLength Expand All -// VarLengthExpand(All) +[[traversal-operators]] +== Traversal operators -Given a start node, the `VarLengthExpand(All)` operator will traverse variable-length and quantified relationships. +Traversal operators enable complex graph traversals by defining how nodes and relationships are connected in a given pattern. +They allow Cypher to express and match various pattern types, such as xref:patterns/fixed-length-patterns.adoc[fixed-length patterns], xref:patterns/variable-length-patterns.adoc[variable-length patterns], xref:patterns/shortest-paths.adoc[shortest paths], and xref:patterns/non-linear-patterns.adoc[non-linear patterns]. -.VarLengthExpand(All) +[[query-plan-anti]] +=== Anti + +The `Anti` operator tests for the absence of a pattern. +If there are incoming rows, the `Anti` operator will yield no rows. +If there are no incoming rows, the `Anti` operator will yield a single row. + + +.Anti ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person)-[:FRIENDS_WITH *1..2]-(q:Person) -RETURN p, q +CYPHER runtime=pipelined +MATCH + (me:Person {name: 'me'}), + (other:Person) +WHERE NOT (me)-[:FRIENDS_WITH]->(other) +RETURN other.name ---- .Query Plan @@ -3792,40 +3616,54 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p, q | 40 | 48 | 0 | | | | | -| | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | q:Person | 40 | 48 | 96 | | | | | -| | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +VarLengthExpand(All) | (p)-[anon_0:FRIENDS_WITH*..2]-(q) | 40 | 48 | 151 | 128 | | | | -| | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 6/0 | 10,457 | Fused in Pipeline 0 | -+-----------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | `other.name` | 4 | 12 | 0 | | 0/0 | 0.068 | | +| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Projection | other.name AS `other.name` | 4 | 12 | 24 | | 2/0 | 0.111 | In Pipeline 4 | +| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Apply | | 4 | 12 | 0 | | 0/0 | | | +| |\ +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Anti | | 4 | 12 | 0 | 1256 | 0/0 | 0.084 | In Pipeline 4 | +| | | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Limit | 1 | 11 | 2 | 0 | 752 | | | | +| | | +--------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Expand(Into) | (me)-[anon_2:FRIENDS_WITH]->(other) | 1 | 2 | 81 | 2632 | | | | +| | | +--------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | me, other | 14 | 14 | 0 | 3192 | 1/0 | 0.904 | Fused in Pipeline 3 | +| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +CartesianProduct | | 14 | 14 | 0 | 3672 | | 1.466 | In Pipeline 2 | +| |\ +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +NodeByLabelScan| other:Person | 14 | 14 | 35 | | | | | +| | +--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeIndexSeek | RANGE INDEX me:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 0/1 | 0.493 | In Pipeline 0 | ++-------------------+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 318, total allocated memory: 208 +Total database accesses: 178, total allocated memory: 6744 ---- ====== -[[query-plan-varlength-expand-into]] -== VarLength Expand Into -// VarLengthExpand(Into) +[[query-plan-optional]] +=== Optional -When both the start and end node have already been found, the `VarLengthExpand(Into)` operator is used to find all variable-length and quantified relationships connecting the two nodes. +The `Optional` operator is used to solve some xref::clauses/optional-match.adoc[OPTIONAL MATCH] queries. +It will pull data from its source, simply passing it through if any data exists. +However, if no data is returned by its source, `Optional` will yield a single row with all columns set to `null`. -.VarLengthExpand(Into) +.Optional ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person)-[:FRIENDS_WITH *1..2]-(p:Person) -RETURN p +MATCH (p:Person {name: 'me'}) +OPTIONAL MATCH (q:Person {name: 'Lulu'}) +RETURN p, q ---- .Query Plan @@ -3839,46 +3677,40 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p | 3 | 4 | 0 | | | | | -| | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +VarLengthExpand(Into) | (p)-[anon_0:FRIENDS_WITH*..2]-(p) | 3 | 4 | 151 | 128 | | | | -| | +-----------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 6/0 | 0,797 | Fused in Pipeline 0 | -+------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - ++------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | p, q | 1 | 1 | 0 | | 2/0 | 0.079 | In Pipeline 2 | +| | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +Apply | | 1 | 1 | 0 | | 0/0 | 0.096 | | +| |\ +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| | +Optional | p | 1 | 1 | 0 | 768 | 0/0 | 0.043 | In Pipeline 2 | +| | | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| | +NodeIndexSeek | RANGE INDEX q:Person(name) WHERE name = $autostring_1 | 1 | 0 | 1 | 2152 | 1/0 | 0.098 | In Pipeline 1 | +| | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 0/1 | 0.364 | In Pipeline 0 | ++------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -Total database accesses: 222, total allocated memory: 192 +Total database accesses: 3, total allocated memory: 3000 ---- ====== +[[query-plan-expand-all]] +=== Expand All -[[query-plan-varlength-expand-pruning]] -== VarLength Expand Pruning -// VarLengthExpand(Pruning) - -Given a start node, the `VarLengthExpand(Pruning)` operator will traverse variable-length and quantified relationships much like the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-varlength-expand-all[`VarLengthExpand(All)`] operator. -However, as an optimization, some paths will not be explored if they are guaranteed to produce an end node that has already been found (by means of a previous path traversal). - -This kind of expand is only planned when: - -* The individual paths are not of interest. -* The relationships have an upper bound. - -The `VarLengthExpand(Pruning)` operator guarantees that all the end nodes produced will be unique. +Given a start node, and depending on the pattern relationship, the `Expand(All)` operator will traverse incoming or outgoing relationships. -.VarLengthExpand(Pruning) +.Expand(All) ====== + .Query [source, cypher] ---- PROFILE -MATCH (p:Person)-[:FRIENDS_WITH *3..4]-(q:Person) -RETURN DISTINCT p, q +MATCH (p:Person {name: 'me'})-[:FRIENDS_WITH]->(fof) +RETURN fof ---- .Query Plan @@ -3892,48 +3724,39 @@ Runtime version {neo4j-version-minor} Batch size 128 -+---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------+ -| +ProduceResults | 0 | p, q | 0 | 0 | 0 | | 0/0 | 0.005 | | | -| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +OrderedDistinct | 1 | p, q | 0 | 0 | 0 | 40 | 0/0 | 0.014 | | | -| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +Filter | 2 | q:Person | 0 | 0 | 0 | | 0/0 | 0.014 | | | -| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +VarLengthExpand(Pruning) | 3 | (p)-[:FRIENDS_WITH*3..4]-(q) | 1 | 0 | 15 | 400 | | | | In Pipeline 1 | -| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+ +---------------+ -| +NodeByLabelScan | 4 | p:Person | 14 | 14 | 15 | 120 | 1/0 | 0.020 | p ASC | In Pipeline 0 | -+---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------+ ++-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | fof | 1 | 2 | 0 | | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | (p)-[anon_0:FRIENDS_WITH]->(fof) | 1 | 2 | 5 | | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 4/1 | 1.137 | Fused in Pipeline 0 | ++-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 30, total allocated memory: 480 +Total database accesses: 7, total allocated memory: 184 ---- ====== -[[query-plan-breadth-first-varlength-expand-pruning-bfs-all]] -== Breadth First VarLength Expand Pruning -// VarLengthExpand(Pruning,BFS) -// New in 5.0 - -Given a start node, the `VarLengthExpand(Pruning,BFS,All)` operator traverses variable-length and quantified relationships much like the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-varlength-expand-all[`VarLengthExpand(All)`] operator. -However, as an optimization, it instead performs a breadth-first search (BFS) and while expanding, some paths are not explored if they are guaranteed to produce an end node that has already been found (by means of a previous path traversal). -This is only used in cases where the individual paths are not of interest. +[[query-plan-expand-into]] +=== Expand Into -This kind of expand is only planned when: +When both the start and end node have already been found, the `Expand(Into)` operator is used to find all relationships connecting the two nodes. +As both the start and end node of the relationship are already in scope, the node with the smallest degree will be used. +This can make a noticeable difference when dense nodes appear as end points. -* The individual paths are not of interest. -* The lower bound is either `0` or `1` (default). -This operator guarantees that all the end nodes produced are unique. +.Expand(Into) +====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person)-[:FRIENDS_WITH *..4]-(q:Person) -RETURN DISTINCT p, q +MATCH (p:Person {name: 'me'})-[:FRIENDS_WITH]->(fof)-->(p) +RETURN fof ---- .Query Plan @@ -3947,42 +3770,44 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+-----------------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProduceResults | 0 | p, q | 12 | 0 | 0 | 0 | | | | | -| | +----+------------------------------+----------------+------+---------+----------------+ | | | | -| +OrderedDistinct | 1 | p, q | 12 | 0 | 0 | 40 | | | | | -| | +----+------------------------------+----------------+------+---------+----------------+ | | | | -| +Filter | 2 | q:Person | 13 | 0 | 0 | | | | | | -| | +----+------------------------------+----------------+------+---------+----------------+ | | | | -| +VarLengthExpand(Pruning,BFS,All) | 3 | (p)-[:FRIENDS_WITH*..4]-(q) | 13 | 0 | 38 | 952 | | | | | -| | +----+------------------------------+----------------+------+---------+----------------+ | | | | -| +NodeByLabelScan | 4 | p:Person | 10 | 10 | 11 | 248 | 3/0 | 4.662 | p ASC | Fused in Pipeline 0 | -+-----------------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ ++-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | fof | 0 | 0 | 0 | | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | not anon_1 = anon_0 | 0 | 0 | 0 | | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(Into) | (p)-[anon_0:FRIENDS_WITH]->(fof) | 0 | 0 | 6 | 896 | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | (p)<-[anon_1]-(fof) | 1 | 1 | 5 | | | | | +| | +-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 4/1 | 0.546 | Fused in Pipeline 0 | ++-----------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 49, total allocated memory: 1200 +Total database accesses: 13, total allocated memory: 976 ---- -[role=label--new-5.9] -[[query-plan-repeat]] -== Repeat (Trail) -// Repeat(Trail) +====== -Given a start node, the `Repeat(Trail)` operator will traverse xref::patterns/variable-length-patterns.adoc#quantified-path-patterns[quantified path patterns] that cannot be solved (or solved efficiently) with the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-varlength-expand-all[`VarLengthExpand(All)`] operator. -Similar to an xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator, it takes a single row from the left-hand side and applies the operators on the right-hand side. -In contrast to `Apply`, however, it repeatedly applies these operators in accordance with the quantifiers on the quantified path pattern. -In the following example, the operator will repeat twice and produce rows for both repetitions. -.Repeat(Trail) +[[query-plan-optional-expand-all]] +=== Optional Expand All + +The `OptionalExpand(All)` operator is analogous to xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-expand-all[Expand(All)], apart from when no relationships match the direction, type and property predicates. +In this situation, `OptionalExpand(all)` will return a single row with the relationship and end node set to `null`. + + +.OptionalExpand(All) ====== .Query [source, cypher] ---- PROFILE -MATCH (me:Person) ((a)-[:FRIENDS_WITH]-(b)-[:FRIENDS_WITH]-(c) WHERE a.name <> b.name AND a.name <> c.name AND b.name <> c.name){1,2} (friend:Person) -RETURN me, friend +MATCH (p:Person) +OPTIONAL MATCH (p)-[works_in:WORKS_IN]->(l) + WHERE works_in.duration > 180 +RETURN p, l ---- .Query Plan @@ -3996,53 +3821,41 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | me, friend | 2 | 34 | 136 | 0 | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | 1 | friend:Person | 2 | 34 | 68 | | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NullifyMetadata | 9 | | 2 | 34 | 0 | | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Repeat(Trail) | 2 | (me) (...){1, 2} (friend) | 2 | 34 | 0 | 29792 | 0/0 | 1.696 | Fused in Pipeline 2 | -| |\ +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Filter | 3 | NOT anon_5 = anon_3 AND (NOT cache[a.name] = cache[c.name] AND NOT cache[b.name] = cache[c.name]) AN | 1 | 34 | 92 | | | | | -| | | | | D isRepeatTrailUnique(anon_5) | | | | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(All) | 4 | (b)-[anon_5:FRIENDS_WITH]-(c) | 3 | 92 | 138 | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Filter | 5 | NOT cache[a.name] = cache[b.name] AND isRepeatTrailUnique(anon_3) | 5 | 46 | 198 | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(All) | 6 | (a)-[anon_3:FRIENDS_WITH]-(b) | 10 | 66 | 100 | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | 7 | a | 15 | 34 | 0 | 15672 | 2/0 | 3.245 | Fused in Pipeline 1 | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeByLabelScan | 8 | me:Person | 14 | 14 | 15 | 376 | 1/0 | 0.107 | In Pipeline 0 | -+------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p, l | 14 | 15 | 1 | | | | | +| | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +OptionalExpand(All) | (p)-[works_in:WORKS_IN]->(l) WHERE works_in.duration > $autoint_0 | 14 | 15 | 53 | | | | | +| | +-------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 5/0 | 1,233 | Fused in Pipeline 0 | ++----------------------+-------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 747, total allocated memory: 45832 +Total database accesses: 125, total allocated memory: 184 ---- ====== -[role=label--new-5.9] -[[query-plan-nullify-metadata]] -== Nullify Metadata -// NullifyMetadata -`NullifyMetadata` is responsible for cleaning up the state produced by xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-repeat[`Repeat(Trail)`]. -It is only planned directly after `Repeat(Trail)`. +[[query-plan-optional-expand-into]] +=== Optional Expand Into -.NullifyMetadata +The `OptionalExpand(Into)` operator is analogous to xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-expand-into[Expand(Into)], apart from when no matching relationships are found. +In this situation, `OptionalExpand(Into)` will return a single row with the relationship and end node set to `null`. +As both the start and end node of the relationship are already in scope, the node with the smallest degree will be used. +This can make a noticeable difference when dense nodes appear as end points. + + +.OptionalExpand(Into) ====== .Query [source, cypher] ---- PROFILE -MATCH (me:Person) ((a)-[:FRIENDS_WITH]-(b)-[:FRIENDS_WITH]-(c) WHERE a.name <> b.name AND a.name <> c.name AND b.name <> c.name){1,2} (friend:Person) -RETURN me, friend +MATCH (p:Person)-[works_in:WORKS_IN]->(l) +OPTIONAL MATCH (l)-->(p) +RETURN p ---- .Query Plan @@ -4056,54 +3869,39 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | me, friend | 2 | 34 | 136 | 0 | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | 1 | friend:Person | 2 | 34 | 68 | | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NullifyMetadata | 9 | | 2 | 34 | 0 | | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Repeat(Trail) | 2 | (me) (...){1, 2} (friend) | 2 | 34 | 0 | 29792 | 0/0 | 1.696 | Fused in Pipeline 2 | -| |\ +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Filter | 3 | NOT anon_5 = anon_3 AND (NOT cache[a.name] = cache[c.name] AND NOT cache[b.name] = cache[c.name]) AN | 1 | 34 | 92 | | | | | -| | | | | D isRepeatTrailUnique(anon_5) | | | | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(All) | 4 | (b)-[anon_5:FRIENDS_WITH]-(c) | 3 | 92 | 138 | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Filter | 5 | NOT cache[a.name] = cache[b.name] AND isRepeatTrailUnique(anon_3) | 5 | 46 | 198 | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Expand(All) | 6 | (a)-[anon_3:FRIENDS_WITH]-(b) | 10 | 66 | 100 | | | | | -| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | 7 | a | 15 | 34 | 0 | 15672 | 2/0 | 3.245 | Fused in Pipeline 1 | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeByLabelScan | 8 | me:Person | 14 | 14 | 15 | 376 | 1/0 | 0.107 | In Pipeline 0 | -+------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p | 15 | 15 | 0 | | | | | +| | +------------------------------+----------------+------+---------+----------------+ | | | +| +OptionalExpand(Into) | (l)-[anon_0]->(p) | 15 | 15 | 105 | 3360 | | | | +| | +------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | (p)-[works_in:WORKS_IN]->(l) | 15 | 15 | 39 | | | | | +| | +------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 7/0 | 3,925 | Fused in Pipeline 0 | ++-----------------------+--- --------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 747, total allocated memory: 45832 +Total database accesses: 215, total allocated memory: 3440 ---- ====== -[[query-plan-assert-same-node]] -== Assert Same Node -// AssertSameNode -The `AssertSameNode` operator is used to ensure that no node property uniqueness constraints are violated in the slotted and interpreted runtime. -The example looks for the presence of a team node with the supplied name and id, and if one does not exist, it will be created. -Owing to the existence of two node property uniqueness constraints on `:Team(name)` and `:Team(id)`, any node that would be found by the `UniqueIndexSeek` operator must be the very same node or the constraints would be violated. +[[query-plan-varlength-expand-all]] +=== VarLength Expand All +Given a start node, the `VarLengthExpand(All)` operator will traverse variable-length and quantified relationships. -.AssertSameNode + +.VarLengthExpand(All) ====== .Query [source, cypher] ---- PROFILE -CYPHER runtime=slotted -MERGE (t:Team {name: 'Engineering', id: 42}) +MATCH (p:Person)-[:FRIENDS_WITH *1..2]-(q:Person) +RETURN p, q ---- .Query Plan @@ -4111,52 +3909,45 @@ MERGE (t:Team {name: 'Engineering', id: 42}) ---- Planner COST -Runtime SLOTTED +Runtime PIPELINED Runtime version {neo4j-version-minor} -+---------------------------------+-------------------------------------------------------+----------------+------+---------+------------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | -+---------------------------------+-------------------------------------------------------+----------------+------+---------+------------------------+ -| +ProduceResults | | 1 | 0 | 0 | 0/0 | -| | +-------------------------------------------------------+----------------+------+---------+------------------------+ -| +EmptyResult | | 1 | 0 | 0 | 0/0 | -| | +-------------------------------------------------------+----------------+------+---------+------------------------+ -| +Merge | CREATE (t:Team {name: $autostring_0, id: $autoint_1}) | 1 | 1 | 0 | 0/0 | -| | +-------------------------------------------------------+----------------+------+---------+------------------------+ -| +AssertSameNode | t | 0 | 1 | 0 | 0/0 | -| |\ +-------------------------------------------------------+----------------+------+---------+------------------------+ -| | +NodeUniqueIndexSeek(Locking) | UNIQUE t:Team(id) WHERE id = $autoint_1 | 1 | 1 | 1 | 0/1 | -| | +-------------------------------------------------------+----------------+------+---------+------------------------+ -| +NodeUniqueIndexSeek(Locking) | UNIQUE t:Team(name) WHERE name = $autostring_0 | 1 | 1 | 1 | 0/1 | -+---------------------------------+-------------------------------------------------------+----------------+------+---------+------------------------+ +Batch size 128 -Total database accesses: 2, total allocated memory: 64 ++-----------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p, q | 40 | 48 | 0 | | | | | +| | +-----------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | q:Person | 40 | 48 | 96 | | | | | +| | +-----------------------------------+----------------+------+---------+----------------+ | | | +| +VarLengthExpand(All) | (p)-[anon_0:FRIENDS_WITH*..2]-(q) | 40 | 48 | 151 | 128 | | | | +| | +-----------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 6/0 | 10,457 | Fused in Pipeline 0 | ++-----------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 318, total allocated memory: 208 ---- ====== -[role=label--new-5.8] -[[query-plan-assert-same-relationship]] -== Assert Same Relationship -// AssertSameRelationship +[[query-plan-varlength-expand-into]] +=== VarLength Expand Into -The `AssertSameRelationship` operator is used to ensure that no relationship property uniqueness constraints are violated in the slotted and interpreted runtime. -The example looks for the presence of a `WORKS_IN` relationship with the supplied `id` and `badgeNumber`. -If it can't be found, then it will be created. -Owing to the existence of two property uniqueness constraints on `:WORKS_IN(id)` and `:WORKS_IN(badgeNumber)`, any relationship that would be found by the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-unique-index-seek[`DirectedRelationshipUniqueIndexSeek`] operator must be the very same relationship or the constraints would be violated. +When both the start and end node have already been found, the `VarLengthExpand(Into)` operator is used to find all variable-length and quantified relationships connecting the two nodes. -.AssertSameRelationship +.VarLengthExpand(Into) ====== .Query [source, cypher] ---- PROFILE -CYPHER runtime=slotted -MERGE (person)-[work:WORKS_IN {id: 0, badgeNumber: 4332}]->(location) +MATCH (p:Person)-[:FRIENDS_WITH *1..2]-(p:Person) +RETURN p ---- .Query Plan @@ -4164,51 +3955,52 @@ MERGE (person)-[work:WORKS_IN {id: 0, badgeNumber: 4332}]->(location) ---- Planner COST -Runtime SLOTTED +Runtime PIPELINED Runtime version {neo4j-version-minor} -+-------------------------------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | -+-------------------------------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ -| +ProduceResults | 0 | | 1 | 0 | 0 | 0/0 | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ -| +EmptyResult | 1 | | 1 | 0 | 0 | 0/0 | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ -| +Merge | 2 | CREATE (person), (location), (person)-[work:WORKS_IN {id: $autoint_0, badgeNumber: $autoint_1}]->(lo | 1 | 1 | 0 | 0/0 | -| | | | cation) | | | | | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ -| +AssertSameRelationship | 3 | work | 0 | 1 | 0 | 0/0 | -| |\ +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ -| | +DirectedRelationshipUniqueIndexSeek(Locking) | 4 | RANGE INDEX (person)-[work:WORKS_IN(badgeNumber)]->(location) WHERE badgeNumber = $autoint_1 | 1 | 1 | 1 | 0/1 | -| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ -| +DirectedRelationshipUniqueIndexSeek(Locking) | 5 | RANGE INDEX (person)-[work:WORKS_IN(id)]->(location) WHERE id = $autoint_0 | 1 | 1 | 1 | 1/1 | -+-------------------------------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +Batch size 128 -Total database accesses: 2, total allocated memory: 64 ++------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p | 3 | 4 | 0 | | | | | +| | +-----------------------------------+----------------+------+---------+----------------+ | | | +| +VarLengthExpand(Into) | (p)-[anon_0:FRIENDS_WITH*..2]-(p) | 3 | 4 | 151 | 128 | | | | +| | +-----------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | p:Person | 14 | 14 | 15 | 120 | 6/0 | 0,797 | Fused in Pipeline 0 | ++------------------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + + +Total database accesses: 222, total allocated memory: 192 ---- ====== -// DropResult -- removed in 4.3 +[[query-plan-varlength-expand-pruning]] +=== VarLength Expand Pruning +// VarLengthExpand(Pruning) +Given a start node, the `VarLengthExpand(Pruning)` operator will traverse variable-length and quantified relationships much like the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-varlength-expand-all[`VarLengthExpand(All)`] operator. +However, as an optimization, some paths will not be explored if they are guaranteed to produce an end node that has already been found (by means of a previous path traversal). -[[query-plan-empty-result]] -== Empty Result -// EmptyResult +This kind of expand is only planned when: -The `EmptyResult` operator eagerly loads all incoming data and discards it. +* The individual paths are not of interest. +* The relationships have an upper bound. + +The `VarLengthExpand(Pruning)` operator guarantees that all the end nodes produced will be unique. -.EmptyResult +.VarLengthExpand(Pruning) ====== - .Query [source, cypher] ---- PROFILE -CREATE (:Person) +MATCH (p:Person)-[:FRIENDS_WITH *3..4]-(q:Person) +RETURN DISTINCT p, q ---- .Query Plan @@ -4222,39 +4014,48 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+-----------------+----------------+------+---------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+-----------------+----------------+------+---------+------------------------+-----------+---------------------+ -| +ProduceResults | | 1 | 0 | 0 | | | | -| | +-----------------+----------------+------+---------+ | | | -| +EmptyResult | | 1 | 0 | 0 | | | | -| | +-----------------+----------------+------+---------+ | | | -| +Create | (anon_0:Person) | 1 | 1 | 1 | 0/0 | 0.000 | Fused in Pipeline 0 | -+-----------------+-----------------+----------------+------+---------+------------------------+-----------+---------------------+ ++---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------+ +| +ProduceResults | 0 | p, q | 0 | 0 | 0 | | 0/0 | 0.005 | | | +| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +OrderedDistinct | 1 | p, q | 0 | 0 | 0 | 40 | 0/0 | 0.014 | | | +| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +Filter | 2 | q:Person | 0 | 0 | 0 | | 0/0 | 0.014 | | | +| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +VarLengthExpand(Pruning) | 3 | (p)-[:FRIENDS_WITH*3..4]-(q) | 1 | 0 | 15 | 400 | | | | In Pipeline 1 | +| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+ +---------------+ +| +NodeByLabelScan | 4 | p:Person | 14 | 14 | 15 | 120 | 1/0 | 0.020 | p ASC | In Pipeline 0 | ++---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------+ -Total database accesses: 1, total allocated memory: 184 +Total database accesses: 30, total allocated memory: 480 ---- ====== -[[query-plan-produce-results]] -== Produce Results -// ProduceResults +[[query-plan-breadth-first-varlength-expand-pruning-bfs-all]] +=== Breadth First VarLength Expand Pruning +// VarLengthExpand(Pruning,BFS) +// New in 5.0 -The `ProduceResults` operator prepares the result so that it is consumable by the user, such as transforming internal values to user values. -It is present in every single query that returns data to the user, and has little bearing on performance optimisation. +Given a start node, the `VarLengthExpand(Pruning,BFS,All)` operator traverses variable-length and quantified relationships much like the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-varlength-expand-all[`VarLengthExpand(All)`] operator. +However, as an optimization, it instead performs a breadth-first search (BFS) and while expanding, some paths are not explored if they are guaranteed to produce an end node that has already been found (by means of a previous path traversal). +This is only used in cases where the individual paths are not of interest. +This kind of expand is only planned when: -.ProduceResults -====== +* The individual paths are not of interest. +* The lower bound is either `0` or `1` (default). + +This operator guarantees that all the end nodes produced are unique. .Query [source, cypher] ---- PROFILE -MATCH (n) -RETURN n +MATCH (p:Person)-[:FRIENDS_WITH *..4]-(q:Person) +RETURN DISTINCT p, q ---- .Query Plan @@ -4268,37 +4069,42 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | n | 35 | 35 | 0 | | | | | -| | +---------+----------------+------+---------+----------------+ | | | -| +AllNodesScan | n | 35 | 35 | 36 | 120 | 3/0 | 0.508 | Fused in Pipeline 0 | -+-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-----------------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProduceResults | 0 | p, q | 12 | 0 | 0 | 0 | | | | | +| | +----+------------------------------+----------------+------+---------+----------------+ | | | | +| +OrderedDistinct | 1 | p, q | 12 | 0 | 0 | 40 | | | | | +| | +----+------------------------------+----------------+------+---------+----------------+ | | | | +| +Filter | 2 | q:Person | 13 | 0 | 0 | | | | | | +| | +----+------------------------------+----------------+------+---------+----------------+ | | | | +| +VarLengthExpand(Pruning,BFS,All) | 3 | (p)-[:FRIENDS_WITH*..4]-(q) | 13 | 0 | 38 | 952 | | | | | +| | +----+------------------------------+----------------+------+---------+----------------+ | | | | +| +NodeByLabelScan | 4 | p:Person | 10 | 10 | 11 | 248 | 3/0 | 4.662 | p ASC | Fused in Pipeline 0 | ++-----------------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -Total database accesses: 36, total allocated memory: 184 +Total database accesses: 49, total allocated memory: 1200 ---- -====== +[role=label--new-5.9] +[[query-plan-repeat]] +=== Repeat (Trail) +// Repeat(Trail) +Given a start node, the `Repeat(Trail)` operator will traverse xref::patterns/variable-length-patterns.adoc#quantified-path-patterns[quantified path patterns] that cannot be solved (or solved efficiently) with the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-varlength-expand-all[`VarLengthExpand(All)`] operator. +Similar to an xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[`Apply`] operator, it takes a single row from the left-hand side and applies the operators on the right-hand side. +In contrast to `Apply`, however, it repeatedly applies these operators in accordance with the quantifiers on the quantified path pattern. +In the following example, the operator will repeat twice and produce rows for both repetitions. -[[query-plan-load-csv]] -== Load CSV -// LoadCSV - -The `LoadCSV` operator loads data from a CSV source into the query. -It is used whenever the xref::clauses/load-csv.adoc[LOAD CSV] clause is used in a query. - - -.LoadCSV -====== +.Repeat(Trail) +====== .Query [source, cypher] ---- PROFILE -LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line -RETURN line +MATCH (me:Person) ((a)-[:FRIENDS_WITH]-(b)-[:FRIENDS_WITH]-(c) WHERE a.name <> b.name AND a.name <> c.name AND b.name <> c.name){1,2} (friend:Person) +RETURN me, friend ---- .Query Plan @@ -4312,56 +4118,114 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | line | 10 | 4 | 0 | | 0/0 | 0.210 | | -| | +---------+----------------+------+---------+----------------+------------------------+-----------+ | -| +LoadCSV | line | 10 | 4 | 0 | 72 | | | In Pipeline 1 | -+-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------+ ++------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | me, friend | 2 | 34 | 136 | 0 | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 1 | friend:Person | 2 | 34 | 68 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NullifyMetadata | 9 | | 2 | 34 | 0 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Repeat(Trail) | 2 | (me) (...){1, 2} (friend) | 2 | 34 | 0 | 29792 | 0/0 | 1.696 | Fused in Pipeline 2 | +| |\ +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Filter | 3 | NOT anon_5 = anon_3 AND (NOT cache[a.name] = cache[c.name] AND NOT cache[b.name] = cache[c.name]) AN | 1 | 34 | 92 | | | | | +| | | | | D isRepeatTrailUnique(anon_5) | | | | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Expand(All) | 4 | (b)-[anon_5:FRIENDS_WITH]-(c) | 3 | 92 | 138 | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Filter | 5 | NOT cache[a.name] = cache[b.name] AND isRepeatTrailUnique(anon_3) | 5 | 46 | 198 | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Expand(All) | 6 | (a)-[anon_3:FRIENDS_WITH]-(b) | 10 | 66 | 100 | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 7 | a | 15 | 34 | 0 | 15672 | 2/0 | 3.245 | Fused in Pipeline 1 | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeByLabelScan | 8 | me:Person | 14 | 14 | 15 | 376 | 1/0 | 0.107 | In Pipeline 0 | ++------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 0, total allocated memory: 184 +Total database accesses: 747, total allocated memory: 45832 ---- ====== +[role=label--new-5.9] +[[query-plan-nullify-metadata]] +=== Nullify Metadata -[[execution-plans-operators-hash-join-general]] -== Hash joins in general +`NullifyMetadata` is responsible for cleaning up the state produced by xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-repeat[`Repeat(Trail)`]. +It is only planned directly after `Repeat(Trail)`. -Hash joins have two inputs: the build input and probe input. -The query planner assigns these roles so that the smaller of the two inputs is the build input. -The build input is pulled in eagerly, and is used to build a probe table. -Once this is complete, the probe table is checked for each row coming from the probe input side. +.NullifyMetadata +====== -In query plans, the build input is always the left operator, and the probe input the right operator. +.Query +[source, cypher] +---- +PROFILE +MATCH (me:Person) ((a)-[:FRIENDS_WITH]-(b)-[:FRIENDS_WITH]-(c) WHERE a.name <> b.name AND a.name <> c.name AND b.name <> c.name){1,2} (friend:Person) +RETURN me, friend +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST -There are four hash join operators: +Runtime PIPELINED -* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-hash-join[NodeHashJoin] -* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-value-hash-join[ValueHashJoin] -* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-left-right-outer-hash-join[NodeLeftOuterHashJoin] -* xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-node-left-right-outer-hash-join[NodeRightOuterHashJoin] +Runtime version {neo4j-version-minor} +Batch size 128 -[[query-plan-node-hash-join]] -== Node Hash Join -// NodeHashJoin ++------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | me, friend | 2 | 34 | 136 | 0 | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 1 | friend:Person | 2 | 34 | 68 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NullifyMetadata | 9 | | 2 | 34 | 0 | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Repeat(Trail) | 2 | (me) (...){1, 2} (friend) | 2 | 34 | 0 | 29792 | 0/0 | 1.696 | Fused in Pipeline 2 | +| |\ +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Filter | 3 | NOT anon_5 = anon_3 AND (NOT cache[a.name] = cache[c.name] AND NOT cache[b.name] = cache[c.name]) AN | 1 | 34 | 92 | | | | | +| | | | | D isRepeatTrailUnique(anon_5) | | | | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Expand(All) | 4 | (b)-[anon_5:FRIENDS_WITH]-(c) | 3 | 92 | 138 | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Filter | 5 | NOT cache[a.name] = cache[b.name] AND isRepeatTrailUnique(anon_3) | 5 | 46 | 198 | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Expand(All) | 6 | (a)-[anon_3:FRIENDS_WITH]-(b) | 10 | 66 | 100 | | | | | +| | | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 7 | a | 15 | 34 | 0 | 15672 | 2/0 | 3.245 | Fused in Pipeline 1 | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeByLabelScan | 8 | me:Person | 14 | 14 | 15 | 376 | 1/0 | 0.107 | In Pipeline 0 | ++------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -The `NodeHashJoin` operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#execution-plans-operators-hash-join-general[hash join]. -`NodeHashJoin` executes the hash join on node ids. -As primitive types and arrays can be used, it can be done very efficiently. +Total database accesses: 747, total allocated memory: 45832 +---- +====== -.NodeHashJoin +[[query-plan-shortest-path]] +=== Shortest path +// ShortestPath + +The `ShortestPath` operator finds one or all shortest paths between two previously matched node variables. +This operator is used for the xref:patterns/reference.adoc#shortest-functions[`shortestPath()` and `allShortestPaths`] functions. + +.ShortestPath ====== + .Query [source, cypher] ---- PROFILE -MATCH (bob:Person {name: 'Bob'})-[:WORKS_IN]->(loc)<-[:WORKS_IN]-(matt:Person {name: 'Mattias'}) -USING JOIN ON loc -RETURN loc.name +MATCH + (andy:Person {name: 'Andy'}), + (mattias:Person {name: 'Mattias'}), + p = shortestPath((andy)-[*]-(mattias)) +RETURN p ---- .Query Plan @@ -4375,42 +4239,41 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | `loc.name` | 10 | 0 | 0 | | 0/0 | 0.000 | | -| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Projection | loc.name AS `loc.name` | 10 | 0 | 0 | | 0/0 | 0.000 | | -| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Filter | not anon_0 = anon_1 | 10 | 0 | 0 | | 0/0 | 0.000 | | -| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +NodeHashJoin | loc | 10 | 0 | 0 | 3688 | | 0.053 | In Pipeline 2 | -| |\ +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Expand(All) | (matt)-[anon_1:WORKS_IN]->(loc) | 19 | 0 | 0 | | | | | -| | | +----------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +NodeIndexSeek | RANGE INDEX matt:Person(name) WHERE name = $autostring_1 | 1 | 0 | 1 | 120 | 1/0 | 0.288 | Fused in Pipeline 1 | -| | +----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Expand(All) | (bob)-[anon_0:WORKS_IN]->(loc) | 19 | 1 | 4 | | | | | -| | +----------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | RANGE INDEX bob:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 3/0 | 0.556 | Fused in Pipeline 0 | -+------------------+----------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++---------------------+-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------+-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | p | 1 | 1 | 0 | | 1/0 | 0.241 | | +| | +-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +ShortestPath | p = (andy)-[anon_0*]-(mattias) | 1 | 1 | 1 | 1424 | | | In Pipeline 1 | +| | +-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +MultiNodeIndexSeek | RANGE INDEX andy:Person(name) WHERE name = $autostring_0, | 1 | 1 | 4 | 120 | 1/1 | 0.308 | In Pipeline 0 | +| | RANGE INDEX mattias:Person(name) WHERE name = $autostring_1 | | | | | | | | ++---------------------+-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -Total database accesses: 7, total allocated memory: 3888 +Total database accesses: 5, total allocated memory: 1488 ---- ====== +[role=label--new-5.21] +[[query-plan-stateful-shortest-path-into]] +=== StatefulShortestPath(Into) +// StatefulShortestPath(Into) -[[query-plan-value-hash-join]] -== Value Hash Join -// ValueHashJoin - -The `ValueHashJoin` operator is a variation of the xref::planning-and-tuning/operators/operators-detail.adoc#execution-plans-operators-hash-join-general[hash join]. -This operator allows for arbitrary values to be used as the join key. -It is most frequently used to solve predicates of the form: `n.prop1 = m.prop2` (i.e. equality predicates between two property columns). +//// +[source, cypher, role=test-setup] +---- +DROP INDEX range_person_name IF EXISTS; +CREATE CONSTRAINT person_name_unique IF NOT EXISTS FOR (p:Person) REQUIRE (p.name) IS UNIQUE; +---- +//// +The `StatefulShortestPath(Into)` operator finds shortest paths between a start node and a single target node. +It uses a bidirectional breadth-first search (BFS) algorithm, which performs two BFS invocations at the same time, one from the left boundary node and one from the right boundary node. +Once a node is found by both BFS invocations, which indicates that it can be reached from both boundary nodes, the algorithm successfully terminates. +If one of the BFS invocations exhausts its search before intersecting, either because no further nodes can be reached or because the maximum number of hops has been reached, then there is no valid path between the boundary nodes and the algorithm terminates. -.ValueHashJoin +.StatefulShortestPath(Into) ====== .Query @@ -4418,10 +4281,8 @@ It is most frequently used to solve predicates of the form: `n.prop1 = m.prop2` ---- PROFILE MATCH - (p:Person), - (q:Person) -WHERE p.age = q.age -RETURN p, q + p = ALL SHORTEST (chris:Person {name: 'Chris'})(()-[]-()-[]-()){1,}(stefan:Person {name: 'Stefan'}) +RETURN p ---- .Query Plan @@ -4435,47 +4296,44 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p, q | 10 | 0 | 0 | | 0/0 | 0.000 | | -| | +---------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +ValueHashJoin | p.age = q.age| 10 | 0 | 0 | 344 | | | In Pipeline 2 | -| |\ +---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +NodeByLabelScan| q:Person | 15 | 0 | 0 | 120 | 0/0 | 0,000 | In Pipeline 1 | -| | +---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeByLabelScan | p:Person | 15 | 15 | 16 | 120 | 1/0 | 0,211 | In Pipeline 0 | -+-------------------+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | p | 2 | 2 | 0 | 0 | 0/0 | 0.039 | | +| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Projection | 1 | (chris) ((anon_12)-[anon_14]-(anon_13)-[anon_11]-())* (stefan) AS p | 2 | 2 | 0 | | 0/0 | 1.365 | | +| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +StatefulShortestPath(Into) | 2 | SHORTEST 1 GROUPS (chris) ((`anon_5`)-[`anon_6`]-(`anon_7`)-[`anon_8`]-(`anon_9`)){1, } (stefan) | 2 | 2 | 39 | 22237 | 1/0 | 37.376 | In Pipeline 1 | +| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +MultiNodeIndexSeek | 3 | UNIQUE chris:Person(name) WHERE name = $autostring_0, | 1 | 1 | 4 | 376 | 1/1 | 10.245 | In Pipeline 0 | +| | | UNIQUE stefan:Person(name) WHERE name = $autostring_1 | | | | | | | | ++-----------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ + +Total database accesses: 43, total allocated memory: 22557 -Total database accesses: 71, total allocated memory: 664 ---- ====== +[role=label--new-5.21] +[[query-plan-stateful-shortest-path-all]] +=== StatefulShortestPath(All) +// StatefulShortestPath(All) -[[query-plan-node-left-right-outer-hash-join]] -== Node Left/Right Outer Hash Join -// NodeLeftOuterHashJoin -// NodeRightOuterHashJoin - -The `NodeLeftOuterHashJoin` and `NodeRightOuterHashJoin` operators are variations of the xref::planning-and-tuning/operators/operators-detail.adoc#execution-plans-operators-hash-join-general[hash join]. -The query below can be planned with either a left or a right outer join. -The decision depends on the cardinalities of the left-hand and right-hand sides; i.e. how many rows would be returned, respectively, for `(a:Person)` and `(a)-->(b:Person)`. -If `(a:Person)` returns fewer results than `(a)-->(b:Person)`, a left outer join -- indicated by `NodeLeftOuterHashJoin` -- is planned. -On the other hand, if `(a:Person)` returns more results than `(a)-->(b:Person)`, a right outer join -- indicated by `NodeRightOuterHashJoin` -- is planned instead. +The `StatefulShortestPath(All)` operator finds shortest paths from a single node to multiple target nodes. +It uses a breadth-first search algorithm. -.NodeRightOuterHashJoin +.StatefulShortestPath(All) ====== .Query [source, cypher] ---- PROFILE -MATCH (a:Person) -OPTIONAL MATCH (a)-->(b:Person) -USING JOIN ON a -RETURN a.name, b.name +MATCH + p = ALL SHORTEST (chris:Person {name:'Chris'})(()-[]-()-[]-()){1,}(location:Location) +RETURN length(p) AS pathLength, location.name AS locationName ---- .Query Plan @@ -4489,33 +4347,37 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | `a.name`, `b.name` | 14 | 16 | 0 | | 0/0 | 0.102 | | -| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Projection | cache[a.name] AS `a.name`, cache[b.name] AS `b.name` | 14 | 16 | 8 | | 0/0 | 0.055 | | -| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +NodeRightOuterHashJoin | a | 14 | 16 | 0 | 4232 | | 0.269 | In Pipeline 2 | -| |\ +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +NodeByLabelScan | a:Person | 15 | 15 | 16 | 120 | 1/0 | 0,049 | In Pipeline 1 | -| | +------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +CacheProperties | cache[b.name], cache[a.name] | 13 | 13 | 39 | | | | | -| | +------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | (b)<-[anon_0]-(a) | 13 | 13 | 55 | | | | | -| | +------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | b:Person | 15 | 15 | 16 | 120 | 5/0 | 1,150 | Fused in Pipeline 0 | -+-------------------------+------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++----------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++----------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | 0 | pathLength, locationName | 14 | 20 | 0 | 0 | 0/0 | 0.074 | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +Projection | 1 | length((chris) ((anon_12)-[anon_14]-(anon_13)-[anon_11]-())* (location)) AS pathLength, | 14 | 20 | 40 | | 1/0 | 6.828 | | +| | | | location.name AS locationName | | | | | | | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | +| +StatefulShortestPath(All) | 2 | SHORTEST 1 GROUPS (chris) ((`anon_5`)-[`anon_6`]-(`anon_7`)-[`anon_8`]-(`anon_9`)){1, } (location) | 14 | 20 | 179 | 37663 | 1/0 | 52.849 | In Pipeline 1 | +| | | | expanding from: chris | | | | | | | | +| | | | inlined predicates: location:Location | | | | | | | | +| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +NodeUniqueIndexSeek | 3 | UNIQUE chris:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 376 | 0/1 | 9.078 | In Pipeline 0 | ++----------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -Total database accesses: 211, total allocated memory: 4312 +Total database accesses: 221, total allocated memory: 37983 + +---- + +//// +[source, cypher, role=test-setup] +---- +DROP CONSTRAINT person_name_unique IF EXISTS; +CREATE RANGE INDEX range_person_name FOR (p:Person) ON (p.name); ---- - +//// ====== [[query-plan-triadic-selection]] -== Triadic Selection -// TriadicSelection +=== Triadic Selection The `TriadicSelection` operator is used to solve triangular queries, such as the very common 'find my friends-of-friends that are not already my friend'. It does so by putting all the friends into a set, and uses the set to check if the friends-of-friends are already connected to me. @@ -4572,8 +4434,7 @@ Total database accesses: 246, total allocated memory: 64 [[query-plan-triadic-build]] -== Triadic Build -// TriadicBuild +=== Triadic Build The `TriadicBuild` operator is used in conjunction with xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-triadic-filter[`TriadicFilter`] to solve triangular queries, such as the very common 'find my friend-of-friends that are not already my friend'. These two operators are specific to Pipelined runtime and together perform the same logic as xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-triadic-selection[`TriadicSelection`] does for other runtimes. @@ -4637,8 +4498,7 @@ Total database accesses: 256, total allocated memory: 7376 [[query-plan-triadic-filter]] -== Triadic Filter -// TriadicFilter +=== Triadic Filter The `TriadicFilter` operator is used in conjunction with xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-triadic-build[`TriadicBuild`] to solve triangular queries, such as the very common 'find my friend-of-friends that are not already my friend'. These two operators are specific to Pipelined runtime and together perform the same logic as xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-triadic-selection[`TriadicSelection`] does for other runtimes. @@ -4701,286 +4561,76 @@ Total database accesses: 256, total allocated memory: 7376 ====== -[[query-plan-cartesian-product]] -== Cartesian Product -// CartesianProduct - -The `CartesianProduct` operator produces a cartesian product of the two inputs -- each row coming from the left child operator will be combined with all the rows from the right child operator. -`CartesianProduct` generally exhibits bad performance and ought to be avoided if possible. - - -.CartesianProduct -====== - -.Query -[source, cypher] ----- -PROFILE -MATCH - (p:Person), - (t:Team) -RETURN p, t ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+--------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p, t | 140 | 140 | 0 | | 2/0 | 1.917 | | -| | +----------+----------------+------+---------+----------------+------------------------+-----------+ | -| +CartesianProduct | | 140 | 140 | 0 | 1736 | | 1.209 | In Pipeline 2 | -| |\ +----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +NodeByLabelScan | t:Team | 10 | 10 | 11 | 136 | 1/0 | 1,145 | In Pipeline 1 | -| | +----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +NodeByLabelScan | p:Person | 15 | 15 | 16 | 120 | 1/0 | 0,409 | In Pipeline 0 | -+--------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 142, total allocated memory: 1816 ----- - -====== - - -[[query-plan-foreach]] -== Foreach -// Foreach - -The `Foreach` operator executes a nested loop between the left child operator and the right child operator. -In an analogous manner to the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator, it takes a row from the left-hand side and, using the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-argument[Argument] operator, provides it to the operator tree on the right-hand side. -`Foreach` will yield all the rows coming in from the left-hand side; all results from the right-hand side are pulled in and discarded. - - -.Foreach -====== - -.Query -[source, cypher] ----- -PROFILE -FOREACH (value IN [1,2,3] | CREATE (:Person {age: value})) ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime SLOTTED - -Runtime version {neo4j-version-minor} - -+-----------------+---------------------------------------------------------+----------------+------+---------+------------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | -+-----------------+---------------------------------------------------------+----------------+------+---------+------------------------+ -| +ProduceResults | | 1 | 0 | 0 | 0/0 | -| | +---------------------------------------------------------+----------------+------+---------+------------------------+ -| +EmptyResult | | 1 | 0 | 0 | 0/0 | -| | +---------------------------------------------------------+----------------+------+---------+------------------------+ -| +Foreach | value IN [1, 2, 3], CREATE (anon_0:Person {age: value}) | 1 | 1 | 9 | 0/0 | -+-----------------+---------------------------------------------------------+----------------+------+---------+------------------------+ - -Total database accesses: 9, total allocated memory: 64 ----- - -====== - -[[query-plan-transaction-foreach]] -== TransactionForeach -// TransactionForeach - -`TransactionForeach` works like the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-foreach[`Foreach`] operator but will commit the current transaction after a specified number of rows. - -.TransactionForeach -====== - -[NOTE] -The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. -If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. - -.Query -[source, cypher] ----- -PROFILE -LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line -CALL (line) { - CREATE (a: Artist {name: line[0]}) -} IN TRANSACTIONS OF 100 ROWS ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -++---------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | - +---------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - | +ProduceResults | 0 | | 10 | 0 | 0 | 0 | | | | - | | +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | - | +EmptyResult | 1 | | 10 | 0 | 0 | | 0/0 | 0.000 | Fused in Pipeline 3 | - | | +----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - | +TransactionForeach | 2 | IN TRANSACTIONS OF $autoint_1 ROWS ON ERROR FAIL | 10 | 4 | 0 | 4856 | | | | - | |\ +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | - | | +Create | 3 | (a:Artist {name: line[$autoint_0]}) | 10 | 4 | 12 | | | | | - | | | +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | - | | +Argument | 4 | line | 10 | 4 | 0 | 3472 | 0/0 | 0.712 | Fused in Pipeline 2 | - | | +----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - | +LoadCSV | 5 | line | 10 | 4 | 0 | 328 | | | In Pipeline 1 | - +---------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - - Total database accesses: 12, total allocated memory: 5704 ----- - -====== - -[[query-plan-subquery-foreach]] -== SubqueryForeach -// SubqueryForeach - -`SubqueryForeach` works like the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-foreach[`Foreach`]operator but it is only used for executing subqueries. - -.SubqueryForeach -====== - -[NOTE] -The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. -If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. - -.Query -[source, cypher] ----- -PROFILE -LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line -CALL (line) { - CREATE (a: Artist {name: line[0]}) -} ----- - -.Query Plan -[role="queryplan", subs="attributes+"] ----- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | | 10 | 0 | 0 | 0 | | | | -| | +----+-------------------------------------+----------------+------+---------+----------------+ | | | -| +EmptyResult | 1 | | 10 | 0 | 0 | | 0/0 | 0.000 | Fused in Pipeline 3 | -| | +----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +SubqueryForeach | 2 | | 10 | 4 | 0 | 4080 | | | | -| |\ +----+-------------------------------------+----------------+------+---------+----------------+ | | | -| | +Create | 3 | (a:Artist {name: line[$autoint_0]}) | 10 | 4 | 12 | | | | | -| | | +----+-------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | 4 | line | 10 | 4 | 0 | 3472 | 0/0 | 0.852 | Fused in Pipeline 2 | -| | +----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +LoadCSV | 5 | line | 10 | 4 | 0 | 328 | | | In Pipeline 1 | -+------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 12, total allocated memory: 4928 ----- - -====== - +[[union-operators]] +== Union operators -[[query-plan-eager]] -== Eager -// Eager +Union operators in Cypher combine the results from multiple query parts by merging their rows. +For more information, see the page about the xref:clauses/union.adoc[`UNION`] clause. -The `Eager` operator causes all preceding operators to execute fully, for the whole dataset, before continuing execution. -This is done to ensure isolation between parts of the query plan that might otherwise affect each other. -Values from the graph are fetched in a lazy manner; i.e. a pattern matching might not be fully exhausted before updates are applied. -To maintain correct semantics, the query planner will insert `Eager` operators into the query plan to prevent updates from influencing pattern matching, or other read operations. -This scenario is exemplified by the query below, where the `DELETE` clause would otherwise influence both the `MATCH` clause and the `MERGE` clause. -For more information on how the `Eager` operator can ensure correct semantics, see the section on xref::clauses/clause-composition.adoc[Clause composition]. +[[query-plan-union]] +=== Union -The `Eager` operator can cause high memory usage when importing data or migrating graph structures. -In such cases, the operations should be split into simpler steps; e.g. importing nodes and relationships separately. -Alternatively, the records to be updated can be returned, followed by an update statement. +The `Union` operator concatenates the results from the right child operator with the results from the left child operator. -.Eager +.Union ====== - .Query [source, cypher] ---- PROFILE -MATCH (a:Person {name: 'me'}), (b:Person {name: 'Bob'}) -DETACH DELETE a, b -MERGE (:Person {name: 'me'}) +MATCH (p:Location) + RETURN p.name +UNION ALL +MATCH (p:Country) + RETURN p.name ---- .Query Plan [role="queryplan", subs="attributes+"] ---- -Planner COST - -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+---------------------+----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------+----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | | 0 | 0 | 0 | 0 | | | | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +EmptyResult | 1 | | 0 | 0 | 0 | | | | | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Apply | 2 | | 0 | 1 | 0 | | | | | -| |\ +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +Merge | 3 | CREATE (anon_0:Person {name: $autostring_2}) | 0 | 1 | 3 | | | | | -| | | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| | +NodeIndexSeek | 4 | RANGE INDEX anon_0:Person(name) WHERE name = $autostring_2 | 0 | 0 | 1 | 3304 | 1/0 | 0.663 | Fused in Pipeline 3 | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Eager | 5 | read/delete conflict for variable: anon_0 (Operator: 6 vs 4, and 1 more conflicting operators) | 0 | 1 | 0 | 360 | 0/0 | 0.008 | In Pipeline 2 | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +DetachDelete | 6 | b | 0 | 1 | 4 | | | | | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +DetachDelete | 7 | a | 0 | 1 | 5 | | | | | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Eager | 8 | read/delete conflict for variable: b (Operator: 6 vs 10, and 1 more conflicting operators), | 0 | 1 | 0 | 360 | 1/0 | 0.226 | Fused in Pipeline 1 | -| | | | read/set conflict for label: Person (Operator: 3 vs 10), | | | | | | | | -| | | | read/set conflict for property: name (Operator: 3 vs 10) | | | | | | | | -| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +MultiNodeIndexSeek | 9 | RANGE INDEX a:Person(name) WHERE name = $autostring_0, | 0 | 1 | 4 | 376 | 2/0 | 0.218 | In Pipeline 0 | -| | | RANGE INDEX b:Person(name) WHERE name = $autostring_1 | | | | | | | | -+---------------------+----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +Planner COST -Total database accesses: 17, total allocated memory: 4184 +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++--------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++--------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | `p.name` | 20 | 0 | 0 | | | | | +| | +----+--------------------+----------------+------+---------+----------------+ | | | +| +Union | 1 | | 20 | 0 | 0 | 0 | 0/0 | 0.000 | Fused in Pipeline 2 | +| |\ +----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| | +Projection | 2 | `p.name` | 10 | 0 | 0 | | | | | +| | | +----+--------------------+----------------+------+---------+----------------+ | | | +| | +Projection | 3 | p.name AS `p.name` | 10 | 0 | 0 | | | | | +| | | +----+--------------------+----------------+------+---------+----------------+ | | | +| | +NodeByLabelScan | 4 | p:Country | 10 | 0 | 0 | 120 | 0/0 | 0.049 | Fused in Pipeline 1 | +| | +----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Projection | 5 | `p.name` | 10 | 0 | 0 | | | | | +| | +----+--------------------+----------------+------+---------+----------------+ | | | +| +Projection | 6 | p.name AS `p.name` | 10 | 0 | 0 | | | | | +| | +----+--------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 7 | p:Location | 10 | 0 | 0 | 120 | 0/0 | 0.077 | Fused in Pipeline 0 | ++--------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 0, total allocated memory: 320 ---- ====== +[[aggregation-operators]] +== Aggregation operators + +Aggregation operators are used to compute summary statistics over groups of graph data, enabling operations such as counting nodes, relationships or properties. + [[query-plan-eager-aggregation]] -== Eager Aggregation -// EagerAggregation +=== Eager Aggregation The `EagerAggregation` operator evaluates a grouping expression and uses the result to group rows into different groupings. For each of these groupings, `EagerAggregation` will then evaluate all aggregation functions and return the result. @@ -5033,8 +4683,7 @@ Total database accesses: 117, total allocated memory: 2664 [[query-plan-ordered-aggregation]] -== Ordered Aggregation -// OrderedAggregation +=== Ordered Aggregation The `OrderedAggregation` operator is an optimization of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-eager-aggregation[`EagerAggregation`] operator that takes advantage of the ordering of the incoming rows. This operator uses lazy evaluation and has a lower memory pressure in the system than the `EagerAggregation` operator. @@ -5080,8 +4729,7 @@ Total database accesses: 3, total allocated memory: 352 [[query-plan-node-count-from-count-store]] -== Node Count From Count Store -// NodeCountFromCountStore +=== Node Count From Count Store The `NodeCountFromCountStore` operator uses the count store to answer questions about node counts. This is much faster than the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-eager-aggregation[`EagerAggregation`] operator which achieves the same result by actually counting. @@ -5126,8 +4774,7 @@ Total database accesses: 1, total allocated memory: 184 [[query-plan-relationship-count-from-count-store]] -== Relationship Count From Count Store -// RelationshipCountFromCountStore +=== Relationship Count From Count Store The `RelationshipCountFromCountStore` operator uses the count store to answer questions about relationship counts. This is much faster than the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-eager-aggregation[`EagerAggregation`] operator which achieves the same result by actually counting. @@ -5170,24 +4817,25 @@ Total database accesses: 1, total allocated memory: 184 ====== +[[filter-order-projection-operators]] +== Filter, order, and projection operators -[[query-plan-distinct]] -== Distinct -// Distinct +The operators in this group handle how rows of data are transformed, filtered, and finalized for query results. +They are responsible for crafting the structure of the returned data, -The `Distinct` operator removes duplicate rows from the incoming stream of rows. -To ensure only distinct elements are returned, `Distinct` will pull in data lazily from its source and build up state. -This may lead to increased memory pressure in the system. +[[query-plan-empty-result]] +=== Empty Result +The `EmptyResult` operator eagerly loads all incoming data and discards it. -.Distinct +.EmptyResult ====== + .Query [source, cypher] ---- PROFILE -MATCH (l:Location)<-[:WORKS_IN]-(p:Person) -RETURN DISTINCT p +CREATE (:Person) ---- .Query Plan @@ -5201,43 +4849,37 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+----+-----------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----+-----------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | p | 14 | 14 | 28 | | | | | -| | +----+-----------------------+----------------+------+---------+----------------+ | | | -| +Distinct | 1 | p | 14 | 14 | 0 | 352 | | | | -| | +----+-----------------------+----------------+------+---------+----------------+ | | | -| +Filter | 2 | p:Person | 15 | 15 | 30 | | | | | -| | +----+-----------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | 3 | (l)<-[r:WORKS_IN]-(p) | 15 | 15 | 26 | | | | | -| | +----+-----------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | 4 | l:Location | 10 | 10 | 11 | 120 | 4/0 | 0.287 | Fused in Pipeline 0 | -+------------------+----+-----------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+-----------------+----------------+------+---------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+-----------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +ProduceResults | | 1 | 0 | 0 | | | | +| | +-----------------+----------------+------+---------+ | | | +| +EmptyResult | | 1 | 0 | 0 | | | | +| | +-----------------+----------------+------+---------+ | | | +| +Create | (anon_0:Person) | 1 | 1 | 1 | 0/0 | 0.000 | Fused in Pipeline 0 | ++-----------------+-----------------+----------------+------+---------+------------------------+-----------+---------------------+ -Total database accesses: 95, total allocated memory: 432 +Total database accesses: 1, total allocated memory: 184 ---- ====== -[[query-plan-ordered-distinct]] -== Ordered Distinct -// OrderedDistinct -The `OrderedDistinct` operator is an optimization of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-distinct[`Distinct`] operator that takes advantage of the ordering of the incoming rows. -This operator has a lower memory pressure in the system than the `Distinct` operator. +[[query-plan-produce-results]] +=== Produce Results +The `ProduceResults` operator prepares the result so that it is consumable by the user, such as transforming internal values to user values. +It is present in every single query that returns data to the user, and has little bearing on performance optimisation. -.OrderedDistinct +.ProduceResults ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -WHERE p.name STARTS WITH 'P' -RETURN DISTINCT p.name +MATCH (n) +RETURN n ---- .Query Plan @@ -5251,29 +4893,25 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+---------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+---------------+ -| +ProduceResults | `p.name` | 0 | 2 | 0 | | 0/0 | 0.046 | | | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +OrderedDistinct | cache[p.name] AS `p.name` | 0 | 2 | 0 | 32 | 0/0 | 0.090 | `p.name` ASC | | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+ | -| +NodeIndexSeekByRange | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 2 | 3 | 120 | 0/1 | 0.493 | p.name ASC | In Pipeline 0 | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+---------------+ ++-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | n | 35 | 35 | 0 | | | | | +| | +---------+----------------+------+---------+----------------+ | | | +| +AllNodesScan | n | 35 | 35 | 36 | 120 | 3/0 | 0.508 | Fused in Pipeline 0 | ++-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 3, total allocated memory: 184 +Total database accesses: 36, total allocated memory: 184 ---- ====== [[query-plan-filter]] -== Filter -// Filter +=== Filter The `Filter` operator filters each row coming from the child operator, only passing through rows that evaluate the predicates to `true`. - .Filter ====== @@ -5312,24 +4950,20 @@ Total database accesses: 15, total allocated memory: 184 ====== +[[query-plan-empty-row]] +=== Empty Row -[[query-plan-limit]] -== Limit -// Limit - -The `Limit` operator returns the first `+n+` rows from the incoming input. - +The `EmptyRow` operator returns a single row with no columns. -.Limit +.EmptyRow ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -RETURN p -LIMIT 3 +CYPHER runtime=slotted +FOREACH (value IN [1,2,3] | MERGE (:Person {age: value})) ---- .Query Plan @@ -5337,46 +4971,52 @@ LIMIT 3 ---- Planner COST -Runtime PIPELINED +Runtime SLOTTED Runtime version {neo4j-version-minor} -Batch size 128 - -+-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | p | 3 | 3 | 0 | | | | | -| | +----------+----------------+------+---------+----------------+ | | | -| +Limit | 3 | 3 | 3 | 0 | 32 | | | | -| | +----------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan| p:Person | 3 | 4 | 5 | 120 | 3/0 | 0,540 | Fused in Pipeline 0 | -+-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+--------------------------------------+----------------+------+---------+------------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | ++--------------------+--------------------------------------+----------------+------+---------+------------------------+ +| +ProduceResults | | 1 | 0 | 0 | 0/0 | +| | +--------------------------------------+----------------+------+---------+------------------------+ +| +EmptyResult | | 1 | 0 | 0 | 0/0 | +| | +--------------------------------------+----------------+------+---------+------------------------+ +| +Foreach | value IN [1, 2, 3] | 1 | 1 | 0 | 0/0 | +| |\ +--------------------------------------+----------------+------+---------+------------------------+ +| | +Merge | CREATE (anon_0:Person {age: value}) | 1 | 3 | 9 | 0/0 | +| | | +--------------------------------------+----------------+------+---------+------------------------+ +| | +Filter | anon_0.age = value | 1 | 0 | 184 | 2/0 | +| | | +--------------------------------------+----------------+------+---------+------------------------+ +| | +NodeByLabelScan | anon_0:Person | 35 | 108 | 111 | 3/0 | +| | +--------------------------------------+----------------+------+---------+------------------------+ +| +EmptyRow | | 1 | 1 | 0 | 0/0 | ++--------------------+--------------------------------------+----------------+------+---------+------------------------+ -Total database accesses: 8, total allocated memory: 184 +Total database accesses: 304, total allocated memory: 64 ---- ====== -[[query-plan-skip]] -== Skip -// Skip +[[query-plan-cache-properties]] +=== Cache Properties -The `Skip` operator skips `+n+` rows from the incoming rows. +The `CacheProperties` operator reads nodes and relationship properties and caches them in the current row. +Future accesses to these properties can avoid reading from the store which will speed up the query. +In the plan below we will cache `l.name` before `Expand(All)` where there are fewer rows. -.Skip +.CacheProperties ====== - .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -RETURN p -ORDER BY p.id -SKIP 1 +MATCH (l:Location)<-[:WORKS_IN]-(p:Person) +RETURN + l.name AS location, + p.name AS name ---- .Query Plan @@ -5390,44 +5030,42 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+------------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProduceResults | p | 13 | 13 | 0 | | 2/0 | 0.165 | | | -| | +----------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +Skip | $autoint_0 | 13 | 13 | 0 | 32 | 0/0 | 0.043 | | | -| | +----------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +Sort | `p.id` ASC | 14 | 14 | 0 | 400 | 0/0 | 0.155 | p.id ASC | In Pipeline 1 | -| | +----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +Projection | p.id AS `p.id` | 14 | 14 | 0 | | | | | | -| | +----------------+----------------+------+---------+----------------+ | +------------+ | -| +NodeByLabelScan | p:Person | 18 | 18 | 19 | 120 | 3/0 | 0,157 | | Fused in Pipeline 0 | -+------------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ ++------------------+----+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | location, name | 13 | 13 | 0 | | | | | +| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | +| +Projection | 1 | cache[l.name] AS location, p.name AS name | 13 | 13 | 26 | | | | | +| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | p:Person | 13 | 13 | 26 | | | | | +| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | 3 | (l)<-[anon_0:WORKS_IN]-(p) | 13 | 13 | 24 | | | | | +| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | +| +CacheProperties | 4 | cache[l.name] | 10 | 10 | 20 | | | | | +| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 5 | l:Location | 10 | 10 | 11 | 120 | 4/0 | 0.344 | Fused in Pipeline 0 | ++------------------+----+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 71, total allocated memory: 512 +Total database accesses: 107, total allocated memory: 200 ---- ====== -[[query-plan-sort]] -== Sort -// Sort +[[query-plan-projection]] +=== Projection -The `Sort` operator sorts rows by a provided key. -In order to sort the data, all data from the source operator needs to be pulled in eagerly and kept in the query state, which will lead to increased memory pressure in the system. +For each incoming row, the `Projection` operator evaluates a set of expressions and produces a row with the results of the expressions. -.Sort +.Projection ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -RETURN p -ORDER BY p.name +RETURN 'hello' AS greeting ---- .Query Plan @@ -5441,44 +5079,36 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProduceResults | p | 14 | 14 | 0 | | 2/0 | 0.178 | | | -| | +--------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +Sort | `p.name` ASC | 14 | 14 | 0 | 1192 | 0/0 | 0.107 | p.name ASC | In Pipeline 1 | -| | +--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +Projection | p.name AS `p.name` | 14 | 14 | 14 | | | | | | -| | +--------------------+----------------+------+---------+----------------+ | +------------+ | -| +NodeByLabelScan |p:Person | 14 | 14 | 35 | 120 | 3/0 | 0,221 | | Fused in Pipeline 0 | -+------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ ++-----------------+---------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+---------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +ProduceResults | greeting | 1 | 1 | 0 | | | | +| | +---------------------------+----------------+------+---------+ | | | +| +Projection | $autostring_0 AS greeting | 1 | 1 | 0 | 0/0 | 0.000 | Fused in Pipeline 0 | ++-----------------+---------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -Total database accesses: 85, total allocated memory: 1272 +Total database accesses: 0, total allocated memory: 184 ---- ====== -[[query-plan-partial-sort]] -== Partial Sort -// PartialSort - -The `PartialSort` operator is an optimization of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-sort[`Sort`] operator that takes advantage of the ordering of the incoming rows. -This operator uses lazy evaluation and has a lower memory pressure in the system than the `Sort` operator. -Partial sort is only applicable when sorting on multiple columns. +[[query-plan-project-endpoints]] +=== Project Endpoints +The `ProjectEndpoints` operator projects the start and end node of a relationship. -.PartialSort +.ProjectEndpoints ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -WHERE p.name STARTS WITH 'P' -RETURN p -ORDER BY p.name, p.age +CREATE (n)-[p:KNOWS]->(m) +WITH p AS r +MATCH (u)-[r]->(v) +RETURN u, v ---- .Query Plan @@ -5492,43 +5122,46 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| +ProduceResults | p | 0 | 2 | 0 | | 2/0 | 0.087 | | | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +PartialSort | `p.name` ASC, `p.age` ASC | 0 | 2 | 0 | 544 | 0/0 | 0.184 | p.name ASC, p.age ASC | In Pipeline 1 | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| +Projection | cache[p.name] AS `p.name`, p.age AS `p.age` | 0 | 2 | 0 | | | | `p.name` ASC | | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+ | +-----------------------+ | -| +NodeIndexSeekByRange | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 2 | 3 | 120 | 0/1 | 0.362 | p.name ASC | Fused in Pipeline 0 | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ ++---------------------+----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------+----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | u, v | 1 | 1 | 2 | 0 | | | | +| | +----+-----------------------------------------+----------------+------+---------+----------------+ | | | +| +Apply | 1 | | 1 | 1 | 0 | | | | | +| |\ +----+-----------------------------------------+----------------+------+---------+----------------+ | | | +| | +ProjectEndpoints | 2 | (u)-[r]->(v) | 1 | 1 | 0 | | | | | +| | | +----+-----------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 3 | r | 1 | 1 | 0 | 4328 | 0/0 | 0.194 | Fused in Pipeline 2 | +| | +----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Eager | 4 | read/create conflict (Operator: 6 vs 2) | 1 | 1 | 0 | 368 | 0/0 | 0.025 | In Pipeline 1 | +| | +----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Projection | 5 | p AS r | 1 | 1 | 0 | | | | | +| | +----+-----------------------------------------+----------------+------+---------+----------------+ | | | +| +Create | 6 | (n), (m), (n)-[p:KNOWS]->(m) | 1 | 1 | 3 | | 0/0 | 0.000 | Fused in Pipeline 0 | ++---------------------+----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 3, total allocated memory: 608 +Total database accesses: 5, total allocated memory: 4920 ---- ====== -[[query-plan-top]] -== Top -// Top +[[query-plan-distinct]] +=== Distinct -The `Top` operator returns the first `+n+` rows sorted by a provided key. -Instead of sorting the entire input, only the top `+n+` rows are retained. +The `Distinct` operator removes duplicate rows from the incoming stream of rows. +To ensure only distinct elements are returned, `Distinct` will pull in data lazily from its source and build up state. +This may lead to increased memory pressure in the system. -.Top +.Distinct ====== - .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -RETURN p -ORDER BY p.name -LIMIT 2 +MATCH (l:Location)<-[:WORKS_IN]-(p:Person) +RETURN DISTINCT p ---- .Query Plan @@ -5542,34 +5175,34 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+------------------+----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProduceResults | p | 2 | 2 | 0 | | 2/0 | 0.093 | | | -| | +----------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +Top | `p.name` ASC LIMIT 2 | 2 | 2 | 0 | 1184 | 0/0 | 0.295 | p.name ASC | In Pipeline 1 | -| | +----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +Projection | p.name AS `p.name` | 14 | 14 | 14 | | | | | | -| | +----------------------+----------------+------+---------+----------------+ | +------------+ | -| +NodeByLabelScan | p:Person | 14 | 14 | 35 | 120 | 3/0 | 0,166 | | Fused in Pipeline 0 | -+------------------+----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ ++------------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | p | 14 | 14 | 29 | 0 | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +Distinct | 1 | p | 14 | 14 | 0 | 352 | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +Filter | 2 | p:Person | 15 | 15 | 30 | | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +Expand(All) | 3 | (l)<-[anon_0:WORKS_IN]-(p) | 15 | 15 | 25 | | | | | +| | +----+----------------------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 4 | l:Location | 10 | 10 | 11 | 248 | 8/0 | 0.758 | Fused in Pipeline 0 | ++------------------+----+----------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 85, total allocated memory: 1264 +Total database accesses: 95, total allocated memory: 432 ---- ====== -[[query-plan-partial-top]] -== Partial Top -// PartialTop +[[query-plan-ordered-distinct]] +=== Ordered Distinct -The `PartialTop` operator is an optimization of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-top[`Top`] operator that takes advantage of the ordering of the incoming rows. -This operator uses lazy evaluation and has a lower memory pressure in the system than the `Top` operator. -Partial top is only applicable when sorting on multiple columns. +The `OrderedDistinct` operator is an optimization of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-distinct[`Distinct`] operator that takes advantage of the ordering of the incoming rows. +This operator has a lower memory pressure in the system than the `Distinct` operator. -.PartialTop +.OrderedDistinct ====== .Query @@ -5578,9 +5211,7 @@ Partial top is only applicable when sorting on multiple columns. PROFILE MATCH (p:Person) WHERE p.name STARTS WITH 'P' -RETURN p -ORDER BY p.name, p.age -LIMIT 2 +RETURN DISTINCT p.name ---- .Query Plan @@ -5594,42 +5225,36 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| +ProduceResults | p | 0 | 2 | 0 | | 2/0 | 0.093 | | | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +PartialTop | `p.name` ASC, `p.age` ASC LIMIT 2 | 0 | 2 | 0 | 640 | 0/0 | 0.870 | p.name ASC, p.age ASC | In Pipeline 1 | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -| +Projection | cache[p.name] AS `p.name`, p.age AS `p.age` | 0 | 2 | 0 | | | | `p.name` ASC | | -| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+ | +-----------------------+ | -| +NodeIndexSeekByRange | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 2 | 3 | 120 | 0/1 | 0.556 | p.name ASC | Fused in Pipeline 0 | -+-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+---------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+---------------+ +| +ProduceResults | `p.name` | 0 | 2 | 0 | | 0/0 | 0.046 | | | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +OrderedDistinct | cache[p.name] AS `p.name` | 0 | 2 | 0 | 32 | 0/0 | 0.090 | `p.name` ASC | | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+ | +| +NodeIndexSeekByRange | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 2 | 3 | 120 | 0/1 | 0.493 | p.name ASC | In Pipeline 0 | ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+--------------+---------------+ -Total database accesses: 3, total allocated memory: 704 +Total database accesses: 3, total allocated memory: 184 ---- ====== +[[query-plan-procedure-call]] +=== Procedure Call -[[query-plan-union]] -== Union -// Union - -The `Union` operator concatenates the results from the right child operator with the results from the left child operator. - +The `ProcedureCall` operator indicates an invocation to a procedure. -.Union +.ProcedureCall ====== + .Query [source, cypher] ---- PROFILE -MATCH (p:Location) - RETURN p.name -UNION ALL -MATCH (p:Country) - RETURN p.name +CALL db.labels() YIELD label +RETURN * +ORDER BY label ---- .Query Plan @@ -5643,39 +5268,26 @@ Runtime version {neo4j-version-minor} Batch size 128 -+--------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+--------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | `p.name` | 20 | 0 | 0 | | | | | -| | +----+--------------------+----------------+------+---------+----------------+ | | | -| +Union | 1 | | 20 | 0 | 0 | 0 | 0/0 | 0.000 | Fused in Pipeline 2 | -| |\ +----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| | +Projection | 2 | `p.name` | 10 | 0 | 0 | | | | | -| | | +----+--------------------+----------------+------+---------+----------------+ | | | -| | +Projection | 3 | p.name AS `p.name` | 10 | 0 | 0 | | | | | -| | | +----+--------------------+----------------+------+---------+----------------+ | | | -| | +NodeByLabelScan | 4 | p:Country | 10 | 0 | 0 | 120 | 0/0 | 0.049 | Fused in Pipeline 1 | -| | +----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Projection | 5 | `p.name` | 10 | 0 | 0 | | | | | -| | +----+--------------------+----------------+------+---------+----------------+ | | | -| +Projection | 6 | p.name AS `p.name` | 10 | 0 | 0 | | | | | -| | +----+--------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | 7 | p:Location | 10 | 0 | 0 | 120 | 0/0 | 0.077 | Fused in Pipeline 0 | -+--------------------+----+--------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-----------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProduceResults | label | 10 | 4 | 0 | | 0/0 | 0.091 | | | +| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +Sort | label ASC | 10 | 4 | 0 | 536 | 0/0 | 0.178 | label ASC | In Pipeline 1 | +| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProcedureCall | db.labels() :: (label :: STRING) | 10 | 4 | | | | | | Fused in Pipeline 0 | ++-----------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -Total database accesses: 0, total allocated memory: 320 +Total database accesses: ?, total allocated memory: 600 ---- ====== - [[query-plan-unwind]] -== Unwind -// Unwind +=== Unwind The `Unwind` operator returns one row per item in a list. - .Unwind ====== @@ -5713,14 +5325,11 @@ Total database accesses: 0, total allocated memory: 184 [role=label--new-5.17] [[query-plan-partitioned-unwind]] -== Partitioned Unwind -// PartitionedUnwind -// New in 5.17 +=== Partitioned Unwind The `PartitionedUnwind` is a variant of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-unwind[`Unwind`] operator used by the xref:planning-and-tuning/runtimes/concepts.adoc#runtimes-parallel-runtime[parallel runtime]. It allows the index to be partitioned into different segments where each segment can be scanned independently in parallel. - .PartitionedUnwind ====== @@ -5757,16 +5366,20 @@ Total database accesses: 0 ====== -[[query-plan-exhaustive-limit]] -== Exhaustive Limit -// LockNodes - changed in 4.3 -// ExhaustiveLimit -The `ExhaustiveLimit` operator is similar to the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-limit[`Limit`] operator but will always exhaust the input. -Used when combining `LIMIT` and updates +[[sort-limit-operators]] +== Sort and limit operators +The operators in this group manage result set ordering and control the flow of data by limiting or skipping rows. +They optimize query performance and ensure users receive results in the desired order and quantity. -.ExhaustiveLimit +[[query-plan-sort]] +=== Sort + +The `Sort` operator sorts rows by a provided key. +In order to sort the data, all data from the source operator needs to be pulled in eagerly and kept in the query state, which will lead to increased memory pressure in the system. + +.Sort ====== .Query @@ -5774,9 +5387,8 @@ Used when combining `LIMIT` and updates ---- PROFILE MATCH (p:Person) -SET p.seen = true RETURN p -LIMIT 3 +ORDER BY p.name ---- .Query Plan @@ -5790,43 +5402,141 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | p | 3 | 3 | 10 | 0 | | | | -| | +----+---------------+----------------+------+---------+----------------+ | | | -| +ExhaustiveLimit | 1 | 3 | 3 | 3 | 0 | 32 | | | | -| | +----+---------------+----------------+------+---------+----------------+ | | | -| +SetProperty | 2 | p.seen = true | 17 | 17 | 34 | | | | | -| | +----+---------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | 3 | p:Person | 17 | 17 | 18 | 240 | 3/0 | 1.966 | Fused in Pipeline 0 | -+------------------+----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProduceResults | p | 14 | 14 | 0 | | 2/0 | 0.178 | | | +| | +--------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +Sort | `p.name` ASC | 14 | 14 | 0 | 1192 | 0/0 | 0.107 | p.name ASC | In Pipeline 1 | +| | +--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +Projection | p.name AS `p.name` | 14 | 14 | 14 | | | | | | +| | +--------------------+----------------+------+---------+----------------+ | +------------+ | +| +NodeByLabelScan |p:Person | 14 | 14 | 35 | 120 | 3/0 | 0,221 | | Fused in Pipeline 0 | ++------------------+--------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -Total database accesses: 62, total allocated memory: 304 +Total database accesses: 85, total allocated memory: 1272 ---- ====== -[[query-plan-optional]] -== Optional -// Optional +[[query-plan-partial-sort]] +=== Partial Sort -The `Optional` operator is used to solve some xref::clauses/optional-match.adoc[OPTIONAL MATCH] queries. -It will pull data from its source, simply passing it through if any data exists. -However, if no data is returned by its source, `Optional` will yield a single row with all columns set to `null`. +The `PartialSort` operator is an optimization of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-sort[`Sort`] operator that takes advantage of the ordering of the incoming rows. +This operator uses lazy evaluation and has a lower memory pressure in the system than the `Sort` operator. +Partial sort is only applicable when sorting on multiple columns. + +.PartialSort +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (p:Person) +WHERE p.name STARTS WITH 'P' +RETURN p +ORDER BY p.name, p.age +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| +ProduceResults | p | 0 | 2 | 0 | | 2/0 | 0.087 | | | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +PartialSort | `p.name` ASC, `p.age` ASC | 0 | 2 | 0 | 544 | 0/0 | 0.184 | p.name ASC, p.age ASC | In Pipeline 1 | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| +Projection | cache[p.name] AS `p.name`, p.age AS `p.age` | 0 | 2 | 0 | | | | `p.name` ASC | | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+ | +-----------------------+ | +| +NodeIndexSeekByRange | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 2 | 3 | 120 | 0/1 | 0.362 | p.name ASC | Fused in Pipeline 0 | ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ + +Total database accesses: 3, total allocated memory: 608 +---- + +====== + + +[[query-plan-top]] +=== Top + +The `Top` operator returns the first `+n+` rows sorted by a provided key. +Instead of sorting the entire input, only the top `+n+` rows are retained. + +.Top +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (p:Person) +RETURN p +ORDER BY p.name +LIMIT 2 +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++------------------+----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++------------------+----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProduceResults | p | 2 | 2 | 0 | | 2/0 | 0.093 | | | +| | +----------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +Top | `p.name` ASC LIMIT 2 | 2 | 2 | 0 | 1184 | 0/0 | 0.295 | p.name ASC | In Pipeline 1 | +| | +----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +Projection | p.name AS `p.name` | 14 | 14 | 14 | | | | | | +| | +----------------------+----------------+------+---------+----------------+ | +------------+ | +| +NodeByLabelScan | p:Person | 14 | 14 | 35 | 120 | 3/0 | 0,166 | | Fused in Pipeline 0 | ++------------------+----------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ + +Total database accesses: 85, total allocated memory: 1264 +---- + +====== + + +[[query-plan-partial-top]] +=== Partial Top + +The `PartialTop` operator is an optimization of the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-top[`Top`] operator that takes advantage of the ordering of the incoming rows. +This operator uses lazy evaluation and has a lower memory pressure in the system than the `Top` operator. +Partial top is only applicable when sorting on multiple columns. -.Optional +.PartialTop ====== .Query [source, cypher] ---- PROFILE -MATCH (p:Person {name: 'me'}) -OPTIONAL MATCH (q:Person {name: 'Lulu'}) -RETURN p, q +MATCH (p:Person) +WHERE p.name STARTS WITH 'P' +RETURN p +ORDER BY p.name, p.age +LIMIT 2 ---- .Query Plan @@ -5840,44 +5550,38 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | p, q | 1 | 1 | 0 | | 2/0 | 0.079 | In Pipeline 2 | -| | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +Apply | | 1 | 1 | 0 | | 0/0 | 0.096 | | -| |\ +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| | +Optional | p | 1 | 1 | 0 | 768 | 0/0 | 0.043 | In Pipeline 2 | -| | | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| | +NodeIndexSeek | RANGE INDEX q:Person(name) WHERE name = $autostring_1 | 1 | 0 | 1 | 2152 | 1/0 | 0.098 | In Pipeline 1 | -| | +-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 0/1 | 0.364 | In Pipeline 0 | -+------------------+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| +ProduceResults | p | 0 | 2 | 0 | | 2/0 | 0.093 | | | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +PartialTop | `p.name` ASC, `p.age` ASC LIMIT 2 | 0 | 2 | 0 | 640 | 0/0 | 0.870 | p.name ASC, p.age ASC | In Pipeline 1 | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ +| +Projection | cache[p.name] AS `p.name`, p.age AS `p.age` | 0 | 2 | 0 | | | | `p.name` ASC | | +| | +--------------------------------------------------------------------------------+----------------+------+---------+----------------+ | +-----------------------+ | +| +NodeIndexSeekByRange | RANGE INDEX p:Person(name) WHERE name STARTS WITH $autostring_0, cache[p.name] | 0 | 2 | 3 | 120 | 0/1 | 0.556 | p.name ASC | Fused in Pipeline 0 | ++-----------------------+--------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+-----------------------+---------------------+ -Total database accesses: 3, total allocated memory: 3000 +Total database accesses: 3, total allocated memory: 704 ---- ====== +[[query-plan-limit]] +=== Limit -[[query-plan-project-endpoints]] -== Project Endpoints -// ProjectEndpoints - -The `ProjectEndpoints` operator projects the start and end node of a relationship. - +The `Limit` operator returns the first `+n+` rows from the incoming input. -.ProjectEndpoints +.Limit ====== .Query [source, cypher] ---- PROFILE -CREATE (n)-[p:KNOWS]->(m) -WITH p AS r -MATCH (u)-[r]->(v) -RETURN u, v +MATCH (p:Person) +RETURN p +LIMIT 3 ---- .Query Plan @@ -5891,45 +5595,40 @@ Runtime version {neo4j-version-minor} Batch size 128 -+---------------------+----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------+----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | u, v | 1 | 1 | 2 | 0 | | | | -| | +----+-----------------------------------------+----------------+------+---------+----------------+ | | | -| +Apply | 1 | | 1 | 1 | 0 | | | | | -| |\ +----+-----------------------------------------+----------------+------+---------+----------------+ | | | -| | +ProjectEndpoints | 2 | (u)-[r]->(v) | 1 | 1 | 0 | | | | | -| | | +----+-----------------------------------------+----------------+------+---------+----------------+ | | | -| | +Argument | 3 | r | 1 | 1 | 0 | 4328 | 0/0 | 0.194 | Fused in Pipeline 2 | -| | +----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Eager | 4 | read/create conflict (Operator: 6 vs 2) | 1 | 1 | 0 | 368 | 0/0 | 0.025 | In Pipeline 1 | -| | +----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +Projection | 5 | p AS r | 1 | 1 | 0 | | | | | -| | +----+-----------------------------------------+----------------+------+---------+----------------+ | | | -| +Create | 6 | (n), (m), (n)-[p:KNOWS]->(m) | 1 | 1 | 3 | | 0/0 | 0.000 | Fused in Pipeline 0 | -+---------------------+----+-----------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | p | 3 | 3 | 0 | | | | | +| | +----------+----------------+------+---------+----------------+ | | | +| +Limit | 3 | 3 | 3 | 0 | 32 | | | | +| | +----------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan| p:Person | 3 | 4 | 5 | 120 | 3/0 | 0,540 | Fused in Pipeline 0 | ++-----------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 5, total allocated memory: 4920 +Total database accesses: 8, total allocated memory: 184 ---- ====== +[[query-plan-exhaustive-limit]] +=== Exhaustive Limit +// LockNodes - changed in 4.3 -[[query-plan-projection]] -== Projection -// Projection - -For each incoming row, the `Projection` operator evaluates a set of expressions and produces a row with the results of the expressions. +The `ExhaustiveLimit` operator is similar to the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-limit[`Limit`] operator but will always exhaust the input. +Used when combining `LIMIT` and updates -.Projection +.ExhaustiveLimit ====== .Query [source, cypher] ---- PROFILE -RETURN 'hello' AS greeting +MATCH (p:Person) +SET p.seen = true +RETURN p +LIMIT 3 ---- .Query Plan @@ -5943,39 +5642,39 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+---------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+---------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| +ProduceResults | greeting | 1 | 1 | 0 | | | | -| | +---------------------------+----------------+------+---------+ | | | -| +Projection | $autostring_0 AS greeting | 1 | 1 | 0 | 0/0 | 0.000 | Fused in Pipeline 0 | -+-----------------+---------------------------+----------------+------+---------+------------------------+-----------+---------------------+ ++------------------+----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | p | 3 | 3 | 10 | 0 | | | | +| | +----+---------------+----------------+------+---------+----------------+ | | | +| +ExhaustiveLimit | 1 | 3 | 3 | 3 | 0 | 32 | | | | +| | +----+---------------+----------------+------+---------+----------------+ | | | +| +SetProperty | 2 | p.seen = true | 17 | 17 | 34 | | | | | +| | +----+---------------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | 3 | p:Person | 17 | 17 | 18 | 240 | 3/0 | 1.966 | Fused in Pipeline 0 | ++------------------+----+---------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 0, total allocated memory: 184 +Total database accesses: 62, total allocated memory: 304 ---- ====== +[[query-plan-skip]] +=== Skip -[[query-plan-shortest-path]] -== Shortest path -// ShortestPath - -The `ShortestPath` operator finds one or all shortest paths between two previously matched node variables. -This operator is used for the xref:patterns/reference.adoc#shortest-functions[`shortestPath()` and `allShortestPaths`] functions. +The `Skip` operator skips `+n+` rows from the incoming rows. -.ShortestPath +.Skip ====== .Query [source, cypher] ---- PROFILE -MATCH - (andy:Person {name: 'Andy'}), - (mattias:Person {name: 'Mattias'}), - p = shortestPath((andy)-[*]-(mattias)) +MATCH (p:Person) RETURN p +ORDER BY p.id +SKIP 1 ---- .Query Plan @@ -5989,50 +5688,48 @@ Runtime version {neo4j-version-minor} Batch size 128 -+---------------------+-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+---------------------+-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | p | 1 | 1 | 0 | | 1/0 | 0.241 | | -| | +-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +ShortestPath | p = (andy)-[anon_0*]-(mattias) | 1 | 1 | 1 | 1424 | | | In Pipeline 1 | -| | +-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +MultiNodeIndexSeek | RANGE INDEX andy:Person(name) WHERE name = $autostring_0, | 1 | 1 | 4 | 120 | 1/1 | 0.308 | In Pipeline 0 | -| | RANGE INDEX mattias:Person(name) WHERE name = $autostring_1 | | | | | | | | -+---------------------+-------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ ++------------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | ++------------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +ProduceResults | p | 13 | 13 | 0 | | 2/0 | 0.165 | | | +| | +----------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +Skip | $autoint_0 | 13 | 13 | 0 | 32 | 0/0 | 0.043 | | | +| | +----------------+----------------+------+---------+----------------+------------------------+-----------+ | | +| +Sort | `p.id` ASC | 14 | 14 | 0 | 400 | 0/0 | 0.155 | p.id ASC | In Pipeline 1 | +| | +----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ +| +Projection | p.id AS `p.id` | 14 | 14 | 0 | | | | | | +| | +----------------+----------------+------+---------+----------------+ | +------------+ | +| +NodeByLabelScan | p:Person | 18 | 18 | 19 | 120 | 3/0 | 0,157 | | Fused in Pipeline 0 | ++------------------+----------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -Total database accesses: 5, total allocated memory: 1488 +Total database accesses: 71, total allocated memory: 512 ---- ====== -[role=label--new-5.21] -[[query-plan-stateful-shortest-path-into]] -== StatefulShortestPath(Into) -// StatefulShortestPath(Into) -//// -[source, cypher, role=test-setup] ----- -DROP INDEX range_person_name IF EXISTS; -CREATE CONSTRAINT person_name_unique IF NOT EXISTS FOR (p:Person) REQUIRE (p.name) IS UNIQUE; ----- -//// +[[data-modification-operators]] +== Data modification operators -The `StatefulShortestPath(Into)` operator finds shortest paths between a start node and a single target node. -It uses a bidirectional breadth-first search (BFS) algorithm, which performs two BFS invocations at the same time, one from the left boundary node and one from the right boundary node. -Once a node is found by both BFS invocations, which indicates that it can be reached from both boundary nodes, the algorithm successfully terminates. -If one of the BFS invocations exhausts its search before intersecting, either because no further nodes can be reached or because the maximum number of hops has been reached, then there is no valid path between the boundary nodes and the algorithm terminates. +The operators in this group modify graph data by creating, deleting, and altering nodes, relationships, and properties. -.StatefulShortestPath(Into) +[[query-plan-create]] +=== Create + +The `Create` operator is used to create nodes and relationships. + + +.Create ====== .Query [source, cypher] ---- PROFILE -MATCH - p = ALL SHORTEST (chris:Person {name: 'Chris'})(()-[]-()-[]-()){1,}(stefan:Person {name: 'Stefan'}) -RETURN p +CREATE + (max:Person {name: 'Max'}), + (chris:Person {name: 'Chris'}) +CREATE (max)-[:FRIENDS_WITH]->(chris) ---- .Query Plan @@ -6046,44 +5743,37 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | p | 2 | 2 | 0 | 0 | 0/0 | 0.039 | | -| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Projection | 1 | (chris) ((anon_12)-[anon_14]-(anon_13)-[anon_11]-())* (stefan) AS p | 2 | 2 | 0 | | 0/0 | 1.365 | | -| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +StatefulShortestPath(Into) | 2 | SHORTEST 1 GROUPS (chris) ((`anon_5`)-[`anon_6`]-(`anon_7`)-[`anon_8`]-(`anon_9`)){1, } (stefan) | 2 | 2 | 39 | 22237 | 1/0 | 37.376 | In Pipeline 1 | -| | +----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +MultiNodeIndexSeek | 3 | UNIQUE chris:Person(name) WHERE name = $autostring_0, | 1 | 1 | 4 | 376 | 1/1 | 10.245 | In Pipeline 0 | -| | | UNIQUE stefan:Person(name) WHERE name = $autostring_1 | | | | | | | | -+-----------------------------+----+--------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ - -Total database accesses: 43, total allocated memory: 22557 ++-----------------+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +| +ProduceResults | | 1 | 0 | 0 | | | | +| | +---------------------------------------------------------------------------+----------------+------+---------+ | | | +| +EmptyResult | | 1 | 0 | 0 | | | | +| | +---------------------------------------------------------------------------+----------------+------+---------+ | | | +| +Create | (max:Person {name: $autostring_0}), (chris:Person {name: $autostring_1}), | 1 | 1 | 7 | 0/0 | 0.000 | Fused in Pipeline 0 | +| | (max)-[anon_0:FRIENDS_WITH]->(chris) | | | | | | | ++-----------------+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ +Total database accesses: 7, total allocated memory: 184 ---- ====== -[role=label--new-5.21] -[[query-plan-stateful-shortest-path-all]] -== StatefulShortestPath(All) -// StatefulShortestPath(All) -The `StatefulShortestPath(All)` operator finds shortest paths from a single node to multiple target nodes. -It uses a breadth-first search algorithm. +[[query-plan-delete]] +=== Delete +The `Delete` operator is used to delete a node or a relationship. -.StatefulShortestPath(All) +.Delete ====== .Query [source, cypher] ---- PROFILE -MATCH - p = ALL SHORTEST (chris:Person {name:'Chris'})(()-[]-()-[]-()){1,}(location:Location) -RETURN length(p) AS pathLength, location.name AS locationName +MATCH (you:Person {name: 'you'}) +DELETE you ---- .Query Plan @@ -6097,51 +5787,39 @@ Runtime version {neo4j-version-minor} Batch size 128 -+----------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+----------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +ProduceResults | 0 | pathLength, locationName | 14 | 20 | 0 | 0 | 0/0 | 0.074 | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +Projection | 1 | length((chris) ((anon_12)-[anon_14]-(anon_13)-[anon_11]-())* (location)) AS pathLength, | 14 | 20 | 40 | | 1/0 | 6.828 | | -| | | | location.name AS locationName | | | | | | | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | -| +StatefulShortestPath(All) | 2 | SHORTEST 1 GROUPS (chris) ((`anon_5`)-[`anon_6`]-(`anon_7`)-[`anon_8`]-(`anon_9`)){1, } (location) | 14 | 20 | 179 | 37663 | 1/0 | 52.849 | In Pipeline 1 | -| | | | expanding from: chris | | | | | | | | -| | | | inlined predicates: location:Location | | | | | | | | -| | +----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ -| +NodeUniqueIndexSeek | 3 | UNIQUE chris:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 376 | 0/1 | 9.078 | In Pipeline 0 | -+----------------------------+----+----------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------+ - -Total database accesses: 221, total allocated memory: 37983 ++-----------------+----+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | | 0 | 0 | 0 | | | | | +| | +----+--------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +EmptyResult | 1 | | 0 | 0 | 0 | | | | | +| | +----+--------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Delete | 2 | you | 0 | 0 | 0 | | | | | +| | +----+--------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | 3 | RANGE INDEX you:Person(name) WHERE name = $autostring_0 | 0 | 0 | 1 | 120 | 1/0 | 0.330 | Fused in Pipeline 0 | ++-----------------+----+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +Total database accesses: 13, total allocated memory: 216 ---- -//// -[source, cypher, role=test-setup] ----- -DROP CONSTRAINT person_name_unique IF EXISTS; -CREATE RANGE INDEX range_person_name FOR (p:Person) ON (p.name); ----- -//// ====== -[[query-plan-empty-row]] -== Empty Row -// EmptyRow +[[query-plan-detach-delete]] +=== Detach Delete -The `EmptyRow` operator returns a single row with no columns. +The `DetachDelete` operator is used in all queries containing the xref::clauses/delete.adoc[DETACH DELETE] clause, when deleting nodes and their relationships. -.EmptyRow +.DetachDelete ====== .Query [source, cypher] ---- PROFILE -CYPHER runtime=slotted -FOREACH (value IN [1,2,3] | MERGE (:Person {age: value})) +MATCH (p:Person) +DETACH DELETE p ---- .Query Plan @@ -6149,51 +5827,50 @@ FOREACH (value IN [1,2,3] | MERGE (:Person {age: value})) ---- Planner COST -Runtime SLOTTED +Runtime PIPELINED Runtime version {neo4j-version-minor} -+-----------------+--------------------------------------+----------------+------+---------+------------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | -+--------------------+--------------------------------------+----------------+------+---------+------------------------+ -| +ProduceResults | | 1 | 0 | 0 | 0/0 | -| | +--------------------------------------+----------------+------+---------+------------------------+ -| +EmptyResult | | 1 | 0 | 0 | 0/0 | -| | +--------------------------------------+----------------+------+---------+------------------------+ -| +Foreach | value IN [1, 2, 3] | 1 | 1 | 0 | 0/0 | -| |\ +--------------------------------------+----------------+------+---------+------------------------+ -| | +Merge | CREATE (anon_0:Person {age: value}) | 1 | 3 | 9 | 0/0 | -| | | +--------------------------------------+----------------+------+---------+------------------------+ -| | +Filter | anon_0.age = value | 1 | 0 | 184 | 2/0 | -| | | +--------------------------------------+----------------+------+---------+------------------------+ -| | +NodeByLabelScan | anon_0:Person | 35 | 108 | 111 | 3/0 | -| | +--------------------------------------+----------------+------+---------+------------------------+ -| +EmptyRow | | 1 | 1 | 0 | 0/0 | -+--------------------+--------------------------------------+----------------+------+---------+------------------------+ +Batch size 128 -Total database accesses: 304, total allocated memory: 64 ++------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | | 14 | 0 | 0 | | | | | +| | +----------+----------------+------+---------+----------------+ | | | +| +EmptyResult | | 14 | 0 | 0 | | | | | +| | +----------+----------------+------+---------+----------------+ | | | +| +DetachDelete | p | 14 | 14 | 41 | | | | | +| | +----------+----------------+------+---------+----------------+ | | | +| +NodeByLabelScan | p:Person | 14 | 14 | 35 | 120 | 21/0 | 12,439 | Fused in Pipeline 0 | ++------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 112, total allocated memory: 200 ---- ====== +[[query-plan-merge]] +=== Merge +// ConditionalApply -- changed in 4.3 to Merge. +// AntiConditionalApply -- removed in 4.3 (by Merge). -[[query-plan-procedure-call]] -== Procedure Call -// ProcedureCall +The `Merge` operator will either read or create nodes and/or relationships. -The `ProcedureCall` operator indicates an invocation to a procedure. +If matches are found it will execute the provided `ON MATCH` operations foreach incoming row. +If no matches are found instead nodes and relationships are created and all `ON CREATE` operations are run. -.ProcedureCall +.Merge ====== .Query [source, cypher] ---- PROFILE -CALL db.labels() YIELD label -RETURN * -ORDER BY label +MERGE (p:Person {name: 'Andy'}) +ON MATCH SET p.existed = true +ON CREATE SET p.existed = false ---- .Query Plan @@ -6207,41 +5884,40 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Ordered by | Pipeline | -+-----------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProduceResults | label | 10 | 4 | 0 | | 0/0 | 0.091 | | | -| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+ | | -| +Sort | label ASC | 10 | 4 | 0 | 536 | 0/0 | 0.178 | label ASC | In Pipeline 1 | -| | +-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ -| +ProcedureCall | db.labels() :: (label :: STRING) | 10 | 4 | | | | | | Fused in Pipeline 0 | -+-----------------+-----------------------------------+----------------+------+---------+----------------+------------------------+-----------+------------+---------------------+ ++-----------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | | 1 | 0 | 0 | | | | | +| | +-------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +EmptyResult | | 1 | 0 | 0 | | | | | +| | +-------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Merge | CREATE (p:Person {name: $autostring_0}), ON MATCH SET p.existed = true, | 1 | 1 | 2 | | | | | +| | | ON CREATE SET p.existed = false | | | | | | | | +| | +-------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +NodeIndexSeek | RANGE INDEX p:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 120 | 2/1 | 0.749 | Fused in Pipeline 0 | ++-----------------+-------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: ?, total allocated memory: 600 +Total database accesses: 4, total allocated memory: 184 ---- ====== -[[query-plan-cache-properties]] -== Cache Properties -// CacheProperties +[[query-plan-locking-merge]] +=== Locking Merge -The `CacheProperties` operator reads nodes and relationship properties and caches them in the current row. -Future accesses to these properties can avoid reading from the store which will speed up the query. -In the plan below we will cache `l.name` before `Expand(All)` where there are fewer rows. +The `LockingMerge` operator is similar to the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-merge[`Merge`] operator but will lock the start and end node when creating a relationship if necessary. -.CacheProperties +.LockingMerge ====== + .Query [source, cypher] ---- PROFILE -MATCH (l:Location)<-[:WORKS_IN]-(p:Person) -RETURN - l.name AS location, - p.name AS name +MATCH (s:Person {name: 'me'}) +MERGE (s)-[:FRIENDS_WITH]->(s) ---- .Query Plan @@ -6255,46 +5931,48 @@ Runtime version {neo4j-version-minor} Batch size 128 -+------------------+----+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | location, name | 13 | 13 | 0 | | | | | -| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | -| +Projection | 1 | cache[l.name] AS location, p.name AS name | 13 | 13 | 26 | | | | | -| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | -| +Filter | 2 | p:Person | 13 | 13 | 26 | | | | | -| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | -| +Expand(All) | 3 | (l)<-[anon_0:WORKS_IN]-(p) | 13 | 13 | 24 | | | | | -| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | -| +CacheProperties | 4 | cache[l.name] | 10 | 10 | 20 | | | | | -| | +----+-------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | 5 | l:Location | 10 | 10 | 11 | 120 | 4/0 | 0.344 | Fused in Pipeline 0 | -+------------------+----+-------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++-----------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | | 1 | 0 | 0 | | | | | +| | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +EmptyResult | 1 | | 1 | 0 | 0 | | | | | +| | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Apply | 2 | | 1 | 1 | 0 | | | | | +| |\ +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +LockingMerge | 3 | CREATE (s)-[anon_0:FRIENDS_WITH]->(s), LOCK(s) | 1 | 1 | 1 | | | | | +| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Expand(Into) | 4 | (s)-[anon_0:FRIENDS_WITH]->(s) | 0 | 0 | 10 | 904 | | | | +| | | +----+-------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 5 | s | 1 | 3 | 0 | 2280 | 2/0 | 0.460 | Fused in Pipeline 1 | +| | +----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +NodeIndexSeek | 6 | RANGE INDEX s:Person(name) WHERE name = $autostring_0 | 1 | 1 | 2 | 376 | 1/0 | 0.211 | In Pipeline 0 | ++-----------------+----+-------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 107, total allocated memory: 200 +Total database accesses: 15, total allocated memory: 2232 ---- ====== -[[query-plan-create]] -== Create (nodes and relationships) -// Create -The `Create` operator is used to create nodes and relationships. +[[query-plan-foreach]] +=== Foreach +// Foreach +The `Foreach` operator executes a nested loop between the left child operator and the right child operator. +In an analogous manner to the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-apply[Apply] operator, it takes a row from the left-hand side and, using the xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-argument[Argument] operator, provides it to the operator tree on the right-hand side. +`Foreach` will yield all the rows coming in from the left-hand side; all results from the right-hand side are pulled in and discarded. -.Create + +.Foreach ====== .Query [source, cypher] ---- PROFILE -CREATE - (max:Person {name: 'Max'}), - (chris:Person {name: 'Chris'}) -CREATE (max)-[:FRIENDS_WITH]->(chris) +FOREACH (value IN [1,2,3] | CREATE (:Person {age: value})) ---- .Query Plan @@ -6302,45 +5980,45 @@ CREATE (max)-[:FRIENDS_WITH]->(chris) ---- Planner COST -Runtime PIPELINED +Runtime SLOTTED Runtime version {neo4j-version-minor} -Batch size 128 - -+-----------------+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ -| +ProduceResults | | 1 | 0 | 0 | | | | -| | +---------------------------------------------------------------------------+----------------+------+---------+ | | | -| +EmptyResult | | 1 | 0 | 0 | | | | -| | +---------------------------------------------------------------------------+----------------+------+---------+ | | | -| +Create | (max:Person {name: $autostring_0}), (chris:Person {name: $autostring_1}), | 1 | 1 | 7 | 0/0 | 0.000 | Fused in Pipeline 0 | -| | (max)-[anon_0:FRIENDS_WITH]->(chris) | | | | | | | -+-----------------+---------------------------------------------------------------------------+----------------+------+---------+------------------------+-----------+---------------------+ ++-----------------+---------------------------------------------------------+----------------+------+---------+------------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | ++-----------------+---------------------------------------------------------+----------------+------+---------+------------------------+ +| +ProduceResults | | 1 | 0 | 0 | 0/0 | +| | +---------------------------------------------------------+----------------+------+---------+------------------------+ +| +EmptyResult | | 1 | 0 | 0 | 0/0 | +| | +---------------------------------------------------------+----------------+------+---------+------------------------+ +| +Foreach | value IN [1, 2, 3], CREATE (anon_0:Person {age: value}) | 1 | 1 | 9 | 0/0 | ++-----------------+---------------------------------------------------------+----------------+------+---------+------------------------+ -Total database accesses: 7, total allocated memory: 184 +Total database accesses: 9, total allocated memory: 64 ---- ====== +[[query-plan-subquery-foreach]] +=== SubqueryForeach -[[query-plan-delete]] -== Delete (nodes and relationships) -// Delete - -The `Delete` operator is used to delete a node or a relationship. - +`SubqueryForeach` works like the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-foreach[`Foreach`]operator but it is only used for executing subqueries. -.Delete +.SubqueryForeach ====== +[NOTE] +The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. +If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. + .Query [source, cypher] ---- PROFILE -MATCH (you:Person {name: 'you'}) -DELETE you +LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line +CALL (line) { + CREATE (a: Artist {name: line[0]}) +} ---- .Query Plan @@ -6354,40 +6032,48 @@ Runtime version {neo4j-version-minor} Batch size 128 -+-----------------+----+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+-----------------+----+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | 0 | | 0 | 0 | 0 | | | | | -| | +----+--------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +EmptyResult | 1 | | 0 | 0 | 0 | | | | | -| | +----+--------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +Delete | 2 | you | 0 | 0 | 0 | | | | | -| | +----+--------------------------------------------------------+----------------+------+---------+----------------+ | | | -| +NodeIndexSeek | 3 | RANGE INDEX you:Person(name) WHERE name = $autostring_0 | 0 | 0 | 1 | 120 | 1/0 | 0.330 | Fused in Pipeline 0 | -+-----------------+----+--------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ ++------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | | 10 | 0 | 0 | 0 | | | | +| | +----+-------------------------------------+----------------+------+---------+----------------+ | | | +| +EmptyResult | 1 | | 10 | 0 | 0 | | 0/0 | 0.000 | Fused in Pipeline 3 | +| | +----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +SubqueryForeach | 2 | | 10 | 4 | 0 | 4080 | | | | +| |\ +----+-------------------------------------+----------------+------+---------+----------------+ | | | +| | +Create | 3 | (a:Artist {name: line[$autoint_0]}) | 10 | 4 | 12 | | | | | +| | | +----+-------------------------------------+----------------+------+---------+----------------+ | | | +| | +Argument | 4 | line | 10 | 4 | 0 | 3472 | 0/0 | 0.852 | Fused in Pipeline 2 | +| | +----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +LoadCSV | 5 | line | 10 | 4 | 0 | 328 | | | In Pipeline 1 | ++------------------+----+-------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -Total database accesses: 13, total allocated memory: 216 +Total database accesses: 12, total allocated memory: 4928 ---- ====== -[[query-plan-detach-delete]] -== Detach Delete -// DetachDelete - -The `DetachDelete` operator is used in all queries containing the xref::clauses/delete.adoc[DETACH DELETE] clause, when deleting nodes and their relationships. +[[query-plan-transaction-foreach]] +=== TransactionForeach +`TransactionForeach` works like the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-foreach[`Foreach`] operator but will commit the current transaction after a specified number of rows. -.DetachDelete +.TransactionForeach ====== +[NOTE] +The below query uses a xref:subqueries/call-subquery.adoc#variable-scope-clause[variable scope clause] (introduced in Neo4j 5.23) to import variables into the `CALL` subquery. +If you are using an older version of Neo4j, use an xref:subqueries/call-subquery.adoc#importing-with[importing `WITH` clause] instead. + .Query [source, cypher] ---- PROFILE -MATCH (p:Person) -DETACH DELETE p +LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line +CALL (line) { + CREATE (a: Artist {name: line[0]}) +} IN TRANSACTIONS OF 100 ROWS ---- .Query Plan @@ -6395,42 +6081,38 @@ DETACH DELETE p ---- Planner COST -Runtime PIPELINED - -Runtime version {neo4j-version-minor} - -Batch size 128 - -+------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | -+------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -| +ProduceResults | | 14 | 0 | 0 | | | | | -| | +----------+----------------+------+---------+----------------+ | | | -| +EmptyResult | | 14 | 0 | 0 | | | | | -| | +----------+----------------+------+---------+----------------+ | | | -| +DetachDelete | p | 14 | 14 | 41 | | | | | -| | +----------+----------------+------+---------+----------------+ | | | -| +NodeByLabelScan | p:Person | 14 | 14 | 35 | 120 | 21/0 | 12,439 | Fused in Pipeline 0 | -+------------------+----------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ - -Total database accesses: 112, total allocated memory: 200 ----- - -====== +Runtime PIPELINED + +Runtime version {neo4j-version-minor} +Batch size 128 -// MergeCreateNode -- removed in 4.3 +++---------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + | Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | + +---------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + | +ProduceResults | 0 | | 10 | 0 | 0 | 0 | | | | + | | +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | + | +EmptyResult | 1 | | 10 | 0 | 0 | | 0/0 | 0.000 | Fused in Pipeline 3 | + | | +----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + | +TransactionForeach | 2 | IN TRANSACTIONS OF $autoint_1 ROWS ON ERROR FAIL | 10 | 4 | 0 | 4856 | | | | + | |\ +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | + | | +Create | 3 | (a:Artist {name: line[$autoint_0]}) | 10 | 4 | 12 | | | | | + | | | +----+--------------------------------------------------+----------------+------+---------+----------------+ | | | + | | +Argument | 4 | line | 10 | 4 | 0 | 3472 | 0/0 | 0.712 | Fused in Pipeline 2 | + | | +----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + | +LoadCSV | 5 | line | 10 | 4 | 0 | 328 | | | In Pipeline 1 | + +---------------------+----+--------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ -// MergeCreateRelationship -- removed in 4.3 + Total database accesses: 12, total allocated memory: 5704 +---- +====== [[query-plan-set-labels]] -== Set Labels -// SetLabels +=== Set Labels The `SetLabels` operator is used when setting labels on a node. - .SetLabels ====== @@ -6472,12 +6154,10 @@ Total database accesses: 58, total allocated memory: 184 [[query-plan-remove-labels]] -== Remove Labels -// RemoveLabels +=== Remove Labels The `RemoveLabels` operator is used when deleting labels from a node. - .RemoveLabels ====== @@ -6519,12 +6199,10 @@ Total database accesses: 51, total allocated memory: 184 [[query-plan-set-node-properties-from-map]] -== Set Node Properties From Map -// SetNodePropertiesFromMap +=== Set Node Properties From Map The `SetNodePropertiesFromMap` operator is used when setting properties from a map on a node. - .SetNodePropertiesFromMap ====== @@ -6566,12 +6244,10 @@ Total database accesses: 141, total allocated memory: 184 [[query-plan-set-relationship-properties-from-map]] -== Set Relationship Properties From Map -// SetRelationshipPropertiesFromMap +=== Set Relationship Properties From Map The `SetRelationshipPropertiesFromMap` operator is used when setting properties from a map on a relationship. - .SetRelationshipPropertiesFromMap ====== @@ -6613,12 +6289,11 @@ Total database accesses: 112, total allocated memory: 184 [[query-plan-set-property]] -== Set Property +=== Set Property // SetProperty The `SetProperty` operator is used when setting a property on a node or relationship. - .SetProperty ====== @@ -6659,12 +6334,10 @@ Total database accesses: 106, total allocated memory: 184 ====== [[query-plan-set-properties]] -== Set Properties -// SetProperties +=== Set Properties The `SetProperties` operator is used when setting multiple properties on a node or relationship. - .SetProperties ====== @@ -6704,10 +6377,233 @@ Total database accesses: 141, total allocated memory: 312 ====== -[[query-plan-create-constraint]] -== Create Constraint -// CreateConstraint +[[query-plan-load-csv]] +=== Load CSV + +The `LoadCSV` operator loads data from a CSV source into the query. +It is used whenever the xref::clauses/load-csv.adoc[LOAD CSV] clause is used in a query. + + +.LoadCSV +====== + +.Query +[source, cypher] +---- +PROFILE +LOAD CSV FROM 'https://neo4j.com/docs/cypher-refcard/3.3/csv/artists.csv' AS line +RETURN line +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------+ +| +ProduceResults | line | 10 | 4 | 0 | | 0/0 | 0.210 | | +| | +---------+----------------+------+---------+----------------+------------------------+-----------+ | +| +LoadCSV | line | 10 | 4 | 0 | 72 | | | In Pipeline 1 | ++-----------------+---------+----------------+------+---------+----------------+------------------------+-----------+---------------+ + +Total database accesses: 0, total allocated memory: 184 +---- + +====== + + +[[query-plan-eager]] +=== Eager + +The `Eager` operator causes all preceding operators to execute fully, for the whole dataset, before continuing execution. +This is done to ensure isolation between parts of the query plan that might otherwise affect each other. + +Values from the graph are fetched in a lazy manner; i.e. a pattern matching might not be fully exhausted before updates are applied. +To maintain correct semantics, the query planner will insert `Eager` operators into the query plan to prevent updates from influencing pattern matching, or other read operations. +This scenario is exemplified by the query below, where the `DELETE` clause would otherwise influence both the `MATCH` clause and the `MERGE` clause. +For more information on how the `Eager` operator can ensure correct semantics, see the section on xref::clauses/clause-composition.adoc[Clause composition]. + +The `Eager` operator can cause high memory usage when importing data or migrating graph structures. +In such cases, the operations should be split into simpler steps; e.g. importing nodes and relationships separately. +Alternatively, the records to be updated can be returned, followed by an update statement. + + +.Eager +====== + +.Query +[source, cypher] +---- +PROFILE +MATCH (a:Person {name: 'me'}), (b:Person {name: 'Bob'}) +DETACH DELETE a, b +MERGE (:Person {name: 'me'}) +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime PIPELINED + +Runtime version {neo4j-version-minor} + +Batch size 128 + ++---------------------+----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline | ++---------------------+----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +ProduceResults | 0 | | 0 | 0 | 0 | 0 | | | | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +EmptyResult | 1 | | 0 | 0 | 0 | | | | | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Apply | 2 | | 0 | 1 | 0 | | | | | +| |\ +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +Merge | 3 | CREATE (anon_0:Person {name: $autostring_2}) | 0 | 1 | 3 | | | | | +| | | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| | +NodeIndexSeek | 4 | RANGE INDEX anon_0:Person(name) WHERE name = $autostring_2 | 0 | 0 | 1 | 3304 | 1/0 | 0.663 | Fused in Pipeline 3 | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +Eager | 5 | read/delete conflict for variable: anon_0 (Operator: 6 vs 4, and 1 more conflicting operators) | 0 | 1 | 0 | 360 | 0/0 | 0.008 | In Pipeline 2 | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +DetachDelete | 6 | b | 0 | 1 | 4 | | | | | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +DetachDelete | 7 | a | 0 | 1 | 5 | | | | | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+ | | | +| +Eager | 8 | read/delete conflict for variable: b (Operator: 6 vs 10, and 1 more conflicting operators), | 0 | 1 | 0 | 360 | 1/0 | 0.226 | Fused in Pipeline 1 | +| | | | read/set conflict for label: Person (Operator: 3 vs 10), | | | | | | | | +| | | | read/set conflict for property: name (Operator: 3 vs 10) | | | | | | | | +| | +----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ +| +MultiNodeIndexSeek | 9 | RANGE INDEX a:Person(name) WHERE name = $autostring_0, | 0 | 1 | 4 | 376 | 2/0 | 0.218 | In Pipeline 0 | +| | | RANGE INDEX b:Person(name) WHERE name = $autostring_1 | | | | | | | | ++---------------------+----+------------------------------------------------------------------------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+ + +Total database accesses: 17, total allocated memory: 4184 +---- + +====== + + +[[query-plan-assert-same-node]] +=== Assert Same Node + +The `AssertSameNode` operator is used to ensure that no node property uniqueness constraints are violated in the slotted and interpreted runtime. +The example looks for the presence of a team node with the supplied name and id, and if one does not exist, it will be created. +Owing to the existence of two node property uniqueness constraints on `:Team(name)` and `:Team(id)`, any node that would be found by the `UniqueIndexSeek` operator must be the very same node or the constraints would be violated. + + +.AssertSameNode +====== + +.Query +[source, cypher] +---- +PROFILE +CYPHER runtime=slotted +MERGE (t:Team {name: 'Engineering', id: 42}) +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime SLOTTED + +Runtime version {neo4j-version-minor} + ++---------------------------------+-------------------------------------------------------+----------------+------+---------+------------------------+ +| Operator | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | ++---------------------------------+-------------------------------------------------------+----------------+------+---------+------------------------+ +| +ProduceResults | | 1 | 0 | 0 | 0/0 | +| | +-------------------------------------------------------+----------------+------+---------+------------------------+ +| +EmptyResult | | 1 | 0 | 0 | 0/0 | +| | +-------------------------------------------------------+----------------+------+---------+------------------------+ +| +Merge | CREATE (t:Team {name: $autostring_0, id: $autoint_1}) | 1 | 1 | 0 | 0/0 | +| | +-------------------------------------------------------+----------------+------+---------+------------------------+ +| +AssertSameNode | t | 0 | 1 | 0 | 0/0 | +| |\ +-------------------------------------------------------+----------------+------+---------+------------------------+ +| | +NodeUniqueIndexSeek(Locking) | UNIQUE t:Team(id) WHERE id = $autoint_1 | 1 | 1 | 1 | 0/1 | +| | +-------------------------------------------------------+----------------+------+---------+------------------------+ +| +NodeUniqueIndexSeek(Locking) | UNIQUE t:Team(name) WHERE name = $autostring_0 | 1 | 1 | 1 | 0/1 | ++---------------------------------+-------------------------------------------------------+----------------+------+---------+------------------------+ + +Total database accesses: 2, total allocated memory: 64 +---- + +====== + + +[role=label--new-5.8] +[[query-plan-assert-same-relationship]] +=== Assert Same Relationship + +The `AssertSameRelationship` operator is used to ensure that no relationship property uniqueness constraints are violated in the slotted and interpreted runtime. +The example looks for the presence of a `WORKS_IN` relationship with the supplied `id` and `badgeNumber`. +If it can't be found, then it will be created. +Owing to the existence of two property uniqueness constraints on `:WORKS_IN(id)` and `:WORKS_IN(badgeNumber)`, any relationship that would be found by the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-directed-relationship-unique-index-seek[`DirectedRelationshipUniqueIndexSeek`] operator must be the very same relationship or the constraints would be violated. + + +.AssertSameRelationship +====== + +.Query +[source, cypher] +---- +PROFILE +CYPHER runtime=slotted +MERGE (person)-[work:WORKS_IN {id: 0, badgeNumber: 4332}]->(location) +---- + +.Query Plan +[role="queryplan", subs="attributes+"] +---- +Planner COST + +Runtime SLOTTED + +Runtime version {neo4j-version-minor} + ++-------------------------------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Page Cache Hits/Misses | ++-------------------------------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| +ProduceResults | 0 | | 1 | 0 | 0 | 0/0 | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| +EmptyResult | 1 | | 1 | 0 | 0 | 0/0 | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| +Merge | 2 | CREATE (person), (location), (person)-[work:WORKS_IN {id: $autoint_0, badgeNumber: $autoint_1}]->(lo | 1 | 1 | 0 | 0/0 | +| | | | cation) | | | | | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| +AssertSameRelationship | 3 | work | 0 | 1 | 0 | 0/0 | +| |\ +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| | +DirectedRelationshipUniqueIndexSeek(Locking) | 4 | RANGE INDEX (person)-[work:WORKS_IN(badgeNumber)]->(location) WHERE badgeNumber = $autoint_1 | 1 | 1 | 1 | 0/1 | +| | +----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ +| +DirectedRelationshipUniqueIndexSeek(Locking) | 5 | RANGE INDEX (person)-[work:WORKS_IN(id)]->(location) WHERE id = $autoint_0 | 1 | 1 | 1 | 1/1 | ++-------------------------------------------------+----+------------------------------------------------------------------------------------------------------+----------------+------+---------+------------------------+ + +Total database accesses: 2, total allocated memory: 64 +---- + +====== + + +[[schema-system-operators]] +== Schema and system operators + +The operators in this group manage the schema and system-level operations within the database. +They handle tasks such as creating or dropping constraints and indexes, showing or terminating transactions, and listing the available procedures, functions, and settings. + +[[query-plan-create-constraint]] +=== Create Constraint The `CreateConstraint` operator creates a constraint. @@ -6720,7 +6616,6 @@ This constraint can have any of the available constraint types: The following query will create a property uniqueness constraint with the name `uniqueness` on the `name` property of nodes with the `Country` label. - .CreateConstraint ====== @@ -6754,8 +6649,7 @@ Total database accesses: ? [[query-plan-do-nothing-if-exists-constraint]] -== Do Nothing If Exists (constraint) -// DoNothingIfExists(CONSTRAINT) +=== Do Nothing If Exists (constraint) To not get an error creating the same constraint twice, we use the `DoNothingIfExists` operator for constraints. This will make sure no other constraint with the given name or another constraint of the same type and schema already exists before the specific `CreateConstraint` operator creates the constraint. @@ -6799,13 +6693,10 @@ Total database accesses: ? ====== [[query-plan-drop-constraint]] -== Drop Constraint -// DropConstraint +=== Drop Constraint The `DropConstraint` operator removes a constraint using the name of the constraint, no matter the type. - - .DropConstraint ====== @@ -6838,13 +6729,11 @@ Total database accesses: ? [[query-plan-show-constraints]] -== Show Constraints -// ShowConstraints +=== Show Constraints The `ShowConstraints` operator lists constraints. It may include filtering on constraint type and can have either default or full output. - .ShowConstraints ====== @@ -6879,8 +6768,7 @@ Total database accesses: 2, total allocated memory: 64 [[query-plan-create-index]] -== Create Index -// CreateIndex +=== Create Index The `CreateIndex` operator creates an index. @@ -6921,8 +6809,7 @@ Total database accesses: ? [[query-plan-do-nothing-if-exists-index]] -== Do Nothing If Exists (index) -// DoNothingIfExists(INDEX) +=== Do Nothing If Exists (index) To not get an error creating the same index twice, we use the `DoNothingIfExists` operator for indexes. This will make sure no other index with the given name or schema already exists before the `CreateIndex` operator creates an index. @@ -6965,12 +6852,10 @@ Total database accesses: ? [[query-plan-drop-index]] -== Drop Index -// DropIndex +=== Drop Index The `DropIndex` operator removes an index using the name of the index. - .DropIndex ====== @@ -7003,13 +6888,11 @@ Total database accesses: ? [[query-plan-show-indexes]] -== Show Indexes -// ShowIndexes +=== Show Indexes The `ShowIndexes` operator lists indexes. It may include filtering on index type and can have either default or full output. - .ShowIndexes ====== @@ -7045,8 +6928,7 @@ Total database accesses: 2, total allocated memory: 64 [[query-plan-show-functions]] -== Show Functions -// ShowFunctions +=== Show Functions The `ShowFunctions` operator lists functions. It may include filtering on built-in vs user-defined functions as well as if a given user can execute the function. @@ -7087,13 +6969,11 @@ Total database accesses: 0, total allocated memory: 64 [[query-plan-show-procedures]] -== Show Procedures -// ShowProcedures +=== Show Procedures The `ShowProcedures` operator lists procedures. It may include filtering on whether a given user can execute the procedure and can have either default or full output. - .ShowProcedures ====== @@ -7127,8 +7007,7 @@ Total database accesses: 0, total allocated memory: 64 ====== [[query-plan-show-settings]] -== Show Settings -// ShowSettings +=== Show Settings The `ShowSettings` operator lists configuration settings. @@ -7165,13 +7044,11 @@ Total database accesses: 0, total allocated memory: 64 ====== [[query-plan-show-transactions]] -== Show Transactions -// ShowTransactions +=== Show Transactions The `ShowTransactions` operator lists transactions. It may include filtering on given ids and can have either default or full output. - .ShowTransactions ====== @@ -7207,12 +7084,10 @@ Total database accesses: 0, total allocated memory: 64 [[query-plan-terminate-transactions]] -== Terminate Transactions -// TerminateTransactions +=== Terminate Transactions The `TerminateTransactions` operator terminates transactions by ID. - .TerminateTransactions ====== diff --git a/modules/ROOT/pages/planning-and-tuning/query-tuning.adoc b/modules/ROOT/pages/planning-and-tuning/query-tuning.adoc index b8ba76a7e..07faae8a7 100644 --- a/modules/ROOT/pages/planning-and-tuning/query-tuning.adoc +++ b/modules/ROOT/pages/planning-and-tuning/query-tuning.adoc @@ -308,7 +308,7 @@ During times of known high load, `replan=skip` can be useful to not introduce un === Cypher infer schema parts For some queries, the planner can infer predicates such as labels or types from the graph structure, thereby enhancing its ability to estimate the number of rows each operator will produce. -(See xref:planning-and-tuning/execution-plans.adoc#runtimes-reading-execution-plans[Understanding execution plans - Reading execution plans] for more information about the role of operators and estimated row counts in query execution plans.) +(See xref:planning-and-tuning/execution-plans.adoc#reading-execution-plans[Understanding execution plans - Reading execution plans] for more information about the role of operators and estimated row counts in query execution plans.) The option `inferSchemaParts` controls the extent to which the planner should infer predicates. [options="header",cols="2m,3a"]