Skip to content

Commit 5efacb1

Browse files
New operators for Dynamic values (#1360)
Applies to both Cypher 5 and Cypher 25
1 parent 7fc9c3e commit 5efacb1

File tree

4 files changed

+192
-38
lines changed

4 files changed

+192
-38
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+
| The xref:planning-and-tuning/execution-plans.adoc[Cypher planner] is 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+
| The Cypher planner is able to 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 use indexes on property values.
659+
For example, `MATCH (n:$(Label) {foo: bar})` will not use any indexes on `n.foo` but can use a `DynamicNodeLabelLookup` on `$(label)`.
660+
661+
|===
646662

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

modules/ROOT/pages/clauses/merge.adoc

Lines changed: 15 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -740,42 +740,23 @@ 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+
`MERGE` 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-
748-
To circumvent possible performance issues, place the dynamic labels or relationship types within `ON CREATE` or `ON MATCH` subclauses.
749-
750-
.Parameters
751-
[source, parameters]
752-
----
753-
{
754-
"onMatchLabels": ["Filmmaker", "AwardRecipient"],
755-
"onCreateLabels": ["ScreenWriter", "AwardWinner"]
756-
}
757-
----
758-
759-
.Merge nodes using dynamic values in `ON CREATE` and `ON MATCH` subclauses
760-
[source, cypher]
761-
----
762-
MERGE (n:Person {name: "Greta Gerwig"})
763-
ON MATCH
764-
SET n:$($onMatchLabels)
765-
ON CREATE
766-
SET n:$($onCreateLabels)
767-
RETURN labels(n) AS gretaLabels
768-
----
769-
770-
Because a `Person` node with the `name` "Greta Gerwig" already exists, this query will only `SET` the dynamic labels added to the `ON MATCH` subclause.
771-
772-
.Result
773-
[role="queryresult",options="footer",cols="1*<m"]
747+
.Neo4j versions and performance caveats
748+
[%header,cols="a,5a"]
774749
|===
775-
| gretaLabels
750+
| Neo4j versions | Performance caveat
776751

777-
| ["Person", "Director", "Filmmaker", "AwardRecipient"]
752+
| 5.26 -- 2025.07
753+
| The xref:planning-and-tuning/execution-plans.adoc[Cypher planner] is 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.
778754

779-
1+d|Rows: 1
780-
|===
755+
| 2025.08 -- current
756+
| The Cypher planner is able to 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 use indexes on property values.
760+
For example, `MERGE (n:$(Label) {foo: bar})` will not use any indexes on `n.foo` but can use a `DynamicNodeLabelLookup` on `$(label)`.
781761

762+
|===

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,28 @@ For more information, see xref:queries/select-version.adoc[].
2525
[[cypher-deprecations-additions-removals-2025.08]]
2626
== Neo4j 2025.08
2727

28+
=== Updated in Cypher 25
29+
30+
[cols="2", options="header"]
31+
|===
32+
| Feature
33+
| Details
34+
35+
a|
36+
label:functionality[]
37+
label:updated[]
38+
[source, cypher]
39+
----
40+
PROFILE
41+
WITH "Person" AS label
42+
MATCH (people:$(label))
43+
RETURN people.name
44+
----
45+
46+
| Cypher can now leverage xref:indexes/search-performance-indexes/using-indexes.adoc#token-lookup-indexes[token lookup indexes] when planning queries with xref:clauses/match.adoc#dynamic-match[dynamic labels and relationship types].
47+
This is enabled 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`].
48+
|===
49+
2850
=== New in Cypher 25
2951

3052
[cols="2", options="header"]
@@ -45,7 +67,6 @@ ORDER BY head(n).x, size(n)
4567

4668
| New xref:functions/predicate.adoc#functions-allreduce[`allReduce()`] function.
4769
It enables the stepwise evaluation of a value accumulated over a path, allowing for early pruning of paths that do not satisfy a given predicate, and is optimized for path expansions.
48-
4970
|===
5071

5172

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

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2576,6 +2576,142 @@ Total database accesses: 106
25762576
25772577
======
25782578

2579+
[role=label--new-2025.08]
2580+
[[query-plan-dynamic-node-label-lookup]]
2581+
=== Dynamic Node Label Lookup
2582+
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+
2625+
[role=label--new-2025.08]
2626+
[[query-plan-dynamic-directed-relationship-type-lookup]]
2627+
=== Dynamic Directed Relationship Type Lookup
2628+
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+
2671+
[role=label--new-2025.08]
2672+
[[query-plan-dynamic-undirected-relationship-type-lookup]]
2673+
=== Dynamic Undirected Relationship Type Lookup
2674+
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+
======
25792715

25802716
[[nested-loops-join-operators]]
25812717
== Nested loops and join operators

0 commit comments

Comments
 (0)