Skip to content

Commit d8c377e

Browse files
query plan
1 parent f84991c commit d8c377e

File tree

4 files changed

+168
-9
lines changed

4 files changed

+168
-9
lines changed

modules/ROOT/pages/clauses/match.adoc

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -639,10 +639,26 @@ RETURN relationshipType, count(r) AS relationshipCount
639639
[[dynamic-match-caveats]]
640640
=== Performance caveats
641641

642-
`MATCH` queries using dynamic values may not be as performant as those using static values.
643-
This is because the xref:planning-and-tuning/execution-plans.adoc[Cypher planner] uses statically available information when planning queries to determine whether to use an xref:indexes/search-performance-indexes/index.adoc[index] or not, and this is not possible when using dynamic values.
642+
`MATCH` queries that use dynamic values may not perform as well as those with static values.
643+
Neo4j is actively working to improve the performance of these queries.
644+
The table below outlines performance caveats for specific Neo4j versions.
644645

645-
As a result, `MATCH` queries using dynamic values cannot leverage xref:planning-and-tuning/operators/operators-detail.adoc#leaf-operators[index scans or seeks] and must instead use the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-all-nodes-scan[`AllNodesScan`] operator, which reads all nodes from the node store and is therefore more costly.
646+
.Neo4j versions and performance caveats
647+
[%header,cols="a,5a"]
648+
|===
649+
| Neo4j versions | Performance caveat
650+
651+
| 5.26 -- 2025.07
652+
| xref:planning-and-tuning/execution-plans.adoc[Cypher planner] not able to leverage xref:indexes/search-performance-indexes/index.adoc[indexes] with xref:planning-and-tuning/operators/operators-detail.adoc#leaf-operators[index scans or seeks] and must instead utilize the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-all-nodes-scan[`AllNodesScan`] operator, which reads all nodes from the node store and is therefore more costly.
653+
654+
| 2025.08 -- current
655+
| Cypher planner is able to leverage leverage xref:indexes/search-performance-indexes/using-indexes.adoc#token-lookup-indexes[token lookup indexes] when matching node labels and relationship types dynamically.
656+
This is enabled by the introduction of three new query plan operators:
657+
xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-node-label-lookup[`DynamicNodeLabelLookup`], xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-directed-relationship-type-lookup[`DynamicDirectedRelationshipTypeLookup`], and xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-undirected-relationship-type-lookup[`DynamicUndirectedRelationshipTypeLookup`].
658+
It is not, however, able to indexes on property values.
659+
For example, `MATCH (n:$(Label) {foo: bar})` will not use any indexes on `n.foo`.
660+
661+
|===
646662

647663
[[further-reading]]
648664
=== Further reading

modules/ROOT/pages/clauses/merge.adoc

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -740,12 +740,28 @@ RETURN greta.name AS name, labels(greta) AS labels, type(rel) AS relType, collec
740740
[[dynamic-merge-caveats]]
741741
=== Performance caveats
742742

743-
`MERGE` queries that use dynamic values may not be as performant as those using static values.
744-
This is because the xref:planning-and-tuning/execution-plans.adoc[Cypher planner] uses statically available information when planning queries to determine whether to use an xref:indexes/search-performance-indexes/index.adoc[index] or not, and this is not possible when using dynamic values.
743+
`MATCH` queries that use dynamic values may not perform as well as those with static values.
744+
Neo4j is actively working to improve the performance of these queries.
745+
The table below outlines performance caveats for specific Neo4j versions.
745746

746-
As a result, `MERGE` queries with dynamic values cannot leverage xref:planning-and-tuning/operators/operators-detail.adoc#leaf-operators[index scans or seeks] and must instead use the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-all-nodes-scan[`AllNodesScan`] operator, which reads all nodes from the node store and is therefore more costly.
747+
.Neo4j versions and performance caveats
748+
[%header,cols="a,5a"]
749+
|===
750+
| Neo4j versions | Performance caveat
751+
752+
| 5.26 -- 2025.07
753+
| xref:planning-and-tuning/execution-plans.adoc[Cypher planner] not able to leverage xref:indexes/search-performance-indexes/index.adoc[indexes] with xref:planning-and-tuning/operators/operators-detail.adoc#leaf-operators[index scans or seeks] and must instead utilize the xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-all-nodes-scan[`AllNodesScan`] operator, which reads all nodes from the node store and is therefore more costly.
754+
755+
| 2025.08 -- current
756+
| Cypher planner is able to leverage leverage xref:indexes/search-performance-indexes/using-indexes.adoc#token-lookup-indexes[token lookup indexes] when matching node labels and relationship types dynamically.
757+
This is enabled by the introduction of three new query plan operators:
758+
xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-node-label-lookup[`DynamicNodeLabelLookup`], xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-directed-relationship-type-lookup[`DynamicDirectedRelationshipTypeLookup`], and xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-undirected-relationship-type-lookup[`DynamicUndirectedRelationshipTypeLookup`].
759+
It is not, however, able to indexes on property values.
760+
For example, `MERGE (n:$(Label) {foo: bar})` will not use any indexes on `n.foo`.
761+
762+
|===
747763

