diff --git a/modules/ROOT/images/graph-predicate-functions.svg b/modules/ROOT/images/graph-predicate-functions.svg index 12f5b3f19..17237ae08 100644 --- a/modules/ROOT/images/graph-predicate-functions.svg +++ b/modules/ROOT/images/graph-predicate-functions.svg @@ -1,39 +1,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ROOT/images/predicate-function-example.svg b/modules/ROOT/images/predicate-function-example.svg deleted file mode 100644 index 7c0fe2032..000000000 --- a/modules/ROOT/images/predicate-function-example.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 51b7133a9..1849ce54d 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -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 + +=== New in Cypher 25 + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:new[] +[source, cypher, role="noheader"] +---- +MATCH (()-->(n))+ +WHERE allReduce(acc = 0, node IN n \| acc + node.x, 6 < acc < 30) +RETURN [i IN n \| i.x] AS sequence +ORDER BY head(n).x, size(n) +---- + +| New xref:functions/predicate.adoc#functions-allreduce[`allReduce()`] function. +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. + +|=== + + [[cypher-deprecations-additions-removals-2025.07]] == Neo4j 2025.07 diff --git a/modules/ROOT/pages/functions/index.adoc b/modules/ROOT/pages/functions/index.adoc index 73eebb482..c800083f3 100644 --- a/modules/ROOT/pages/functions/index.adoc +++ b/modules/ROOT/pages/functions/index.adoc @@ -373,7 +373,12 @@ These functions return either true or false for the given arguments. 1.1+| xref::functions/predicate.adoc#functions-all[`all()`] | `all(variable :: ANY, list :: LIST, predicate :: ANY) :: BOOLEAN` | Returns true if the predicate holds for all elements in the given `LIST`. - + +1.1+| xref::functions/predicate.adoc#functions-allreduce[`allReduce()`] +| `allReduce(accumulator = initial, stepVariable IN list \| reductionFunction, predicate) :: BOOLEAN` +| Returns true if, during the stepwise evaluation of a value across the elements in a given `LIST`, the accumulated result satisfies a specified predicate at every step. +Where that list is a group variable defined in a quantified path pattern, it allows for the early pruning of paths that do not satisfy the predicate. label:new[Introduced in Neo4j 2025.08] + 1.1+| xref::functions/predicate.adoc#functions-any[`any()`] | `any(variable :: ANY, list :: LIST, predicate :: ANY) :: BOOLEAN` | Returns true if the predicate holds for at least one element in the given `LIST`. diff --git a/modules/ROOT/pages/functions/predicate.adoc b/modules/ROOT/pages/functions/predicate.adoc index 6fe2e4197..5c465c2cf 100644 --- a/modules/ROOT/pages/functions/predicate.adoc +++ b/modules/ROOT/pages/functions/predicate.adoc @@ -27,12 +27,12 @@ CREATE (kathryn:Person {name:'Kathryn Bigelow', age:71, nationality:'American'}), (jessica:Person {name:'Jessica Chastain', age:45, address:''}), (theMatrix:Movie {title:'The Matrix'}), - (keanu)-[:KNOWS]->(carrie), - (keanu)-[:KNOWS]->(liam), - (keanu)-[:KNOWS]->(kathryn), - (kathryn)-[:KNOWS]->(jessica), - (carrie)-[:KNOWS]->(guy), - (liam)-[:KNOWS]->(guy), + (keanu)-[:KNOWS {since: 1999}]->(carrie), + (keanu)-[:KNOWS {since: 2005}]->(liam), + (keanu)-[:KNOWS {since: 2010}]->(kathryn), + (kathryn)-[:KNOWS {since: 2012}]->(jessica), + (carrie)-[:KNOWS {since: 2008}]->(guy), + (liam)-[:KNOWS {since: 2009}]->(guy), (keanu)-[:ACTED_IN]->(theMatrix), (carrie)-[:ACTED_IN]->(theMatrix) ---- @@ -58,34 +58,29 @@ CREATE | `all()` returns `true` if `list` is empty because there are no elements to falsify the `predicate`. |=== -.+all()+ +.all() ====== -.Query +.Find paths where all nodes meet a given property value // tag::functions_predicate_all[] [source, cypher, indent=0] ---- -MATCH p = (a)-[*]->(b) -WHERE - a.name = 'Keanu Reeves' - AND b.name = 'Guy Pearce' - AND all(x IN nodes(p) WHERE x.age < 60) -RETURN p +MATCH p = (a:Person {name: 'Keanu Reeves'})-[]-{2,}() +WHERE all(x IN nodes(p) WHERE x.age < 60) +RETURN [n IN nodes(p) | n.name] AS actorsList ---- // end::functions_predicate_all[] -All nodes in the returned paths will have a property `age` with a value lower than `60`: - -image::predicate-function-example.svg[Actor nodes connected via knows relationships,width=300,role=popup] +All nodes in the returned paths have an `age`property below `60`: .Result [role="queryresult",options="header,footer",cols="1*(:Person {nationality: "American",name: "Carrie Anne Moss",age: 55})-[:KNOWS]->(:Person {nationality: "Australian",name: "Guy Pearce",age: 55})+ -1+d|Rows: 1 +| ["Keanu Reeves", "Carrie Anne Moss", "Guy Pearce"] +1+d|Rows: 2 |=== .`all()` on an empty `LIST` @@ -107,7 +102,102 @@ RETURN all(i in emptyList WHERE true) as allTrue, all(i in emptyList WHERE false ====== +[[functions-allreduce]] +[role=label--new-2025.08] +== allReduce() + +.Details +|=== +| *Syntax* 3+| `allReduce(accumulator = initial, stepVariable IN list \| reductionFunction, predicate)` +| *Description* 3+| Returns true if, during the stepwise evaluation of a value across the elements in a given `LIST`, the accumulated result satisfies a specified predicate at every step. +Where that list is a xref:patterns/variable-length-patterns.adoc#group-variables[group variable] defined in a xref:patterns/variable-length-patterns.adoc#quantified-path-patterns[quantified path pattern], it allows for the early pruning of paths that do not satisfy the predicate. +.7+| *Arguments* | *Name* | *Type* | *Description* +| `accumulator` | `ANY` | A variable that holds the result of the `reductionFunction` as the `list` is iterated. +It is initialized with the value of `initial`. +| `initial` | `ANY` | The value of the `accumulator` for the first evaluation of `reductionFunction`. +| `stepVariable` | `ANY` | A variable that holds the value of each element of `list` during iteration. +| `list` | `LIST` | The list that is being iterated over. +| `reductionFunction` | `ANY` | An expression whose return value becomes the next value of the `accumulator`. +The return type must match the return type of `initial`. +| `predicate` | `BOOLEAN` | A predicate that is evaluated for each iteration. +It has access to the variable `accumulator`, but not `stepVariable`. +| *Returns* 3+| `BOOLEAN` +|=== + +.Considerations +|=== +| `allReduce()` differs from most Cypher functions because it iterates over a list, evaluating an expression for each element, rather than returning a result from a single evaluation. +| `allReduce()` combines the functionality of the xref:functions/predicate.adoc#functions-all[`all()`] and xref:functions/list.adoc#functions-reduce[`reduce()`] functions. +| If all evaluations of `predicate` are `true`, `allReduce()` will return `true`. +| If any evaluations of `predicate` are `false`, `allReduce()` will return `false`. +| `allReduce()` returns `true` if `list` is empty because there are no elements to falsify the `predicate`. +| `null` is returned if the `list` is `null` or if the `predicate` evaluates to `null` for at least one element and does not evaluate to `false` for any other element. +|=== + +.allReduce() +====== + +The below query finds `KNOWS` paths with a length of `3` where the `accumulator` begins with first node's `age` and the accumulated `age` values of all nodes in the path never exceeds `230`. +Paths that do not meet this requirement are excluded, such as the path with the sequence `["Keanu Reeves (58)", "Carrie Anne Moss (55)", "Guy Pearce (55)", "Liam Neeson (70)"]` which has an aggregated `age` value of `238`. +.Find aggregated ages within a boundary +// tag::functions_predicate_allreduce_boundary[] +[source, cypher] +---- +MATCH (s) (()-[:KNOWS]-(n)){3} +WHERE allReduce( + acc = s.age, + node IN n | acc + node.age, + acc < 230 +) +RETURN [i IN [s] + n | i.name || " (" + toString(i.age) || ")"] AS ageSequence, + reduce(acc = 0, node IN [s] + n | acc + node.age) AS aggregatedAges +ORDER BY aggregatedAges +---- +// end::functions_predicate_allreduce_boundary[] + +.Result +[role="queryresult",options="header,footer",cols="2* 2000 +) +LET people = nodes(path) +RETURN [actor IN people | actor.name] AS connectedActors, + [rel IN r | rel.since] AS sinceYears +ORDER BY sinceYears +---- +// end::functions_predicate_allreduce_relationship_values[] + +.Result +[role="queryresult",options="header,footer",cols="2*()) AS has_acted_in_rel +RETURN p.name AS name, + exists((p)-[:ACTED_IN]->()) AS has_acted_in_rel ---- // end::functions_predicate_exists[] @@ -316,11 +402,11 @@ The `name` property of each node that has an empty `STRING` `address` property i .Result [role="queryresult",options="header,footer",cols="1*(b) -WHERE - n.name = 'Keanu Reeves' - AND none(x IN nodes(p) WHERE x.age > 60) -RETURN p +MATCH p = (n:Person {name: 'Keanu Reeves'})-[]-{2}() +WHERE none(x IN nodes(p) WHERE x.age > 60) +RETURN [x IN nodes(p) | x.name] AS connectedActors ---- // end::functions_predicate_none[] -No node in the returned path has an `age` property with a greater value than `60`: - -image::predicate-function-example.svg[Actor nodes connected via knows relationships,width=300,role=popup] +No nodes in the returned paths have an `age` property with a greater value than `60`: .Result [role="queryresult",options="header,footer",cols="1*(:Person {nationality: "American",name: "Carrie Anne Moss",age: 55}) -| (:Person {nationality: "Canadian",name: "Keanu Reeves",age: 58})-[:KNOWS]->(:Person {nationality: "American",name: "Carrie Anne Moss",age: 55})-[:KNOWS]->(:Person {nationality: "Australian",name: "Guy Pearce",age: 55}) -1+d|Rows: 2 +| ["Keanu Reeves", "Carrie Anne Moss", "Guy Pearce"] +1+d|Rows: 1 |=== .`none()` on an empty `LIST` @@ -429,28 +510,26 @@ RETURN none(i IN emptyList WHERE true) as noneTrue, none(i IN emptyList WHERE fa .+single()+ ====== -.Query +.Find paths where exactly one node has a given property value // tag::functions_predicate_single[] [source, cypher, indent=0] ---- -MATCH p = (n)-->(b) -WHERE - n.name = 'Keanu Reeves' - AND single(x IN nodes(p) WHERE x.nationality = 'Northern Irish') -RETURN p +MATCH p = (n:Person {name: 'Keanu Reeves'})-[:KNOWS]-+(b) +WHERE single(x IN [b] WHERE x.nationality = 'Northern Irish') +RETURN [person IN nodes(p) | person.name + " (" + person.nationality + ")"] AS northernIrishPaths +ORDER BY length(p) ---- // end::functions_predicate_single[] -In every returned path there is exactly one node which has the `nationality` property value `Northern Irish`: - .Result [role="queryresult",options="header,footer",cols="1*(:Person {nationality: "Northern Irish",name: "Liam Neeson",age: 70}) -1+d|Rows: 1 +| ["Keanu Reeves (Canadian)", "Liam Neeson (Northern Irish)"] +| ["Keanu Reeves (Canadian)", "Carrie Anne Moss (American)", "Guy Pearce (Australian)", "Liam Neeson (Northern Irish)"] +1+d|Rows: 2 |=== .`single()` on an empty `LIST`