From 07ff037a4e1563bfb8d7392d3bc3e86949faf4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 14 Aug 2025 15:19:02 +0200 Subject: [PATCH 1/4] New operators for Dynamic values (#1360) Applies to both Cypher 5 and Cypher 25 --- modules/ROOT/pages/clauses/match.adoc | 22 ++- modules/ROOT/pages/clauses/merge.adoc | 49 ++----- ...ions-additions-removals-compatibility.adoc | 25 ++++ .../operators/operators-detail.adoc | 136 ++++++++++++++++++ 4 files changed, 195 insertions(+), 37 deletions(-) diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index cc40a7b1d..31ad53a95 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -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`]. +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 diff --git a/modules/ROOT/pages/clauses/merge.adoc b/modules/ROOT/pages/clauses/merge.adoc index 9f905e4ef..23c1369bc 100644 --- a/modules/ROOT/pages/clauses/merge.adoc +++ b/modules/ROOT/pages/clauses/merge.adoc @@ -741,42 +741,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*() +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 From dd5f6eac23b847e24337575cce8d3be056122736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 4 Aug 2025 10:54:26 +0200 Subject: [PATCH 2/4] update antora for 2025.08 (#1350) --- antora.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/antora.yml b/antora.yml index 25c134786..7a1ac5e76 100644 --- a/antora.yml +++ b/antora.yml @@ -6,4 +6,4 @@ nav: - modules/ROOT/content-nav.adoc asciidoc: attributes: - neo4j-version: '2025.07' + neo4j-version: '2025.08' From e6ab4351289eb27633c4f10db49b1cc85b478019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:57:07 +0200 Subject: [PATCH 3/4] add new dynamic operators to overview table (#1363) Cypher 5 and Cypher 25 --- .../planning-and-tuning/operators/index.adoc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/modules/ROOT/pages/planning-and-tuning/operators/index.adoc b/modules/ROOT/pages/planning-and-tuning/operators/index.adoc index 5b4413e9f..322e6d1ae 100644 --- a/modules/ROOT/pages/planning-and-tuning/operators/index.adoc +++ b/modules/ROOT/pages/planning-and-tuning/operators/index.adoc @@ -212,6 +212,25 @@ Restricts the xref:planning-and-tuning/runtimes/index.adoc[Cypher runtime] to no | label:yes[] | +| xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-node-label-lookup[DynamicNodeLabelLookup] +| Allows Cypher to use token lookup indexes when planning queries using dynamic node labels. +| label:yes[] +| +| label:new[Introduced in Neo4j 2025.08] + + +| xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-directed-relationship-type-lookup[DynamicDirectedRelationshipTypeLookup] +| Allows Cypher to use token lookup indexes when planning queries using dynamic relationship types in directed relationship patterns. +| label:yes[] +| +| label:new[Introduced in Neo4j 2025.08] + +| xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-undirected-relationship-type-lookup[DynamicUndirectedRelationshipTypeLookup] +| Allows Cypher to use token lookup indexes when planning queries using dynamic relationship types in undirected relationship patterns. +| label:yes[] +| +| label:new[Introduced in Neo4j 2025.08] + | xref::planning-and-tuning/operators/operators-detail.adoc#query-plan-eager[Eager] | For isolation purposes, `Eager` ensures that operations affecting subsequent operations are executed fully for the whole dataset before continuing execution. | From a3c5b14371b1dbe9221183f8d7ebf7eebd135908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 18 Aug 2025 14:38:06 +0200 Subject: [PATCH 4/4] dynamiclabelnodelookup not dynamicnodelabellookup (#1364) --- modules/ROOT/pages/clauses/match.adoc | 4 ++-- modules/ROOT/pages/clauses/merge.adoc | 4 ++-- .../deprecations-additions-removals-compatibility.adoc | 2 +- modules/ROOT/pages/planning-and-tuning/operators/index.adoc | 2 +- .../planning-and-tuning/operators/operators-detail.adoc | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index 31ad53a95..33510ea25 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -654,9 +654,9 @@ The table below outlines performance caveats for specific Neo4j versions. | 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`]. +xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-label-node-lookup[`DynamicLabelNodeLookup`], 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, `MATCH (n:$(Label) {foo: bar})` will not use any indexes on `n.foo` but can use a `DynamicNodeLabelLookup` on `$(label)`. +For example, `MATCH (n:$(Label) {foo: bar})` will not use any indexes on `n.foo` but can use a `DynamicLabelNodeLookup` on `$(label)`. |=== diff --git a/modules/ROOT/pages/clauses/merge.adoc b/modules/ROOT/pages/clauses/merge.adoc index 23c1369bc..41fabe9c8 100644 --- a/modules/ROOT/pages/clauses/merge.adoc +++ b/modules/ROOT/pages/clauses/merge.adoc @@ -756,8 +756,8 @@ The table below outlines performance caveats for specific Neo4j versions. | 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`]. +xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-label-node-lookup[`DynamicLabelNodeLookup`], 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)`. +For example, `MERGE (n:$(Label) {foo: bar})` will not use any indexes on `n.foo` but can use a `DynamicLabelNodeLookup` on `$(label)`. |=== \ No newline at end of file diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 47f19114e..0a9e5e6c8 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -50,7 +50,7 @@ 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`]. +This is enabled by the introduction of three new query plan operators: xref:planning-and-tuning/operators/operators-detail.adoc#query-plan-dynamic-label-node-lookup[`DynamicLabelNodeLookup`], 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.06]] diff --git a/modules/ROOT/pages/planning-and-tuning/operators/index.adoc b/modules/ROOT/pages/planning-and-tuning/operators/index.adoc index 322e6d1ae..0cd2ebc76 100644 --- a/modules/ROOT/pages/planning-and-tuning/operators/index.adoc +++ b/modules/ROOT/pages/planning-and-tuning/operators/index.adoc @@ -212,7 +212,7 @@ Restricts the xref:planning-and-tuning/runtimes/index.adoc[Cypher runtime] to no | label:yes[] | -| 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-label-node-lookup[DynamicLabelNodeLookup] | Allows Cypher to use token lookup indexes when planning queries using dynamic node labels. | label:yes[] | 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 c075ea4ed..4547bf7e9 100644 --- a/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc +++ b/modules/ROOT/pages/planning-and-tuning/operators/operators-detail.adoc @@ -2600,12 +2600,12 @@ Total database accesses: 106 ====== [role=label--new-2025.08] -[[query-plan-dynamic-node-label-lookup]] -=== Dynamic Node Label Lookup +[[query-plan-dynamic-label-node-lookup]] +=== Dynamic Label Node 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 +.DynamicLabelNodeLookup ====== .Query