748-
To circumvent possible performance issues, place the dynamic labels or relationship types within `ON CREATE` or `ON MATCH` subclauses.
764+
If you are using Neo4j 5.26 -- 2025.07, you can circumvent the inability of the Cypher planner to leverage token lookup indexes by placing the dynamic labels or relationship types within `ON CREATE` or `ON MATCH` subclauses.
749765

750766
.Parameters
751767
[source, parameters]

modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,13 @@ label:functionality[]
3737
label:updated[]
3838
[source, cypher]
3939
----
40-
40+
PROFILE
41+
WITH "Person" AS label
42+
MATCH (people:$(label))
43+
RETURN people.name
4144
----
4245

43-
| Dynamic labels and relationship types can leverage xref:indexes/search-performance-indexes/using-indexes.adoc#token-lookup-indexes[token lookup indexes].
46+
| xref:clauses/match.adoc#dynamic-match[Dynamic labels and relationship types] can now leverage xref:indexes/search-performance-indexes/using-indexes.adoc#token-lookup-indexes[token lookup indexes].
4447
This is enables by the introduction of three new query plan operators: xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-node-label-lookup[`DynamicNodeLabelLookup`], xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-directed-relationship-type-lookup[`DynamicDirectedRelationshipTypeLookup`], and xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-undirected-relationship-type-lookup[`DynamicUndirectedRelationshipTypeLookup`].
4548

4649
|===

modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2580,14 +2580,138 @@ Total database accesses: 106
25802580
[[query-plan-dynamic-node-label-lookup]]
25812581
== Dynamic Node Label Lookup
25822582

2583+
Allows Cypher to use xref:indexes/search-performance-indexes/using-indexes.adoc#token-lookup-indexes[token lookup indexes] when planning queries using xref:clauses/match.adoc#dynamic-match[dynamic node labels].
2584+
2585+
.DynamicNodeLabelLookup
2586+
======
2587+
2588+
.Query
2589+
[source, cypher]
2590+
----
2591+
PROFILE
2592+
WITH "Person" AS label
2593+
MATCH (people:$(label))
2594+
RETURN people.name
2595+
----
2596+
2597+
.Query Plan
2598+
[role="queryplan", subs="attributes+"]
2599+
----
2600+
Planner COST
2601+
2602+
Runtime PIPELINED
2603+
2604+
Runtime version {neo4j-version}
2605+
2606+
Batch size 128
2607+
2608+
2609+
+---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2610+
| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline |
2611+
+---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2612+
| +ProduceResults | 0 | `people.name` | 26 | 14 | 0 | 0 | | | |
2613+
| | +----+------------------------------+----------------+------+---------+----------------+ | | |
2614+
| +Projection | 1 | people.name AS `people.name` | 26 | 14 | 28 | | | | |
2615+
| | +----+------------------------------+----------------+------+---------+----------------+ | | |
2616+
| +Apply | 2 | | 26 | 14 | 0 | | | | |
2617+
| |\ +----+------------------------------+----------------+------+---------+----------------+ | | |
2618+
| | +DynamicLabelNodeLookup | 3 | people:$all(label) | 26 | 14 | 15 | 1832 | 2/0 | 0.366 | Fused in Pipeline 1 |
2619+
| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2620+
| +Projection | 4 | $autostring_0 AS label | 1 | 1 | 0 | | | | Fused in Pipeline 0 |
2621+
+---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2622+
----
2623+
======
2624+
25832625
[role=label--new-2025.08]
25842626
[[query-plan-dynamic-directed-relationship-type-lookup]]
25852627
== Dynamic Directed Relationship Type Lookup
25862628

