Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions modules/ROOT/pages/clauses/match.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -639,10 +639,26 @@ RETURN relationshipType, count(r) AS relationshipCount
[[dynamic-match-caveats]]
=== Performance caveats

`MATCH` queries using dynamic values may not be as performant as those using static values.
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.
`MATCH` queries that use dynamic values may not perform as well as those with static values.
Neo4j is actively working to improve the performance of these queries.
The table below outlines performance caveats for specific Neo4j versions.

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.
.Neo4j versions and performance caveats
[%header,cols="a,5a"]
|===
| Neo4j versions | Performance caveat

| 5.26 -- 2025.07
| 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.

| 2025.08 -- current
| 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.
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`].

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm afraid this is incorrect - the operator is called DynamicLabelNodeLookup!

It is not, however, able to use indexes on property values.
For example, `MATCH (n:$(Label) {foo: bar})` will not use any indexes on `n.foo` but can use a `DynamicNodeLabelLookup` on `$(label)`.

|===

[[further-reading]]
=== Further reading
Expand Down
49 changes: 15 additions & 34 deletions modules/ROOT/pages/clauses/merge.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -740,42 +740,23 @@ RETURN greta.name AS name, labels(greta) AS labels, type(rel) AS relType, collec
[[dynamic-merge-caveats]]
=== Performance caveats

`MERGE` queries that use dynamic values may not be as performant as those using static values.
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.
`MERGE` queries that use dynamic values may not perform as well as those with static values.
Neo4j is actively working to improve the performance of these queries.
The table below outlines performance caveats for specific Neo4j versions.

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.

To circumvent possible performance issues, place the dynamic labels or relationship types within `ON CREATE` or `ON MATCH` subclauses.

.Parameters
[source, parameters]
----
{
"onMatchLabels": ["Filmmaker", "AwardRecipient"],
"onCreateLabels": ["ScreenWriter", "AwardWinner"]
}
----

.Merge nodes using dynamic values in `ON CREATE` and `ON MATCH` subclauses
[source, cypher]
----
MERGE (n:Person {name: "Greta Gerwig"})
ON MATCH
SET n:$($onMatchLabels)
ON CREATE
SET n:$($onCreateLabels)
RETURN labels(n) AS gretaLabels
----

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.

.Result
[role="queryresult",options="footer",cols="1*<m"]
.Neo4j versions and performance caveats
[%header,cols="a,5a"]
|===
| gretaLabels
| Neo4j versions | Performance caveat

| ["Person", "Director", "Filmmaker", "AwardRecipient"]
| 5.26 -- 2025.07
| 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.

1+d|Rows: 1
|===
| 2025.08 -- current
| 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.
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`].
It is not, however, able to use indexes on property values.
For example, `MERGE (n:$(Label) {foo: bar})` will not use any indexes on `n.foo` but can use a `DynamicNodeLabelLookup` on `$(label)`.

|===
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,33 @@ Cypher 25 was introduced in Neo4j 2025.06 and can only be used on Neo4j 2025.06+
Features removed in Cypher 25 are still available on Neo4j 2025.06+ databases either by prepending a query with `CYPHER 5` or by having Cypher 5 as the default language for the database.
For more information, see xref:queries/select-version.adoc[].

[[cypher-deprecations-additions-removals-2025.08]]
== Neo4j 2025.08

=== Updated in Cypher 25

[cols="2", options="header"]
|===
| Feature
| Details

a|
label:functionality[]
label:updated[]
[source, cypher]
----
PROFILE
WITH "Person" AS label
MATCH (people:$(label))
RETURN people.name
----

