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' diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index cc40a7b1d..33510ea25 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-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 `DynamicLabelNodeLookup` on `$(label)`. + +|=== [[further-reading]] === Further reading diff --git a/modules/ROOT/pages/clauses/merge.adoc b/modules/ROOT/pages/clauses/merge.adoc index 9f905e4ef..41fabe9c8 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