2629+
Allows Cypher to use xref:indexes/search-performance-indexes/using-indexes.adoc#token-lookup-indexes[token lookup indexes] when planning queries using xref:clauses/match.adoc#dynamic-match[dynamic relationship types] in directed relationship patterns.
2630+
2631+
.DynamicDirectedRelationshipTypeLookup
2632+
======
2633+
2634+
.Query
2635+
[source, cypher]
2636+
----
2637+
PROFILE
2638+
WITH "FRIENDS_WITH" AS relType
2639+
MATCH ()-[r:$(relType)]->()
2640+
RETURN count(r) as relCount
2641+
----
2642+
2643+
.Query Plan
2644+
[role="queryplan", subs="attributes+"]
2645+
----
2646+
Planner COST
2647+
2648+
Runtime PIPELINED
2649+
2650+
Runtime version {neo4j-version}
2651+
2652+
Batch size 128
2653+
2654+
+------------------------------------------+----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2655+
| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline |
2656+
+------------------------------------------+----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2657+
| +ProduceResults | 0 | relCount | 1 | 1 | 0 | 0 | 0/0 | 0.022 | In Pipeline 2 |
2658+
| | +----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2659+
| +EagerAggregation | 1 | count(r) AS relCount | 1 | 1 | 0 | 40 | | | |
2660+
| | +----+--------------------------+----------------+------+---------+----------------+ | | |
2661+
| +Apply | 2 | | 21 | 12 | 0 | | | | |
2662+
| |\ +----+--------------------------+----------------+------+---------+----------------+ | | |
2663+
| | +DynamicDirectedRelationshipTypeLookup | 3 | ()-[r:$all(relType)]->() | 21 | 12 | 13 | 1968 | 2/0 | 0.359 | Fused in Pipeline 1 |
2664+
| | +----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2665+
| +Projection | 4 | $autostring_0 AS relType | 1 | 1 | 0 | | | | Fused in Pipeline 0 |
2666+
+------------------------------------------+----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2667+
----
2668+
======
2669+
2670+
25872671
[role=label--new-2025.08]
25882672
[[query-plan-dynamic-undirected-relationship-type-lookup]]
25892673
== Dynamic Undirected Relationship Type Lookup
25902674

2675+
Allows Cypher to use xref:indexes/search-performance-indexes/using-indexes.adoc#token-lookup-indexes[token lookup indexes] when planning queries using dynamic relationship types in undirected relationship patterns.
2676+
2677+
.DynamicUndirectedRelationshipTypeLookup
2678+
======
2679+
2680+
.Query
2681+
[source, cypher]
2682+
----
2683+
PROFILE
2684+
WITH "FRIENDS_WITH" AS relType
2685+
MATCH ()-[r:$(relType)]-()
2686+
RETURN count(r) as relCount
2687+
----
2688+
2689+
.Query Plan
2690+
[role="queryplan", subs="attributes+"]
2691+
----
2692+
Planner COST
2693+
2694+
Runtime PIPELINED
2695+
2696+
Runtime version {neo4j-version}
2697+
2698+
Batch size 128
2699+
2700+
+--------------------------------------------+----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2701+
| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline |
2702+
+--------------------------------------------+----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2703+
| +ProduceResults | 0 | relCount | 1 | 1 | 0 | 0 | 0/0 | 0.011 | In Pipeline 2 |
2704+
| | +----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2705+
| +EagerAggregation | 1 | count(r) AS relCount | 1 | 1 | 0 | 40 | | | |
2706+
| | +----+--------------------------+----------------+------+---------+----------------+ | | |
2707+
| +Apply | 2 | | 42 | 24 | 0 | | | | |
2708+
| |\ +----+--------------------------+----------------+------+---------+----------------+ | | |
2709+
| | +DynamicUndirectedRelationshipTypeLookup | 3 | ()-[r:$all(relType)]-() | 42 | 24 | 13 | 1968 | 2/0 | 0.121 | Fused in Pipeline 1 |
2710+
| | +----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2711+
| +Projection | 4 | $autostring_0 AS relType | 1 | 1 | 0 | | | | Fused in Pipeline 0 |
2712+
+--------------------------------------------+----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
2713+
----
2714+
======
25912715

25922716
[[nested-loops-join-operators]]
25932717
== Nested loops and join operators

0 commit comments

Comments
 (0)