diff --git a/antora.yml b/antora.yml index 49a7d0d63..ac8acc752 100644 --- a/antora.yml +++ b/antora.yml @@ -7,5 +7,5 @@ nav: asciidoc: attributes: neo4j-version: '5' - neo4j-version-minor: '5.2' - neo4j-version-exact: '5.2.0' + neo4j-version-minor: '5.3' + neo4j-version-exact: '5.3.0' diff --git a/modules/ROOT/images/graph_expression_subqueries.svg b/modules/ROOT/images/graph_expression_subqueries.svg new file mode 100644 index 000000000..b037298a9 --- /dev/null +++ b/modules/ROOT/images/graph_expression_subqueries.svg @@ -0,0 +1,119 @@ + + + + + + +L + + + +Andy + +Swedish, Person + +age = 36 +name = 'Andy' + + + +DogAndy + +Dog + +name = 'Andy' + + + +Andy->DogAndy + + +  HAS_DOG +since = 2016 + + + +Timothy + +Person + +age = 25 +name = 'Timothy' + + + +Mittens + +Cat + +name = 'Mittens' + + + +Timothy->Mittens + + +  HAS_CAT +since = 2019 + + + +Peter + +Person + +age = 35 +name = 'Peter' + + + +Ozzy + +Dog + +name = 'Ozzy' + + + +Peter->Ozzy + + +  HAS_DOG +since = 2018 + + + +Fido + +Dog + +name = 'Fido' + + + +Peter->Fido + + +  HAS_DOG +since = 2010 + + + +Banana + +Toy + +name = 'Banana' + + + +Fido->Banana + + +  HAS_TOY + + + diff --git a/modules/ROOT/pages/access-control/manage-roles.adoc b/modules/ROOT/pages/access-control/manage-roles.adoc index ffc006fb3..f303b1305 100644 --- a/modules/ROOT/pages/access-control/manage-roles.adoc +++ b/modules/ROOT/pages/access-control/manage-roles.adoc @@ -27,7 +27,7 @@ m| SHOW ROLES a| [source, syntax, role="noheader"] ---- -SHOW [ALL\|POPULATED] ROLES +SHOW [ALL\|POPULATED] ROLE[S] [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] [WHERE expression] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] @@ -63,7 +63,7 @@ m| SHOW ROLES WITH USERS a| [source, syntax, role="noheader"] ---- -SHOW [ALL\|POPULATED] ROLES WITH USERS +SHOW [ALL\|POPULATED] ROLE[S] WITH USER[S] [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] [WHERE expression] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] @@ -84,7 +84,6 @@ a| GRANT SHOW ROLE ---- -[source, privilege, role="noheader"] (see xref::access-control/dbms-administration.adoc#access-control-dbms-administration-role-management[DBMS ROLE MANAGEMENT privileges]) diff --git a/modules/ROOT/pages/access-control/manage-servers.adoc b/modules/ROOT/pages/access-control/manage-servers.adoc index 1692ba52a..2942260d5 100644 --- a/modules/ROOT/pages/access-control/manage-servers.adoc +++ b/modules/ROOT/pages/access-control/manage-servers.adoc @@ -153,7 +153,7 @@ m| SHOW SERVERS a| [source, cyper, role=noplay] ---- -SHOW SERVERS +SHOW SERVER[S] [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] [WHERE expression] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] @@ -352,6 +352,11 @@ Using `DRYRUN REALLOCATE DATABASE` returns a view of how the databases would hav | "db3" | "server-1" | "00000000-94ff-4ede-87be-3d741b795480" | "server-5" | "00000003-0df7-4057-81fd-1cf43c9ef5f7" | "primary" |=== +[NOTE] +==== +`DRYRUN` is introduced in Neo4j 5.2, and thus is not available in earlier minor releases of v5. +==== + [[server-management-deallocate]] == Deallocate databases @@ -370,7 +375,6 @@ Using `DRYRUN DEALLOCATE DATABASES FROM 'server-1', 'server-2'` returns a view o [options="header,footer", width="100%", cols="m,m,m,m,m,m"] |=== |database|fromServerName|fromServerId|toServerName|toServerId|mode - | "db1" | "server-1" | "00000001-8c04-4731-a2fd-7b0289c511ce" | "server-4" | "00000002-5b91-43c1-8b25-5289f674563e" | "primary" | "db1" | "server-2" | "00000000-7e53-427c-a987-24634c4745f3" | "server-5" | "00000003-0e98-44c8-9844-f0a4eb95b0d8" | "primary" |=== diff --git a/modules/ROOT/pages/access-control/manage-users.adoc b/modules/ROOT/pages/access-control/manage-users.adoc index 12d2c9fbc..2005fa7cb 100644 --- a/modules/ROOT/pages/access-control/manage-users.adoc +++ b/modules/ROOT/pages/access-control/manage-users.adoc @@ -55,7 +55,7 @@ m| SHOW USERS a| [source, syntax, role="noheader"] ---- -SHOW USERS +SHOW USER[S] [YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] [WHERE expression] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] @@ -477,7 +477,7 @@ This example shows how to: [source, cypher, role=noplay] ---- -SHOW USERS YIELD user, suspended, passwordChangeRequired, roles, home +SHOW USER YIELD user, suspended, passwordChangeRequired, roles, home WHERE user = 'jake' ---- ====== diff --git a/modules/ROOT/pages/clauses/union.adoc b/modules/ROOT/pages/clauses/union.adoc index 7bb7fc475..25221e6aa 100644 --- a/modules/ROOT/pages/clauses/union.adoc +++ b/modules/ROOT/pages/clauses/union.adoc @@ -15,6 +15,16 @@ The number and the names of the columns must be identical in all queries combine To keep all the result rows, use `UNION ALL`. Using just `UNION` will combine and remove duplicates from the result set. +[NOTE] +==== +If any of the queries in a UNION contain updates, the order of queries in the UNION is relevant. + +Any clause before the UNION cannot observe writes made by a clause after the UNION. +Any clause after UNION can observe all writes made by a clause before the UNION. + +For details see xref::introduction/clause_composition.adoc#cypher-clause-composition-union-queries[clause composition in queries with `UNION`] for details. +==== + image:graph_union_clause.svg[] //// diff --git a/modules/ROOT/pages/databases.adoc b/modules/ROOT/pages/databases.adoc index 6ccea090b..f46d26bd6 100644 --- a/modules/ROOT/pages/databases.adoc +++ b/modules/ROOT/pages/databases.adoc @@ -24,13 +24,13 @@ The syntax of the database management commands is as follows: | [source, syntax, role="noheader"] ---- -SHOW { DATABASE name \| DATABASES \| DEFAULT DATABASE \| HOME DATABASE } +SHOW { DATABASE[S] name \| DATABASE[S] \| DEFAULT DATABASE \| HOME DATABASE } [WHERE expression] ---- [source, syntax, role="noheader"] ---- -SHOW { DATABASE name \| DATABASES \| DEFAULT DATABASE \| HOME DATABASE } +SHOW { DATABASE[S] name \| DATABASE[S] \| DEFAULT DATABASE \| HOME DATABASE } YIELD { * \| field[, ...] } [ORDER BY field[, ...]] [SKIP n] [LIMIT n] [WHERE expression] [RETURN field[, ...] [ORDER BY field[, ...]] [SKIP n] [LIMIT n]] @@ -258,6 +258,11 @@ However, some privileges enable users to see additional databases regardless of If a user has not been granted `ACCESS` privilege to any databases nor any of the above special cases, the command can still be executed but will only return the `system` database, which is always visible. ==== +[NOTE] +==== +Databases hosted on servers that are offline are also returned by the `SHOW DATABASES` command. +For such databases, the `address` column displays `NULL`, the `currentStatus` column displays `unknown`, and the `statusMessage` displays `Server is unavailable`. +==== ====== diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index c498a5b64..649634b25 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -1,5 +1,5 @@ [[cypher-deprecations-additions-removals-compatibility]] -= Deprecations, additions and compatibility += Deprecations, additions, and compatibility :description: all of the features that have been removed, deprecated, added, or extended in different Cypher versions. @@ -9,6 +9,60 @@ New features are added to the language continuously, and occasionally, some feat This section lists all of the features that have been removed, deprecated, added, or extended in different Cypher versions. Replacement syntax for deprecated and removed features are also indicated. +[[cypher-deprecations-additions-removals-5.3]] +== Version 5.3 + +=== Updated features + +[cols="2", options="header"] +|=== +| Feature +| Details + +a| +label:functionality[] +label:updated[] +[source, cypher, role="noheader"] +---- +SHOW DATABASES +---- +a| +Changes to the visibility of databases hosted on offline servers. + +For such databases: + +* The `address` column will return `NULL`. +* The `currentStatus` column will return `unknown`. +* The `statusMessage` will return `Server is unavailable`. + +a| +label:functionality[] +label:updated[] +[source, cypher, role="noheader"] +---- +EXISTS { + ... +} +---- +a| + +An `EXISTS` subquery now supports any non-writing query. For example, it now supports `UNION` and `CALL` clauses. + + +a| +label:functionality[] +label:updated[] +[source, cypher, role="noheader"] +---- +COUNT { + ... +} +---- +a| + +A `COUNT` subquery now supports any non-writing query. For example, it now supports `UNION` and `CALL` clauses. + +|=== [[cypher-deprecations-additions-removals-5.2]] == Version 5.2 @@ -59,6 +113,16 @@ a| Creating composite databases now allows for an empty options clause. There are no applicable option values for composite databases. +a| +label:functionality[] +label:new[] +[source, cypher, role="noheader"] +---- +DRYRUN REALLOCATE\|DEALLOCATE DATABASES FROM +---- + +a| To preview of the result of either `REALLOCATE` or `DEALLOCATE` without executing, prepend the command with `DRYRUN`. + |=== [[cypher-deprecations-additions-removals-5.1]] diff --git a/modules/ROOT/pages/syntax/expressions.adoc b/modules/ROOT/pages/syntax/expressions.adoc index c07783c84..f0589b036 100644 --- a/modules/ROOT/pages/syntax/expressions.adoc +++ b/modules/ROOT/pages/syntax/expressions.adoc @@ -437,79 +437,39 @@ Variables introduced inside the subquery are not part of the outside scope and t The following graph is used for the examples below: -.Graph -["dot", "Subquery expressions-1.svg", "neoviz", ""] ----- - N0 [ - label = "{Swedish, Person|age = 36\lname = \'Andy\'\l}" - ] - N0 -> N3 [ - color = "#2e3436" - fontcolor = "#2e3436" - label = "HAS_DOG\nsince = 2016\l" - ] - N1 [ - label = "{Person|age = 25\lname = \'Timothy\'\l}" - ] - N2 [ - label = "{Person|age = 35\lname = \'Peter\'\l}" - ] - N2 -> N5 [ - color = "#2e3436" - fontcolor = "#2e3436" - label = "HAS_DOG\nsince = 2018\l" - ] - N2 -> N4 [ - color = "#2e3436" - fontcolor = "#2e3436" - label = "HAS_DOG\nsince = 2010\l" - ] - N3 [ - label = "{Dog|name = \'Andy\'\l}" - ] - N4 [ - label = "{Dog|name = \'Fido\'\l}" - ] - N4 -> N6 [ - color = "#4e9a06" - fontcolor = "#4e9a06" - label = "HAS_TOY\n" - ] - N5 [ - label = "{Dog|name = \'Ozzy\'\l}" - ] - N6 [ - label = "{Toy|name = \'Banana\'\l}" - ] - ----- - +//// +CREATE +(andy:Swedish:Person {name: 'Andy', age: 36}), +(timothy:Person {name: 'Timothy', age: 25}), +(peter:Person {name: 'Peter', age: 35}), +(andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) +//// + +image:graph_expression_subqueries.svg[] [[existential-subqueries]] === `EXISTS` subqueries An `EXISTS` subquery can be used to find out if a specified pattern exists at least once in the data. -It serves the same purpose as a <> but is more powerful because it allows you to use `MATCH` and `WHERE` clauses internally. +It serves the same purpose as a xref::clauses/where.adoc#filter-on-patterns[path pattern] but is more powerful because it allows you to use `MATCH` and `WHERE` clauses internally. Moreover, it can appear in any expression position, unlike path patterns. If the subquery evaluates to at least one row, the whole expression will become `true`. This also means that the system only needs to evaluate if there is at least one row and can skip the rest of the work. +Any non-writing query is allowed. `EXISTS` subqueries differ from regular queries in that the final `RETURN` clause may be omitted, +as any variable defined within the subquery will not be available outside of the expression, even if a final `RETURN` clause is used. -*Syntax:* -[source, cypher, role=noplay] -EXISTS { - MATCH [Pattern] - WHERE [Expression] -} - - -It is worth noting that the `MATCH` keyword can be omitted in such subqueries and that the `WHERE` clause is optional. +It is worth noting that the `MATCH` keyword can be omitted in subqueries in cases where the `EXISTS` consists of only +a pattern and an optional `WHERE` clause. [[existential-subquery-simple-case]] ==== Simple `EXISTS` subquery -Variables introduced by the outside scope can be used in the `EXISTS` subquery without importing them, -unlike the case for `CALL` subqueries, <>. +Variables introduced by the outside scope can be used in the `EXISTS` subquery without importing them. +In this regard, `EXISTS` subqueries are different from `CALL` subqueries, xref::clauses/call-subquery.adoc#subquery-correlated-importing[which do require importing]. The following example shows this: @@ -518,7 +478,7 @@ The following example shows this: ---- MATCH (person:Person) WHERE EXISTS { - MATCH (person)-[:HAS_DOG]->(:Dog) + (person)-[:HAS_DOG]->(:Dog) } RETURN person.name AS name ---- @@ -543,13 +503,14 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) ]]>(:Dog) + (person)-[:HAS_DOG]->(:Dog) } RETURN person.name AS name ]]> @@ -593,6 +554,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -648,6 +610,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -702,6 +665,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -714,27 +678,189 @@ RETURN person.name AS name, EXISTS { ++++ endif::nonhtmloutput[] -[[count-subqueries]] -=== `COUNT` subqueries +[[existential-subquery-with-union]] +==== `EXISTS` subquery with a `UNION` -A `COUNT` subquery expression can be used to count the number of results of the subquery. +`Exists` can be used with a `UNION` clause, and the `RETURN` clauses are not required. +It is worth noting that if one branch has a `RETURN` clause, then all branches require one. +The below example demonstrates that if one of the `UNION` branches was to return at least one row, the entire `EXISTS` expression will evaluate to true. +.Query +[source, cypher] +---- +MATCH (person:Person) +RETURN + person.name AS name, + EXISTS { + MATCH (person)-[:HAS_DOG]->(:Dog) + UNION + MATCH (person)-[:HAS_CAT]->(:Cat) + } AS hasPet +---- -*Syntax:* -[source, cypher, role=noplay] -COUNT { - MATCH [Pattern] - WHERE [Expression] +.Result +[role="queryresult",options="header,footer",cols="2* +Try this query live +(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) + +]]>(:Dog) + UNION + MATCH (person)-[:HAS_CAT]->(:Cat) + } AS hasPet +]]> +++++ +endif::nonhtmloutput[] + +[[existential-subquery-with-with]] +==== `EXISTS` subquery with `WITH` + +Variables from the outside scope are visible for the entire subquery, even when using a `WITH` clause. +This means that shadowing of these variables is not allowed. +An outside scope variable is shadowed when a newly introduced variable within the inner scope is defined with the same variable. +In the below example, a `WITH` clause introduces a new variable. +Note that the outer scope variable `person` referenced in the main query is still available after the `WITH` clause. + +.Query +[source, cypher] +---- +MATCH (person:Person) +WHERE EXISTS { + WITH "Ozzy" AS dogName + MATCH (person)-[:HAS_DOG]->(d:Dog) + WHERE d.name = dogName } +RETURN person.name AS name +---- +.Result +[role="queryresult",options="header,footer",cols="1* +Try this query live +(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) + +]]>(d:Dog) + WHERE d.name = dogName +} +RETURN person.name AS name +]]> +++++ +endif::nonhtmloutput[] + +[[existential-subquery-with-return]] +==== `EXISTS` subquery with `RETURN` -It is worth noting that the `MATCH` keyword can be omitted in such subqueries and that the `WHERE` clause is optional. +`EXISTS` subqueries do not require a `RETURN` clause at the end of the subquery. If one is present, it does not +need to be aliased, which is different compared to xref::clauses/call-subquery.adoc[`CALL` subqueries]. +Any variables returned in an `EXISTS` subquery will not be available after the subquery. + +.Query +[source, cypher] +---- +MATCH (person:Person) +WHERE EXISTS { + MATCH (person)-[:HAS_DOG]->(:Dog) + RETURN person.name +} +RETURN person.name AS name +---- + +.Result +[role="queryresult",options="header,footer",cols="1* +Try this query live +(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) + +]]>(:Dog) + RETURN person.name +} +RETURN person.name AS name +]]> +++++ +endif::nonhtmloutput[] + +[[count-subqueries]] +=== `COUNT` subqueries + +A `COUNT` subquery expression can be used to count the number of rows returned by the subquery. + +Any non-writing query is allowed. `COUNT` subqueries differ from regular queries in that the final `RETURN` clause may be omitted, +as any variable defined within the subquery will not be available outside of the expression, +even if a final `RETURN` clause is used. One exception to this is that for a `DISTINCT UNION` clause, the `RETURN` clause is still mandatory. + +It is worth noting that the `MATCH` keyword can be omitted in subqueries in cases where the `COUNT` consists of only a pattern and an optional `WHERE` clause. [[count-subquery-simple-case]] ==== Simple `COUNT` subquery -Variables introduced by the outside scope can be used in the `COUNT` subquery without importing them, -unlike the case for `CALL` subqueries, <>. +Variables introduced by the outside scope can be used in the `COUNT` subquery without importing them. +In this regard, `COUNT` subqueries are different from `CALL` subqueries, xref::clauses/call-subquery.adoc#subquery-correlated-importing[which do require importing]. The following query exemplifies this and outputs the owners of more than one dog: @@ -765,6 +891,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -813,6 +940,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -827,6 +955,124 @@ RETURN person.name AS name ++++ endif::nonhtmloutput[] +[[count-subquery-with-union]] +==== `COUNT` subquery with a `UNION` + +`COUNT` can be used with a `UNION` clause. If the `UNION` clause is distinct, the `RETURN` clause is required. +`UNION ALL` clauses do not require the `RETURN` clause. However, it is worth noting that if one branch has a `RETURN` clause, then all require one. +The below example shows the count of pets each person has by using a `UNION` clause: + +.Query +[source, cypher] +---- +MATCH (person:Person) +RETURN + person.name AS name, + COUNT { + MATCH (person)-[:HAS_DOG]->(dog:Dog) + RETURN dog.name AS petName + UNION + MATCH (person)-[:HAS_CAT]->(cat:Cat) + RETURN cat.name AS petName + } AS numPets +---- + +.Result +[role="queryresult",options="header,footer",cols="2* +Try this query live +(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) + +]]>(dog:Dog) + RETURN dog.name AS petName + UNION + MATCH (person)-[:HAS_CAT]->(cat:Cat) + RETURN cat.name AS petName + } AS numPets +]]> +++++ +endif::nonhtmloutput[] + +[[count-subquery-with-with]] +==== `COUNT` subquery with `WITH` + +Variables from the outside scope are visible for the entire subquery, even when using a `WITH` clause. +This means that shadowing of these variables is not allowed. +An outside scope variable is shadowed when a newly introduced variable within the inner scope is defined with the same variable. +In the below example, a `WITH` clause introduces a new variable. +Note that the outer scope variable `person` referenced in the main query is still available after the `WITH` clause. + +.Query +[source, cypher] +---- +MATCH (person:Person) +WHERE COUNT { + WITH "Ozzy" AS dogName + MATCH (person)-[:HAS_DOG]->(d:Dog) + WHERE d.name = dogName +} = 1 +RETURN person.name AS name +---- + +.Result +[role="queryresult",options="header,footer",cols="1* +Try this query live +(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) + +]]>(d:Dog) + WHERE d.name = dogName +} = 1 +RETURN person.name AS name +]]> +++++ +endif::nonhtmloutput[] + [[count-subqueries-other-clauses]] ==== Using `COUNT` subqueries inside other clauses @@ -842,7 +1088,7 @@ See a few examples below: ---- MATCH (person:Person) RETURN person.name, COUNT { (person)-[:HAS_DOG]->(:Dog) } as howManyDogs - + ---- .Result @@ -866,13 +1112,14 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) ]]>(:Dog) } as howManyDogs - + ]]> ++++ endif::nonhtmloutput[] @@ -887,7 +1134,7 @@ endif::nonhtmloutput[] MATCH (person:Person) WHERE person.name ="Andy" SET person.howManyDogs = COUNT { (person)-[:HAS_DOG]->(:Dog) } RETURN person.howManyDogs as howManyDogs - + ---- .Result @@ -910,6 +1157,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -917,7 +1165,7 @@ CREATE MATCH (person:Person) WHERE person.name ="Andy" SET person.howManyDogs = COUNT { (person)-[:HAS_DOG]->(:Dog) } RETURN person.howManyDogs as howManyDogs - + ]]> ++++ endif::nonhtmloutput[] @@ -935,7 +1183,7 @@ RETURN WHEN COUNT { (person)-[:HAS_DOG]->(:Dog) } > 1 THEN "Doglover " + person.name ELSE person.name END AS result - + ---- .Result @@ -959,6 +1207,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -969,7 +1218,7 @@ RETURN WHEN COUNT { (person)-[:HAS_DOG]->(:Dog) } > 1 THEN "Doglover " + person.name ELSE person.name END AS result - + ]]> ++++ endif::nonhtmloutput[] @@ -988,7 +1237,7 @@ MATCH (person:Person) RETURN COUNT { (person)-[:HAS_DOG]->(:Dog) } AS numDogs, avg(person.age) AS averageAge ORDER BY numDogs - + ---- .Result @@ -1012,6 +1261,7 @@ CREATE (timothy:Person {name: 'Timothy', age: 25}), (peter:Person {name: 'Peter', age: 35}), (andy)-[:HAS_DOG {since: 2016}]->(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), (fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), (fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) @@ -1020,11 +1270,62 @@ MATCH (person:Person) RETURN COUNT { (person)-[:HAS_DOG]->(:Dog) } AS numDogs, avg(person.age) AS averageAge ORDER BY numDogs - + ]]> ++++ endif::nonhtmloutput[] +[[count-subquery-with-return]] +==== `COUNT` subquery with `RETURN` + +`COUNT` subqueries do not require a `RETURN` clause at the end of the subquery. If one is present, it does not need to be aliased. +This is a difference compared to from xref::clauses/call-subquery.adoc[`CALL` subqueries]. +Any variables returned in a `COUNT` subquery will not be available after the subquery. + +.Query +[source, cypher] +---- +MATCH (person:Person) +WHERE COUNT { + MATCH (person)-[:HAS_DOG]->(:Dog) + RETURN person.name +} = 1 +RETURN person.name AS name +---- + +.Result +[role="queryresult",options="header,footer",cols="1* +Try this query live +(:Dog {name:'Andy'}), +(timothy)-[:HAS_CAT {since: 2019}]->(:Cat {name:'Mittens'}), +(fido:Dog {name:'Fido'})<-[:HAS_DOG {since: 2010}]-(peter)-[:HAS_DOG {since: 2018}]->(:Dog {name:'Ozzy'}), +(fido)-[:HAS_TOY]->(:Toy{name:'Banana'}) + +]]>(:Dog) + RETURN person.name +} = 1 +RETURN person.name AS name +]]> +++++ +endif::nonhtmloutput[] [[label-expressions]] == Label expressions @@ -1685,7 +1986,7 @@ RETURN n:A&B == Relationship type expressions Relationship type expressions evaluate to `true` or `false` when applied to the type of a relationship. - + Assuming no other filters are applied, then a relationship type expression evaluating to `true` means the relationship is matched. [IMPORTANT] diff --git a/modules/ROOT/pages/syntax/index.adoc b/modules/ROOT/pages/syntax/index.adoc index 4a6b2dc25..19e536fb7 100644 --- a/modules/ROOT/pages/syntax/index.adoc +++ b/modules/ROOT/pages/syntax/index.adoc @@ -8,8 +8,13 @@ * xref::syntax/expressions.adoc[Expressions] ** xref::syntax/expressions.adoc#cypher-expressions-general[Expressions in general] ** xref::syntax/expressions.adoc#cypher-expressions-string-literals[Note on string literals] + ** xref::syntax/expressions.adoc#cypher-expressions-number-literals[Note on number literals] ** xref::syntax/expressions.adoc#query-syntax-case[`CASE` Expressions] + ** xref::syntax/expressions.adoc#cypher-subquery-expressions[Subquery expressions] + *** xref::syntax/expressions.adoc#existential-subqueries[`EXISTS` subqueries] + *** xref::syntax/expressions.adoc#count-subqueries[`COUNT` subqueries] ** xref::syntax/expressions.adoc#label-expressions[Label expressions] + ** xref::syntax/expressions.adoc#relationship-type-expressions[Relationship type expressions] * xref::syntax/variables.adoc[Variables] * xref::syntax/reserved.adoc[Reserved keywords] * xref::syntax/parameters.adoc[Parameters]