From d168ac073d1b7a485bba53d6fa987099269ef9f5 Mon Sep 17 00:00:00 2001 From: Gemma Lamont Date: Thu, 17 Nov 2022 15:22:42 +0100 Subject: [PATCH 1/4] Update documentation for Exists and Count subqueries --- .../images/graph_expression_subqueries.svg | 119 ++++++ ...ions-additions-removals-compatibility.adoc | 38 ++ modules/ROOT/pages/syntax/expressions.adoc | 346 ++++++++++++++---- 3 files changed, 425 insertions(+), 78 deletions(-) create mode 100644 modules/ROOT/images/graph_expression_subqueries.svg 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/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index bb24b5b94..407f5c7fd 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -9,6 +9,44 @@ 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"] +---- +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 diff --git a/modules/ROOT/pages/syntax/expressions.adoc b/modules/ROOT/pages/syntax/expressions.adoc index fbcc73274..56c09f9f0 100644 --- a/modules/ROOT/pages/syntax/expressions.adoc +++ b/modules/ROOT/pages/syntax/expressions.adoc @@ -437,73 +437,32 @@ 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. -Moreover, it can appear in any expression position, unlike path patterns. +An `EXISTS` subquery can be used to find out if at least one row is returned by the given query. +It is more powerful than using a path pattern as it allows full queries to be executed. 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-updating query is allowed, how it differs from regular queries is 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 the case where the `EXISTS` consists of only +a Pattern and an optional `WHERE` clause. [[existential-subquery-simple-case]] ==== Simple `EXISTS` subquery @@ -518,7 +477,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 +502,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 +553,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 +609,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 +664,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,21 +677,126 @@ RETURN person.name AS name, EXISTS { ++++ endif::nonhtmloutput[] +[[existential-subquery-with-union]] +==== `EXISTS` subquery with a `UNION` +`Exists` can be used with a `UNION` clause, and the `RETURN` clauses are not required. +Here is the result that shows if at least one row is returned from the union of 2 queries. + +.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 +---- + +.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 will not go out of scope when using a `WITH`, they are visible for the entirety of the subquery, +this means that shadowing of these variables is also not allowed. +Here is an example where a `WITH` clause introduces a new variable, and the outer scope variable person being referenced is still available after: + +.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[] + [[count-subqueries]] === `COUNT` subqueries A `COUNT` subquery expression can be used to count the number of results of the subquery. -*Syntax:* -[source, cypher, role=noplay] -COUNT { - MATCH [Pattern] - WHERE [Expression] -} - +Any non-updating query is allowed, how it differs from regular queries is 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 `DISTINCT UNIONS` the `RETURN` is still mandatory. -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 the case where the `COUNT` consists of only a Pattern and an optional `WHERE` clause. [[count-subquery-simple-case]] ==== Simple `COUNT` subquery @@ -765,6 +833,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 +882,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 +897,122 @@ 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. +Here is the result that shows the count of pets each person has by using a `UNION`: + +.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 will not go out of scope when using a `WITH`, +they are visible for the entirety of the subquery, this means that shadowing of these variables is also not allowed. +Here is an example where a `WITH` clause introduces a new variable, +and the outer scope variable person being referenced is still available after: + +.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 +1028,7 @@ See a few examples below: ---- MATCH (person:Person) RETURN person.name, COUNT { (person)-[:HAS_DOG]->(:Dog) } as howManyDogs - + ---- .Result @@ -866,13 +1052,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 +1074,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 +1097,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 +1105,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 +1123,7 @@ RETURN WHEN COUNT { (person)-[:HAS_DOG]->(:Dog) } > 1 THEN "Doglover " + person.name ELSE person.name END AS result - + ---- .Result @@ -959,6 +1147,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 +1158,7 @@ RETURN WHEN COUNT { (person)-[:HAS_DOG]->(:Dog) } > 1 THEN "Doglover " + person.name ELSE person.name END AS result - + ]]> ++++ endif::nonhtmloutput[] @@ -988,7 +1177,7 @@ MATCH (person:Person) RETURN COUNT { (person)-[:HAS_DOG]->(:Dog) } AS numDogs, avg(person.age) AS averageAge ORDER BY numDogs - + ---- .Result @@ -1012,6 +1201,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,7 +1210,7 @@ MATCH (person:Person) RETURN COUNT { (person)-[:HAS_DOG]->(:Dog) } AS numDogs, avg(person.age) AS averageAge ORDER BY numDogs - + ]]> ++++ endif::nonhtmloutput[] @@ -1685,7 +1875,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] From c43af34c8df5756ab4fb998ec9c7590d608e80e0 Mon Sep 17 00:00:00 2001 From: Gemma Lamont Date: Fri, 18 Nov 2022 11:36:17 +0100 Subject: [PATCH 2/4] PR Review updates --- modules/ROOT/pages/syntax/expressions.adoc | 129 +++++++++++++++++++-- modules/ROOT/pages/syntax/index.adoc | 3 + 2 files changed, 122 insertions(+), 10 deletions(-) diff --git a/modules/ROOT/pages/syntax/expressions.adoc b/modules/ROOT/pages/syntax/expressions.adoc index 56c09f9f0..0eebfdd24 100644 --- a/modules/ROOT/pages/syntax/expressions.adoc +++ b/modules/ROOT/pages/syntax/expressions.adoc @@ -454,21 +454,21 @@ image:graph_expression_subqueries.svg[] === `EXISTS` subqueries An `EXISTS` subquery can be used to find out if at least one row is returned by the given query. -It is more powerful than using a path pattern as it allows full queries to be executed. +It is more powerful than using a xref::clauses/where.adoc#filter-on-patterns[path pattern] as it allows full queries to be executed. 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-updating query is allowed, how it differs from regular queries is that the final `RETURN` clause may be omitted +Any non-writing query is allowed, how it differs from regular queries is 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. It is worth noting that the `MATCH` keyword can be omitted in subqueries in the case where the `EXISTS` consists of only -a Pattern and an optional `WHERE` clause. +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, <>. +unlike the case for `CALL` subqueries, xref::clauses/call-subquery.adoc#subquery-correlated-importing[where they require importing]. The following example shows this: @@ -679,7 +679,9 @@ endif::nonhtmloutput[] [[existential-subquery-with-union]] ==== `EXISTS` subquery with a `UNION` + `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. Here is the result that shows if at least one row is returned from the union of 2 queries. .Query @@ -735,8 +737,10 @@ endif::nonhtmloutput[] [[existential-subquery-with-with]] ==== `EXISTS` subquery with `WITH` + Variables from the outside scope will not go out of scope when using a `WITH`, they are visible for the entirety of the subquery, this means that shadowing of these variables is also not allowed. +Shadowing of an outside scope variable is when a newly introduced variable inside the inner scope is defined with the same name as one from the outside scope. Here is an example where a `WITH` clause introduces a new variable, and the outer scope variable person being referenced is still available after: .Query @@ -786,23 +790,75 @@ RETURN person.name AS name ++++ endif::nonhtmloutput[] +[[existential-subquery-with-return]] +==== `EXISTS` subquery with `RETURN` + +`EXISTS` subqueries do not require a `RETURN` clause at the end of the subquery, but if one is present, it does not +need to be aliased, a difference from 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 results of the subquery. - +A `COUNT` subquery expression can be used to count the number of rows returned by the subquery. -Any non-updating query is allowed, how it differs from regular queries is that the final `RETURN` clause may be omitted +Any non-writing query is allowed, how it differs from regular queries is 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 `DISTINCT UNIONS` the `RETURN` is still mandatory. +even if a final `RETURN` clause is used. One exception to this is that for a `DISTINCT UNION` the `RETURN` is still mandatory. -It is worth noting that the `MATCH` keyword can be omitted in subqueries in the case where the `COUNT` consists of only a Pattern and an optional `WHERE` clause. +It is worth noting that the `MATCH` keyword can be omitted in subqueries in the case 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, <>. +unlike the case for `CALL` subqueries, xref::clauses/call-subquery.adoc#subquery-correlated-importing[where they require importing]. The following query exemplifies this and outputs the owners of more than one dog: @@ -901,6 +957,7 @@ endif::nonhtmloutput[] ==== `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. Here is the result that shows the count of pets each person has by using a `UNION`: .Query @@ -963,6 +1020,7 @@ endif::nonhtmloutput[] Variables from the outside scope will not go out of scope when using a `WITH`, they are visible for the entirety of the subquery, this means that shadowing of these variables is also not allowed. +Shadowing of an outside scope variable is when a newly introduced variable inside the inner scope is defined with the same name as one from the outside scope. Here is an example where a `WITH` clause introduces a new variable, and the outer scope variable person being referenced is still available after: @@ -1215,6 +1273,57 @@ RETURN COUNT { (person)-[:HAS_DOG]->(:Dog) } AS 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, but if one is present, it does not +need to be aliased, a difference 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 diff --git a/modules/ROOT/pages/syntax/index.adoc b/modules/ROOT/pages/syntax/index.adoc index 4a6b2dc25..e0a0e5255 100644 --- a/modules/ROOT/pages/syntax/index.adoc +++ b/modules/ROOT/pages/syntax/index.adoc @@ -8,8 +8,11 @@ * 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#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] From 66d5e40e6dd74bed0b50752ecd73aaf4671516ff Mon Sep 17 00:00:00 2001 From: Gemma Lamont Date: Mon, 21 Nov 2022 09:53:12 +0100 Subject: [PATCH 3/4] Add COUNT and EXISTS as submenus in menu --- modules/ROOT/pages/syntax/index.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/ROOT/pages/syntax/index.adoc b/modules/ROOT/pages/syntax/index.adoc index e0a0e5255..19e536fb7 100644 --- a/modules/ROOT/pages/syntax/index.adoc +++ b/modules/ROOT/pages/syntax/index.adoc @@ -11,6 +11,8 @@ ** 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] From 34fd359c6a29c6b6c92f36fecb94267cc0c01826 Mon Sep 17 00:00:00 2001 From: Gemma Lamont Date: Fri, 25 Nov 2022 10:32:14 +0100 Subject: [PATCH 4/4] PR review updates --- modules/ROOT/pages/syntax/expressions.adoc | 53 +++++++++++----------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/modules/ROOT/pages/syntax/expressions.adoc b/modules/ROOT/pages/syntax/expressions.adoc index 0eebfdd24..5ca7b3329 100644 --- a/modules/ROOT/pages/syntax/expressions.adoc +++ b/modules/ROOT/pages/syntax/expressions.adoc @@ -458,17 +458,17 @@ It is more powerful than using a xref::clauses/where.adoc#filter-on-patterns[pat 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, how it differs from regular queries is that the final `RETURN` clause may be omitted +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. -It is worth noting that the `MATCH` keyword can be omitted in subqueries in the case where the `EXISTS` consists of only +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, xref::clauses/call-subquery.adoc#subquery-correlated-importing[where they require importing]. +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: @@ -682,7 +682,7 @@ endif::nonhtmloutput[] `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. -Here is the result that shows if at least one row is returned from the union of 2 queries. +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] @@ -738,10 +738,11 @@ endif::nonhtmloutput[] [[existential-subquery-with-with]] ==== `EXISTS` subquery with `WITH` -Variables from the outside scope will not go out of scope when using a `WITH`, they are visible for the entirety of the subquery, -this means that shadowing of these variables is also not allowed. -Shadowing of an outside scope variable is when a newly introduced variable inside the inner scope is defined with the same name as one from the outside scope. -Here is an example where a `WITH` clause introduces a new variable, and the outer scope variable person being referenced is still available after: +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] @@ -793,8 +794,8 @@ endif::nonhtmloutput[] [[existential-subquery-with-return]] ==== `EXISTS` subquery with `RETURN` -`EXISTS` subqueries do not require a `RETURN` clause at the end of the subquery, but if one is present, it does not -need to be aliased, a difference from xref::clauses/call-subquery.adoc[`CALL` subqueries]. +`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 @@ -848,17 +849,17 @@ endif::nonhtmloutput[] A `COUNT` subquery expression can be used to count the number of rows returned by the subquery. -Any non-writing query is allowed, how it differs from regular queries is that the final `RETURN` clause may be omitted +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` the `RETURN` is still mandatory. +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 the case where the `COUNT` consists of only a pattern and an optional `WHERE` clause. +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, xref::clauses/call-subquery.adoc#subquery-correlated-importing[where they require importing]. +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: @@ -956,9 +957,9 @@ 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. -Here is the result that shows the count of pets each person has by using 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] @@ -1018,11 +1019,11 @@ endif::nonhtmloutput[] [[count-subquery-with-with]] ==== `COUNT` subquery with `WITH` -Variables from the outside scope will not go out of scope when using a `WITH`, -they are visible for the entirety of the subquery, this means that shadowing of these variables is also not allowed. -Shadowing of an outside scope variable is when a newly introduced variable inside the inner scope is defined with the same name as one from the outside scope. -Here is an example where a `WITH` clause introduces a new variable, -and the outer scope variable person being referenced is still available after: +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] @@ -1276,8 +1277,8 @@ endif::nonhtmloutput[] [[count-subquery-with-return]] ==== `COUNT` subquery with `RETURN` -`COUNT` subqueries do not require a `RETURN` clause at the end of the subquery, but if one is present, it does not -need to be aliased, a difference from xref::clauses/call-subquery.adoc[`CALL` subqueries]. +`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