| 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].
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`].

|===


[[cypher-deprecations-additions-removals-2025.07]]
== Neo4j 2025.07

Expand Down
136 changes: 136 additions & 0 deletions modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2576,6 +2576,142 @@ Total database accesses: 106

======

[role=label--new-2025.08]
[[query-plan-dynamic-node-label-lookup]]
=== Dynamic Node Label Lookup

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].

.DynamicNodeLabelLookup
======

.Query
[source, cypher]
----
PROFILE
WITH "Person" AS label
MATCH (people:$(label))
RETURN people.name
----

.Query Plan
[role="queryplan", subs="attributes+"]
----
Planner COST

Runtime PIPELINED

Runtime version {neo4j-version}

Batch size 128


+---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline |
+---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
| +ProduceResults | 0 | `people.name` | 26 | 14 | 0 | 0 | | | |
| | +----+------------------------------+----------------+------+---------+----------------+ | | |
| +Projection | 1 | people.name AS `people.name` | 26 | 14 | 28 | | | | |
| | +----+------------------------------+----------------+------+---------+----------------+ | | |
| +Apply | 2 | | 26 | 14 | 0 | | | | |
| |\ +----+------------------------------+----------------+------+---------+----------------+ | | |
| | +DynamicLabelNodeLookup | 3 | people:$all(label) | 26 | 14 | 15 | 1832 | 2/0 | 0.366 | Fused in Pipeline 1 |
| | +----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
| +Projection | 4 | $autostring_0 AS label | 1 | 1 | 0 | | | | Fused in Pipeline 0 |
+---------------------------+----+------------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
----
======

[role=label--new-2025.08]
[[query-plan-dynamic-directed-relationship-type-lookup]]
=== Dynamic Directed Relationship Type Lookup

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.

.DynamicDirectedRelationshipTypeLookup
======

.Query
[source, cypher]
----
PROFILE
WITH "FRIENDS_WITH" AS relType
MATCH ()-[r:$(relType)]->()
RETURN count(r) as relCount
----

.Query Plan
[role="queryplan", subs="attributes+"]
----
Planner COST

Runtime PIPELINED

Runtime version {neo4j-version}

Batch size 128

+------------------------------------------+----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline |
+------------------------------------------+----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
| +ProduceResults | 0 | relCount | 1 | 1 | 0 | 0 | 0/0 | 0.022 | In Pipeline 2 |
| | +----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
| +EagerAggregation | 1 | count(r) AS relCount | 1 | 1 | 0 | 40 | | | |
| | +----+--------------------------+----------------+------+---------+----------------+ | | |
| +Apply | 2 | | 21 | 12 | 0 | | | | |
| |\ +----+--------------------------+----------------+------+---------+----------------+ | | |
| | +DynamicDirectedRelationshipTypeLookup | 3 | ()-[r:$all(relType)]->() | 21 | 12 | 13 | 1968 | 2/0 | 0.359 | Fused in Pipeline 1 |
| | +----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
| +Projection | 4 | $autostring_0 AS relType | 1 | 1 | 0 | | | | Fused in Pipeline 0 |
+------------------------------------------+----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
----
======


[role=label--new-2025.08]
[[query-plan-dynamic-undirected-relationship-type-lookup]]
=== Dynamic Undirected Relationship Type Lookup

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.

.DynamicUndirectedRelationshipTypeLookup
======

.Query
[source, cypher]
----
PROFILE
WITH "FRIENDS_WITH" AS relType
MATCH ()-[r:$(relType)]-()
RETURN count(r) as relCount
----

.Query Plan
[role="queryplan", subs="attributes+"]
----
Planner COST

Runtime PIPELINED

Runtime version {neo4j-version}

Batch size 128

+--------------------------------------------+----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
| Operator | Id | Details | Estimated Rows | Rows | DB Hits | Memory (Bytes) | Page Cache Hits/Misses | Time (ms) | Pipeline |
+--------------------------------------------+----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
| +ProduceResults | 0 | relCount | 1 | 1 | 0 | 0 | 0/0 | 0.011 | In Pipeline 2 |
| | +----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
| +EagerAggregation | 1 | count(r) AS relCount | 1 | 1 | 0 | 40 | | | |
| | +----+--------------------------+----------------+------+---------+----------------+ | | |
| +Apply | 2 | | 42 | 24 | 0 | | | | |
| |\ +----+--------------------------+----------------+------+---------+----------------+ | | |
| | +DynamicUndirectedRelationshipTypeLookup | 3 | ()-[r:$all(relType)]-() | 42 | 24 | 13 | 1968 | 2/0 | 0.121 | Fused in Pipeline 1 |
| | +----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
| +Projection | 4 | $autostring_0 AS relType | 1 | 1 | 0 | | | | Fused in Pipeline 0 |
+--------------------------------------------+----+--------------------------+----------------+------+---------+----------------+------------------------+-----------+---------------------+
----
======

[[nested-loops-join-operators]]
== Nested loops and join operators
Expand Down