Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
| 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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
| 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.
| 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
| 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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
| 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.
| 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 indexes on property values.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
It is not, however, able to indexes on property values.
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`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
For example, `MATCH (n:$(Label) {foo: bar})` will not use any indexes on `n.foo`.
For example, `MATCH (n:$(label) {foo: bar})` will not use any indexes on `n.foo` but might use a `DynamicNodeLabelLookup` on `$(label)`.


|===

[[further-reading]]
=== Further reading
Expand Down
24 changes: 20 additions & 4 deletions modules/ROOT/pages/clauses/merge.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -740,12 +740,28 @@ 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.
.Neo4j versions and performance caveats
[%header,cols="a,5a"]
|===
| Neo4j versions | Performance caveat

| 5.26 -- 2025.07
| 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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
| 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.
| 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
| 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.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
| 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.
| 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 indexes on property values.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
It is not, however, able to indexes on property values.
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`.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
For example, `MERGE (n:$(Label) {foo: bar})` will not use any indexes on `n.foo`.
For example, `MERGE (n:$(label) {foo: bar})` will not use any indexes on `n.foo` but might use a `DynamicNodeLabelLookup` on `$(label)`.


|===

To circumvent possible performance issues, place the dynamic labels or relationship types within `ON CREATE` or `ON MATCH` subclauses.
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.
Copy link
Contributor

Choose a reason for hiding this comment

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

I know this "workaround" has been there before but I do not see that particular example helping:

If you have a static label Label, then

MERGE (n:Person:$($dynamicLabels) {name: "Greta Gerwig"})

should plan a label scan on :Person and then add a filter on :$($dynamicLabels) without ON .... But if you want to add other labels depending on MATCH or MERGE behaviour, you will need to use the ON ... anyway.


.Parameters
[source, parameters]
Expand Down
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
----

| 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].
Copy link
Contributor

Choose a reason for hiding this comment

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

I find it a bit odd from the wording to read that the labels/types are leveraging the indexes. I my mind, the execution engine (or the planner) is leveraging indexes to answer queries with dynamic labels/types. But maybe this is splitting hairs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, I think what I wrote is too ambiguous. Will update.

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`].
Copy link
Contributor

Choose a reason for hiding this comment

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

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