From 5accc0b2f3438f08dccdbe18863bdc9fe6c4b8f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 17 Aug 2023 13:54:45 +0200 Subject: [PATCH 01/24] initial rearchitecture --- modules/ROOT/content-nav.adoc | 8 +- modules/ROOT/pages/expressions/case.adoc | 274 ++ modules/ROOT/pages/expressions/index.adoc | 80 + modules/ROOT/pages/expressions/label.adoc | 534 ++++ .../ROOT/pages/expressions/relationship.adoc | 337 +++ modules/ROOT/pages/expressions/subquery.adoc | 819 ++++++ .../pages/expressions/type-predicate.adoc | 298 +++ modules/ROOT/pages/syntax/expressions.adoc | 2349 ----------------- modules/ROOT/pages/syntax/index.adoc | 1 - 9 files changed, 2349 insertions(+), 2351 deletions(-) create mode 100644 modules/ROOT/pages/expressions/case.adoc create mode 100644 modules/ROOT/pages/expressions/index.adoc create mode 100644 modules/ROOT/pages/expressions/label.adoc create mode 100644 modules/ROOT/pages/expressions/relationship.adoc create mode 100644 modules/ROOT/pages/expressions/subquery.adoc create mode 100644 modules/ROOT/pages/expressions/type-predicate.adoc delete mode 100644 modules/ROOT/pages/syntax/expressions.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index 84a44640b..c6308ff83 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -7,7 +7,6 @@ * xref:syntax/index.adoc[] ** xref:syntax/parsing.adoc[] ** xref:syntax/naming.adoc[] -** xref:syntax/expressions.adoc[] ** xref:syntax/variables.adoc[] ** xref:syntax/reserved.adoc[] ** xref:syntax/parameters.adoc[] @@ -58,6 +57,13 @@ ** xref:values-and-types/maps.adoc[] ** xref:values-and-types/casting-data.adoc[] +* xref:expressions/index.adoc[] +** xref:expressions/case.adoc[] +** xref:expressions/subquery.adoc[] +** xref:expressions/type-predicate.adoc[] +** xref:expressions/label.adoc[] +** xref:expressions/relationship.adoc[] + * xref:functions/index.adoc[] ** xref:functions/predicate.adoc[] diff --git a/modules/ROOT/pages/expressions/case.adoc b/modules/ROOT/pages/expressions/case.adoc new file mode 100644 index 000000000..36e43a211 --- /dev/null +++ b/modules/ROOT/pages/expressions/case.adoc @@ -0,0 +1,274 @@ +[[expressions-case]] += `CASE` expressions + +Generic conditional expressions may be expressed using the `CASE` construct. +Two variants of `CASE` exist within Cypher: the simple form, which allows an expression to be compared against multiple values, and the generic form, which allows multiple conditional statements to be expressed. + +[NOTE] +==== +`CASE` can only be used as part of `RETURN` or `WITH` if you want to use the result in the succeeding clause or statement. +==== + +[[expressions-case-example]] +== Example graph + +The following graph is used for the examples below: + +//// +[source, cypher, role=test-setup] +---- +CREATE + (alice:A {name:'Alice', age: 38, eyes: 'brown'}), + (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), + (charlie:C {name: 'Charlie', age: 53, eyes: 'green'}), + (daniel:D {name: 'Daniel', eyes: 'brown'}), + (eskil:E {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}), + (alice)-[:KNOWS]->(bob), + (alice)-[:KNOWS]->(charlie), + (bob)-[:KNOWS]->(daniel), + (charlie)-[:KNOWS]->(daniel), + (bob)-[:MARRIED]->(eskil) +---- +//// + +image:graph3.svg[] + +[[expressions-case-simple]] +== Simple `CASE` + +The simple `CASE` form is used to compare an expression against multiple values. +The expression is calculated, and compared in order with the `WHEN` clauses until a match is found. +If no match is found, the expression in the `ELSE` clause is returned. +However, if there is no `ELSE` case and no match is found, `null` will be returned. + + +[source, syntax] +---- +CASE test + WHEN value THEN result + [WHEN ...] + [ELSE default] +END +---- + + +*Arguments:* +[options="header"] +|=== +| Name | Description + +| `test` +| A valid expression. + +| `value` +| An expression whose result will be compared to `test`. + +| `result` +| This is the expression returned as output if `value` matches `test`. + +| `default` +| If no match is found, `default` is returned. +|=== + + +[source, cypher] +---- +MATCH (n) +RETURN +CASE n.eyes + WHEN 'blue' THEN 1 + WHEN 'brown' THEN 2 + ELSE 3 +END AS result +---- + +[role="queryresult",options="header,footer",cols="1*+ +| +"Eskil"+ | +31+ +2+d|Rows: 5 +|=== + +The corrected query, behaving as expected, is given by the following generic `CASE` form: + +[source, cypher] +---- +MATCH (n) +RETURN n.name, +CASE + WHEN n.age IS NULL THEN -1 + ELSE n.age - 10 +END AS age_10_years_ago +---- + +We now see that the `age_10_years_ago` correctly returns `-1` for the node named `Daniel`. + +[role="queryresult",options="header,footer",cols="2*+ +| +"Eskil"+ | +31+ +2+d|Rows: 5 +|=== + +====== diff --git a/modules/ROOT/pages/expressions/index.adoc b/modules/ROOT/pages/expressions/index.adoc new file mode 100644 index 000000000..e1dc00b5d --- /dev/null +++ b/modules/ROOT/pages/expressions/index.adoc @@ -0,0 +1,80 @@ +[[expressions]] += Expressions + +Cypher includes a several different types of expressions. +More information about each of these can be found in the following sections: + +* xref:expressions/case.adoc[] +* xref:expressions/subquery.adoc[] +* xref:expressions/type-predicate.adoc[] +* xref:expressions/label.adoc[] +* xref:expressions/relationship.adoc[] + +[[expressions-overview]] +== General overview + +An expression in Cypher can be: + +* A decimal (integer or float) literal: `13`, `-40000`, `3.14`. +* A decimal (integer or float) literal in scientific notation: `6.022E23`. +* A hexadecimal integer literal (starting with `0x`): `0x13af`, `0xFC3A9`, `-0x66eff`. +* An octal integer literal (starting with `0o`): `0o1372`, `-0o5671`. +* A string literal: `'Hello'`, `"World"`. +* A float literal: `Inf`, `Infinity`, `NaN` +* A boolean literal: `true`, `false`. +* A variable: `n`, `x`, `rel`, `myFancyVariable`, `++`A name with weird stuff in it[]!`++`. +* A property: `n.prop`, `x.prop`, `rel.thisProperty`, `++myFancyVariable.`(weird property name)`++`. +* A dynamic property: `n["prop"]`, `rel[n.city + n.zip]`, `map[coll[0]]`. +* A parameter: `$param`, `$0`. +* A list of expressions: `['a', 'b']`, `[1, 2, 3]`, `['a', 2, n.property, $param]`, `[]`. +* A function call: `length(p)`, `nodes(p)`. +* An aggregate function: `avg(x.prop)`, `+count(*)+`. +* A path-pattern: `+(a)-[r]->(b)+`, `+(a)-[r]-(b)+`, `+(a)--(b)+`, `+(a)-->()<--(b)+`. +* An operator application: `1 + 2`, `3 < 4`. +* A predicate expression is an expression that returns `true` or `false`: `a.prop = 'Hello'`, `length(p) > 10`, `a.name IS NOT NULL`. +* A special case of predicates are label and relationship type expressions: `(n:A|B)`, `()-[r:R1|R2]->()`. +* A subquery expression. For example: +`EXISTS { + MATCH (n)-[r]->(p) + WHERE p.name = 'Sven' +}`. +* A regular expression: `a.name =~ 'Tim.*'`. +* A case-sensitive string matching expression: `a.surname STARTS WITH 'Sven'`, `a.surname ENDS WITH 'son'` or `a.surname CONTAINS 'son'`. +* A `CASE` expression. + +[NOTE] +==== +Most expressions in Cypher evaluate to `null` if any of their inner expressions are `null`. +Notable exceptions are the operators `IS NULL`, `IS NOT NULL`, and the type predicate expression. +==== + +[[expressions-string-literals]] +== Note on string literals + +String literals can contain the following escape sequences: + +[options="header", cols=">1,<2"] +|=================== +|Escape sequence|Character +|`\t`|Tab +|`\b`|Backspace +|`\n`|Newline +|`\r`|Carriage return +|`\f`|Form feed +|`\'`|Single quote +|`\"`|Double quote +|`\\`|Backslash +|`\uxxxx`|Unicode UTF-16 code point (4 hex digits must follow the `\u`) +|=================== + +[NOTE] +==== +Using regular expressions with unsanitized user input makes you vulnerable to Cypher injection. +Consider using xref:syntax/parameters.adoc[parameters] instead. +==== + +[[expressions-number-literals]] +== Note on number literals + +Any number literal may contain an underscore `_` between digits. +There may be an underscore between the `0x` or `0o` and the digits for hexadecimal and octal literals. diff --git a/modules/ROOT/pages/expressions/label.adoc b/modules/ROOT/pages/expressions/label.adoc new file mode 100644 index 000000000..f9900170d --- /dev/null +++ b/modules/ROOT/pages/expressions/label.adoc @@ -0,0 +1,534 @@ +[[label-expressions]] += Label expressions + +In earlier versions of Neo4j, label expressions for nodes had a single colon operator that represented the `AND` operator. +With the release of version 5.0, a new label expression with an extended set of logical operators is being introduced, in addition to the single colon operator. +It is important to note that you cannot mix these different types of label expression syntax. +For more information, see <>. + +Label expressions evaluate to `true` or `false` when applied to the set of labels for a node. + +Assuming no other filters are applied, then a label expression evaluating to `true` means the node is matched. + +The following table displays whether the label expression matches the relationship: + +.Label expression matches +[cols="^3,^2,^2,^2,^2,^2,^2,^2,^2"] +|=== +| +8+^|*Node* + +|*Label expression* | `()` | `(:A)` | `(:B)` | `(:C)` | `(:A:B)` | `(:A:C)` | `(:B:C)` | `(:A:B:C)` +| `()` +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} + +| `(:A)` +| +| {check-mark} +| +| +| {check-mark} +| {check-mark} +| +| {check-mark} + +| `(:A&B)` +| +| +| +| +| {check-mark} +| +| +| {check-mark} + +| `(:A\|B)` +| +| {check-mark} +| {check-mark} +| +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} + +| `(:!A)` +| {check-mark} +| +| {check-mark} +| {check-mark} +| +| +| {check-mark} +| + +| `(:!!A)` +| +| {check-mark} +| +| +| {check-mark} +| {check-mark} +| +| {check-mark} + +| `(:A&!A)` +| +| +| +| +| +| +| +| + +| `(:A\|!A)` +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} + +| `(:%)` +| +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} + +| `(:!%)` +| {check-mark} +| +| +| +| +| +| +| + +| `(:%\|!%)` +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} + +| `(:%&!%)` +| +| +| +| +| +| +| +| + +| `(:A&%)` +| +| {check-mark} +| +| +| {check-mark} +| {check-mark} +| +| {check-mark} + +| `(:A\|%)` +| +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} +| {check-mark} + +| `(:(A&B)&!(B&C))` +| +| +| +| +| {check-mark} +| +| +| + +| `(:!(A&%)&%)` +| +| +| {check-mark} +| {check-mark} +| +| +| {check-mark} +| + +|=== + + +[[syntax-restrictions-label]] +== Restrictions on using the different types of label expression syntax + +Neo4j version 5.0 introduced an ampersand operator, which is equivalent to the colon conjunction operator. +Mixing the colon conjunction operator with any of the new label expression operators in the same clause will raise a syntax error. + +For example, each of the following clauses will raise syntax errors: + +* `MATCH (n:A|B:C)` +* `MATCH (n:A:B)-[]-(m:(A&B)|C)` +* `MATCH (n:A:B)--(m), (n)-->(o:(A&B)|C)` +* `RETURN n:A&B, n:A:B` +* `MATCH (n:A:B)-[]-(m) WHERE m:(A&B)|C` + +In earlier versions of Neo4j (version 4.4 and earlier), relationship type expressions only had the pipe operator. +As the pipe operator will continue to act as an `OR` operator, it can continue to be used alongside the new operators. + +To make it easier to use the new syntax when extending existing queries, using the different syntax types in separate clauses will be supported. + +For example, the following query will not raise a syntax error: + +[source, cypher, role=noplay] +---- +MATCH (m:A:B:C)-[]->() +MATCH (n:(A&B)|C)-[]->(m) +RETURN m,n +---- + +Queries that exclusively use syntax from earlier versions of Neo4j (version 4.4 and earlier) will continue to be supported. + +For example, the following will not raise a syntax error: + +[source, cypher, role=noplay] +---- +MATCH (m:A:B:C)-[:S|T]->() +RETURN + CASE + WHEN m:D:E THEN m.p + ELSE null + END AS result +---- + +[[label-expressions-examples]] +=== Examples + +The following graph is used for the examples below: + +//// +[source, cypher, role=test-setup] +---- +MATCH (n:Toy|Cat|Dog|Person|Swedish) DETACH DELETE n; + +CREATE + (:A {name:'Alice'}), + (:B {name:'Bob'}), + (:C {name:'Charlie'}), + (:A:B {name:'Daniel'}), + (:A:C {name:'Eskil'}), + (:B:C {name:'Frank'}), + (:A:B:C {name:'George'}), + ({name:'Henry'}) +---- +//// + +image:graph_label_expressions.svg[] + +* xref:syntax/expressions.adoc#label-expressions-node-pattern-without-label-expressions[] +* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-single-node-label[] +* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-and-expression[] +* xref:syntax/expressions.adoc#label-expressions-note-pattern-with-or-expression[] +* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-not-expressions[] +* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-wildcard-expression[] +* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-nested-label-expressions[] +* xref:syntax/expressions.adoc#label-expressions-where-clause-with-label-expression-as-predicate[] +* xref:syntax/expressions.adoc#label-expressions-with-return-clauses[] + + +[discrete] +[[label-expressions-node-pattern-without-label-expressions]] +=== Node pattern without label expressions + +A node pattern without a label expression returns all nodes in the graph, including nodes without labels. + +.+Label expression+ +====== + +[source, cypher] +---- +MATCH (n) +RETURN n.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(:B), + (:C)-[:R2 {name:'Studies'}]->(:D), + (:E)-[:R3 {name:'Parents'}]->(:F) +---- +//// + +[[relationship-type-expressions-pattern-without-relationship-type-expression]] +== Relationship pattern without relationship type expression + +A relationship pattern without a relationship type expression returns all relationships in the graph. + + +.Relationship type expressions +====== + +[source, cypher] +---- +MATCH ()-[r]->() +RETURN r.name as name +---- + +[role="queryresult",options="header,footer",cols="1*() +RETURN r.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*() +RETURN r.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*() +RETURN r.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*() +RETURN r.name as name +---- + +[role="queryresult",options="header,footer",cols="1*(m) +WHERE r:R1|R2 +RETURN r.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(m) +RETURN r:R1|R2 AS result +---- + +[role="queryresult",options="header,footer",cols="1*(m) +RETURN +CASE + WHEN n:A&B THEN 1 + WHEN r:!R1&!R2 THEN 2 + ELSE -1 +END AS result +---- + +[role="queryresult",options="header,footer",cols="1*(: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]] +== `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 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. + +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-simple]] +=== Simple `EXISTS` subquery + +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: + + +[source, cypher] +---- +MATCH (person:Person) +WHERE EXISTS { + (person)-[:HAS_DOG]->(:Dog) +} +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(dog:Dog) + WHERE person.name = dog.name +} +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(dog:Dog) + WHERE EXISTS { + MATCH (dog)-[:HAS_TOY]->(toy:Toy) + WHERE toy.name = 'Banana' + } +} +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(:Dog) +} AS hasDog +---- + +[role="queryresult",options="header,footer",cols="2*(:Dog) + UNION + MATCH (person)-[:HAS_CAT]->(:Cat) + } AS hasPet +---- + +[role="queryresult",options="header,footer",cols="2*(d:Dog) + WHERE d.name = name +} +RETURN person.name AS name +---- + +.Error message +[source, output, role="noheader"] +---- +The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 90)) +---- + +New variables can be introduced into the subquery, as long as they use a different identifier. +In the example below, 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. + +[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 +---- + +[role="queryresult",options="header,footer",cols="1*(:Dog) + RETURN person.name +} +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(:Dog) } > 1 +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(dog:Dog) + WHERE person.name = dog.name +} = 1 +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(dog:Dog) + RETURN dog.name AS petName + UNION + MATCH (person)-[:HAS_CAT]->(cat:Cat) + RETURN cat.name AS petName + } AS numPets +---- + +[role="queryresult",options="header,footer",cols="2*(d:Dog) + WHERE d.name = name +} = 1 +RETURN person.name AS name +---- + +.Error message +[source, output, role="noheader"] +---- +The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 90)) +---- + +New variables can be introduced into the subquery, as long as they use a different identifier. +In the example below, 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. + +[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 +---- + +[role="queryresult",options="header,footer",cols="1*(:Dog) } as howManyDogs + +---- + +[role="queryresult",options="header,footer",cols="2*(:Dog) } +RETURN person.howManyDogs as howManyDogs + +---- + +[role="queryresult",options="header,footer",cols="1*(:Dog) } > 1 THEN "Doglover " + person.name + ELSE person.name + END AS result + +---- + +[role="queryresult",options="header,footer",cols="1*(:Dog) } AS numDogs, + avg(person.age) AS averageAge + ORDER BY numDogs + +---- + +[role="queryresult",options="header,footer",cols="2*(:Dog) + RETURN person.name +} = 1 +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(dog:Dog) RETURN dog.name } +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(dog:Dog) + WHERE r.since > 2017 + RETURN dog.name +} as youngDogs +---- + +[role="queryresult",options="header,footer",cols="2*(dog:Dog) + RETURN dog.name AS petName + UNION + MATCH (person)-[:HAS_CAT]->(cat:Cat) + RETURN cat.name AS petName + } AS petNames +---- + +[role="queryresult",options="header,footer",cols="2*(d:Dog {name: name}) + RETURN d.name +} as dogsOfTheYear +---- + +.Error message +[source, output, role="noheader"] +---- +The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 92)) +---- + +New variables can be introduced into the subquery, as long as they use a different identifier. +In the example below, 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. + +[source, cypher] +---- +MATCH (person:Person) +RETURN person.name AS name, COLLECT { + WITH 2018 AS yearOfTheDog + MATCH (person)-[r:HAS_DOG]->(d:Dog) + WHERE r.since = yearOfTheDog + RETURN d.name +} as dogsOfTheYear +---- + +[role="queryresult",options="header,footer",cols="2*(d:Dog) + MATCH (d)-[:HAS_TOY]->(t:Toy) + RETURN t.name + } as toyNames +---- + +[role="queryresult",options="header,footer",cols="2*(d:Dog) RETURN d.name } +RETURN person.dogNames as dogNames +---- + +[role="queryresult",options="header,footer",cols="1*(d:Dog) RETURN d.name } = [] THEN "No Dogs " + person.name + ELSE person.name + END AS result +---- + +[role="queryresult",options="header,footer",cols="1*(d:Dog) RETURN d.name } AS dogNames, + avg(person.age) AS averageAge + ORDER BY dogNames +---- + +[role="queryresult",options="header,footer",cols="2* IS :: +---- + +Where `` is any Cypher expression and `` is a Cypher Type. +For all available Cypher types, see the section on xref::values-and-types/property-structural-constructed.adoc#types-synonyms[types and their synonyms]. + +[source, cypher] +---- +UNWIND [42, true, 'abc'] AS val +RETURN val, val IS :: INTEGER AS isInteger +---- + +[role="queryresult",options="header,footer",cols="2* 18 +RETURN n.name AS name, n.age AS age +---- + +[role="queryresult",options="header,footer",cols="2* IS TYPED +---- + +[source, syntax, role="noheader", indent=0] +---- + :: +---- + +For verifying that an expression is not of a certain type, the following alternative syntax is supported: + +[source, syntax, role="noheader", indent=0] +---- + IS NOT TYPED +---- + +[[type-predicate-any-and-nothing]] +== Use of `ANY` and `NOTHING` types + +_This feature was introduced in Neo4j 5.10._ + +`ANY` is a supertype which matches values of all types. +`NOTHING` is a type containing an empty set of values. +This means that it returns `false` for all values. + +[source, cypher] +---- +RETURN 42 IS :: ANY AS isOfTypeAny, 42 IS :: NOTHING AS isOfTypeNothing +---- + +[role="queryresult",options="header,footer",cols="2* AS isIntList +---- + +[role="queryresult",options="header,footer",cols="2* AS isNothingList, + [] IS :: LIST AS isIntList, + [] IS :: LIST AS isFloatNotNullList +---- + +[role="queryresult",options="header,footer",cols="3* as isMixedList +---- + +[role="queryresult",options="header,footer",cols="1*(b)+`, `+(a)-[r]-(b)+`, `+(a)--(b)+`, `+(a)-->()<--(b)+`. -* An operator application: `1 + 2`, `3 < 4`. -* A predicate expression is an expression that returns `true` or `false`: `a.prop = 'Hello'`, `length(p) > 10`, `a.name IS NOT NULL`. -* A special case of predicates are label and relationship type expressions: `(n:A|B)`, `()-[r:R1|R2]->()`. -* A subquery expression. For example: -`EXISTS { - MATCH (n)-[r]->(p) - WHERE p.name = 'Sven' -}`. -* A regular expression: `a.name =~ 'Tim.*'`. -* A case-sensitive string matching expression: `a.surname STARTS WITH 'Sven'`, `a.surname ENDS WITH 'son'` or `a.surname CONTAINS 'son'`. -* A `CASE` expression. - - -[[cypher-expressions-string-literals]] -== Note on string literals - -String literals can contain the following escape sequences: - -[options="header", cols=">1,<2"] -|=================== -|Escape sequence|Character -|`\t`|Tab -|`\b`|Backspace -|`\n`|Newline -|`\r`|Carriage return -|`\f`|Form feed -|`\'`|Single quote -|`\"`|Double quote -|`\\`|Backslash -|`\uxxxx`|Unicode UTF-16 code point (4 hex digits must follow the `\u`) -|=================== - -[NOTE] -==== -Using regular expressions with unsanitized user input makes you vulnerable to Cypher injection. -Consider using xref:syntax/parameters.adoc[parameters] instead. -==== - -[[cypher-expressions-number-literals]] -== Note on number literals - -Any number literal may contain an underscore `_` between digits. -There may be an underscore between the `0x` or `0o` and the digits for hexadecimal and octal literals. - -[[query-syntax-case]] -== `CASE` expressions - -Generic conditional expressions may be expressed using the `CASE` construct. -Two variants of `CASE` exist within Cypher: the simple form, which allows an expression to be compared against multiple values, and the generic form, which allows multiple conditional statements to be expressed. - -[NOTE] -==== -CASE can only be used as part of RETURN or WITH if you want to use the result in the succeeding clause or statement. -==== - -The following graph is used for the examples below: - -//// -[source, cypher, role=test-setup] ----- -CREATE - (alice:A {name:'Alice', age: 38, eyes: 'brown'}), - (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), - (charlie:C {name: 'Charlie', age: 53, eyes: 'green'}), - (daniel:D {name: 'Daniel', eyes: 'brown'}), - (eskil:E {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}), - (alice)-[:KNOWS]->(bob), - (alice)-[:KNOWS]->(charlie), - (bob)-[:KNOWS]->(daniel), - (charlie)-[:KNOWS]->(daniel), - (bob)-[:MARRIED]->(eskil) ----- -//// - -image:graph3.svg[] - -[[syntax-simple-case]] -=== Simple `CASE` form: comparing an expression against multiple values - -The expression is calculated, and compared in order with the `WHEN` clauses until a match is found. -If no match is found, the expression in the `ELSE` clause is returned. -However, if there is no `ELSE` case and no match is found, `null` will be returned. - - -[source, syntax] ----- -CASE test - WHEN value THEN result - [WHEN ...] - [ELSE default] -END ----- - - -*Arguments:* -[options="header"] -|=== -| Name | Description - -| `test` -| A valid expression. - -| `value` -| An expression whose result will be compared to `test`. - -| `result` -| This is the expression returned as output if `value` matches `test`. - -| `default` -| If no match is found, `default` is returned. -|=== - - -[source, cypher] ----- -MATCH (n) -RETURN -CASE n.eyes - WHEN 'blue' THEN 1 - WHEN 'brown' THEN 2 - ELSE 3 -END AS result ----- - -[role="queryresult",options="header,footer",cols="1*+ -| +"Eskil"+ | +31+ -2+d|Rows: 5 -|=== - -The corrected query, behaving as expected, is given by the following generic `CASE` form: - -[source, cypher] ----- -MATCH (n) -RETURN n.name, -CASE - WHEN n.age IS NULL THEN -1 - ELSE n.age - 10 -END AS age_10_years_ago ----- - -We now see that the `age_10_years_ago` correctly returns `-1` for the node named `Daniel`. - -[role="queryresult",options="header,footer",cols="2*+ -| +"Eskil"+ | +31+ -2+d|Rows: 5 -|=== - -====== - - -[[cypher-subquery-expressions]] -== Subquery expressions - -Subquery expressions can appear anywhere that an expression is valid. -A subquery has a scope, as indicated by the opening and closing braces, `{` and `}`. -Any variable that is defined in the outside scope can be referenced inside the subquery's own scope. -Variables introduced inside the subquery are not part of the outside scope and therefore can't be accessed on the outside. - - -The following graph is used for the examples below: - -//// -[source, cypher, role=test-setup] ----- -MATCH (n:A|B|C|D|E) DETACH DELETE n; -CREATE -(andy:Swedish:Person {name: 'Andy', age: 36}), -(timothy:Person {name: 'Timothy', nickname: 'Tim', age: 25}), -(peter:Person {name: 'Peter', nickname: 'Pete', 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 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. - -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. -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: - - -[source, cypher] ----- -MATCH (person:Person) -WHERE EXISTS { - (person)-[:HAS_DOG]->(:Dog) -} -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(dog:Dog) - WHERE person.name = dog.name -} -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(dog:Dog) - WHERE EXISTS { - MATCH (dog)-[:HAS_TOY]->(toy:Toy) - WHERE toy.name = 'Banana' - } -} -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(:Dog) -} AS hasDog ----- - -[role="queryresult",options="header,footer",cols="2*(:Dog) - UNION - MATCH (person)-[:HAS_CAT]->(:Cat) - } AS hasPet ----- - -[role="queryresult",options="header,footer",cols="2*(d:Dog) - WHERE d.name = name -} -RETURN person.name AS name ----- - -.Error message -[source, output, role="noheader"] ----- -The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 90)) ----- - -New variables can be introduced into the subquery, as long as they use a different identifier. -In the example below, 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. - -[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 ----- - -[role="queryresult",options="header,footer",cols="1*(:Dog) - RETURN person.name -} -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(:Dog) } > 1 -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(dog:Dog) - WHERE person.name = dog.name -} = 1 -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(dog:Dog) - RETURN dog.name AS petName - UNION - MATCH (person)-[:HAS_CAT]->(cat:Cat) - RETURN cat.name AS petName - } AS numPets ----- - -[role="queryresult",options="header,footer",cols="2*(d:Dog) - WHERE d.name = name -} = 1 -RETURN person.name AS name ----- - -.Error message -[source, output, role="noheader"] ----- -The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 90)) ----- - -New variables can be introduced into the subquery, as long as they use a different identifier. -In the example below, 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. - -[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 ----- - -[role="queryresult",options="header,footer",cols="1*(:Dog) } as howManyDogs - ----- - -[role="queryresult",options="header,footer",cols="2*(:Dog) } -RETURN person.howManyDogs as howManyDogs - ----- - -[role="queryresult",options="header,footer",cols="1*(:Dog) } > 1 THEN "Doglover " + person.name - ELSE person.name - END AS result - ----- - -[role="queryresult",options="header,footer",cols="1*(:Dog) } AS numDogs, - avg(person.age) AS averageAge - ORDER BY numDogs - ----- - -[role="queryresult",options="header,footer",cols="2*(:Dog) - RETURN person.name -} = 1 -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(dog:Dog) RETURN dog.name } -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(dog:Dog) - WHERE r.since > 2017 - RETURN dog.name -} as youngDogs ----- - -[role="queryresult",options="header,footer",cols="2*(dog:Dog) - RETURN dog.name AS petName - UNION - MATCH (person)-[:HAS_CAT]->(cat:Cat) - RETURN cat.name AS petName - } AS petNames ----- - -[role="queryresult",options="header,footer",cols="2*(d:Dog {name: name}) - RETURN d.name -} as dogsOfTheYear ----- - -.Error message -[source, output, role="noheader"] ----- -The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 92)) ----- - -New variables can be introduced into the subquery, as long as they use a different identifier. -In the example below, 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. - -[source, cypher] ----- -MATCH (person:Person) -RETURN person.name AS name, COLLECT { - WITH 2018 AS yearOfTheDog - MATCH (person)-[r:HAS_DOG]->(d:Dog) - WHERE r.since = yearOfTheDog - RETURN d.name -} as dogsOfTheYear ----- - -[role="queryresult",options="header,footer",cols="2*(d:Dog) - MATCH (d)-[:HAS_TOY]->(t:Toy) - RETURN t.name - } as toyNames ----- - -[role="queryresult",options="header,footer",cols="2*(d:Dog) RETURN d.name } -RETURN person.dogNames as dogNames ----- - -[role="queryresult",options="header,footer",cols="1*(d:Dog) RETURN d.name } = [] THEN "No Dogs " + person.name - ELSE person.name - END AS result ----- - -[role="queryresult",options="header,footer",cols="1*(d:Dog) RETURN d.name } AS dogNames, - avg(person.age) AS averageAge - ORDER BY dogNames ----- - -[role="queryresult",options="header,footer",cols="2* IS :: ----- - -Where `` is any Cypher expression and `` is a Cypher Type. -For all available Cypher types, see the section on xref::values-and-types/property-structural-constructed.adoc#types-synonyms[types and their synonyms]. - -[source, cypher] ----- -UNWIND [42, true, 'abc'] AS val -RETURN val, val IS :: INTEGER AS isInteger ----- - -[role="queryresult",options="header,footer",cols="2* 18 -RETURN n.name AS name, n.age AS age ----- - -[role="queryresult",options="header,footer",cols="2* IS TYPED ----- - -[source, syntax, role="noheader", indent=0] ----- - :: ----- - -For verifying that an expression is not of a certain type, the following alternative syntax is supported: - -[source, syntax, role="noheader", indent=0] ----- - IS NOT TYPED ----- - -[[type-predicate-expressions-any-and-nothing]] -=== Use of `ANY` and `NOTHING` types - -_This feature was introduced in Neo4j 5.10._ - -`ANY` is a supertype which matches values of all types. -`NOTHING` is a type containing an empty set of values. -This means that it returns `false` for all values. - -[source, cypher] ----- -RETURN 42 IS :: ANY AS isOfTypeAny, 42 IS :: NOTHING AS isOfTypeNothing ----- - -[role="queryresult",options="header,footer",cols="2* AS isIntList ----- - -[role="queryresult",options="header,footer",cols="2* AS isNothingList, - [] IS :: LIST AS isIntList, - [] IS :: LIST AS isFloatNotNullList ----- - -[role="queryresult",options="header,footer",cols="3* as isMixedList ----- - -[role="queryresult",options="header,footer",cols="1*>. - -Label expressions evaluate to `true` or `false` when applied to the set of labels for a node. - -Assuming no other filters are applied, then a label expression evaluating to `true` means the node is matched. - -The following table displays whether the label expression matches the relationship: - -.Label expression matches -[cols="^3,^2,^2,^2,^2,^2,^2,^2,^2"] -|=== -| -8+^|*Node* - -|*Label expression* | `()` | `(:A)` | `(:B)` | `(:C)` | `(:A:B)` | `(:A:C)` | `(:B:C)` | `(:A:B:C)` -| `()` -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} - -| `(:A)` -| -| {check-mark} -| -| -| {check-mark} -| {check-mark} -| -| {check-mark} - -| `(:A&B)` -| -| -| -| -| {check-mark} -| -| -| {check-mark} - -| `(:A\|B)` -| -| {check-mark} -| {check-mark} -| -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} - -| `(:!A)` -| {check-mark} -| -| {check-mark} -| {check-mark} -| -| -| {check-mark} -| - -| `(:!!A)` -| -| {check-mark} -| -| -| {check-mark} -| {check-mark} -| -| {check-mark} - -| `(:A&!A)` -| -| -| -| -| -| -| -| - -| `(:A\|!A)` -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} - -| `(:%)` -| -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} - -| `(:!%)` -| {check-mark} -| -| -| -| -| -| -| - -| `(:%\|!%)` -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} - -| `(:%&!%)` -| -| -| -| -| -| -| -| - -| `(:A&%)` -| -| {check-mark} -| -| -| {check-mark} -| {check-mark} -| -| {check-mark} - -| `(:A\|%)` -| -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} - -| `(:(A&B)&!(B&C))` -| -| -| -| -| {check-mark} -| -| -| - -| `(:!(A&%)&%)` -| -| -| {check-mark} -| {check-mark} -| -| -| {check-mark} -| - -|=== - - -[[syntax-restrictions-label]] -=== Restrictions on using the different types of label expression syntax - -Neo4j version 5.0 introduced an ampersand operator, which is equivalent to the colon conjunction operator. -Mixing the colon conjunction operator with any of the new label expression operators in the same clause will raise a syntax error. - -For example, each of the following clauses will raise syntax errors: - -* `MATCH (n:A|B:C)` -* `MATCH (n:A:B)-[]-(m:(A&B)|C)` -* `MATCH (n:A:B)--(m), (n)-->(o:(A&B)|C)` -* `RETURN n:A&B, n:A:B` -* `MATCH (n:A:B)-[]-(m) WHERE m:(A&B)|C` - -In earlier versions of Neo4j (version 4.4 and earlier), relationship type expressions only had the pipe operator. -As the pipe operator will continue to act as an `OR` operator, it can continue to be used alongside the new operators. - -To make it easier to use the new syntax when extending existing queries, using the different syntax types in separate clauses will be supported. - -For example, the following query will not raise a syntax error: - -[source, cypher, role=noplay] ----- -MATCH (m:A:B:C)-[]->() -MATCH (n:(A&B)|C)-[]->(m) -RETURN m,n ----- - -Queries that exclusively use syntax from earlier versions of Neo4j (version 4.4 and earlier) will continue to be supported. - -For example, the following will not raise a syntax error: - -[source, cypher, role=noplay] ----- -MATCH (m:A:B:C)-[:S|T]->() -RETURN - CASE - WHEN m:D:E THEN m.p - ELSE null - END AS result ----- - -[[label-expressions-examples]] -==== Examples - -The following graph is used for the examples below: - -//// -[source, cypher, role=test-setup] ----- -MATCH (n:Toy|Cat|Dog|Person|Swedish) DETACH DELETE n; - -CREATE - (:A {name:'Alice'}), - (:B {name:'Bob'}), - (:C {name:'Charlie'}), - (:A:B {name:'Daniel'}), - (:A:C {name:'Eskil'}), - (:B:C {name:'Frank'}), - (:A:B:C {name:'George'}), - ({name:'Henry'}) ----- -//// - -image:graph_label_expressions.svg[] - -* xref:syntax/expressions.adoc#label-expressions-node-pattern-without-label-expressions[] -* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-single-node-label[] -* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-and-expression[] -* xref:syntax/expressions.adoc#label-expressions-note-pattern-with-or-expression[] -* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-not-expressions[] -* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-wildcard-expression[] -* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-nested-label-expressions[] -* xref:syntax/expressions.adoc#label-expressions-where-clause-with-label-expression-as-predicate[] -* xref:syntax/expressions.adoc#label-expressions-with-return-clauses[] - - -[discrete] -[[label-expressions-node-pattern-without-label-expressions]] -=== Node pattern without label expressions - -A node pattern without a label expression returns all nodes in the graph, including nodes without labels. - -.+Label expression+ -====== - -[source, cypher] ----- -MATCH (n) -RETURN n.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(:B), - (:C)-[:R2 {name:'Studies'}]->(:D), - (:E)-[:R3 {name:'Parents'}]->(:F) ----- -//// - -[discrete] -[[relationship-type-expressions-pattern-without-relationship-type-expression]] -=== Relationship pattern without relationship type expression - -A relationship pattern without a relationship type expression returns all relationships in the graph. - - -.Relationship type expressions -====== - -[source, cypher] ----- -MATCH ()-[r]->() -RETURN r.name as name ----- - -[role="queryresult",options="header,footer",cols="1*() -RETURN r.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*() -RETURN r.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*() -RETURN r.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*() -RETURN r.name as name ----- - -[role="queryresult",options="header,footer",cols="1*(m) -WHERE r:R1|R2 -RETURN r.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(m) -RETURN r:R1|R2 AS result ----- - -[role="queryresult",options="header,footer",cols="1*(m) -RETURN -CASE - WHEN n:A&B THEN 1 - WHEN r:!R1&!R2 THEN 2 - ELSE -1 -END AS result ----- - -[role="queryresult",options="header,footer",cols="1* Date: Thu, 17 Aug 2023 14:37:25 +0200 Subject: [PATCH 02/24] fix xrefs --- modules/ROOT/pages/clauses/match.adoc | 2 +- modules/ROOT/pages/clauses/where.adoc | 2 +- ...eprecations-additions-removals-compatibility.adoc | 8 ++++---- modules/ROOT/pages/expressions/label.adoc | 10 ---------- modules/ROOT/pages/expressions/relationship.adoc | 12 ------------ modules/ROOT/pages/functions/predicate.adoc | 2 +- modules/ROOT/pages/introduction/cypher_tutorial.adoc | 2 +- modules/ROOT/pages/keyword-glossary.adoc | 10 +++++----- modules/ROOT/pages/patterns/index.adoc | 2 +- modules/ROOT/pages/patterns/primer.adoc | 2 +- modules/ROOT/pages/patterns/reference.adoc | 2 +- modules/ROOT/pages/syntax/operators.adoc | 6 +++--- modules/ROOT/pages/syntax/parsing.adoc | 2 +- .../property-structural-constructed.adoc | 8 ++++---- .../pages/values-and-types/working-with-null.adoc | 2 +- 15 files changed, 25 insertions(+), 47 deletions(-) diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index 98d1548bc..1ab8ba836 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -286,7 +286,7 @@ Returns all actors who `ACTED_IN` the movie `Wall Street`. |Rows: 3 |=== -Read more about xref:/syntax/expressions.adoc#relationship-type-expressions[relationship type expressions]. +Read more about xref:expressions/relationship.adoc[relationship type expressions]. [[match-on-multiple-rel-types]] === Match on multiple relationship types diff --git a/modules/ROOT/pages/clauses/where.adoc b/modules/ROOT/pages/clauses/where.adoc index d37f8de9d..504131079 100644 --- a/modules/ROOT/pages/clauses/where.adoc +++ b/modules/ROOT/pages/clauses/where.adoc @@ -424,7 +424,7 @@ The `name`, `age`, and `email` values for `Peter` are returned because his email Note that the regular expression constructs in link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html[Java regular expressions] are applied only after resolving the escaped character sequences in the given -xref::syntax/expressions.adoc#cypher-expressions-string-literals[string literal]. +xref::expressions/index.adoc#expressions-string-literals[string literal]. It is sometimes necessary to add additional backslashes to express regular expression constructs. This list clarifies the combination of these two definitions, containing the original escape sequence and the resulting character in the regular expression: diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index 9b9437c4d..b193ede36 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -51,7 +51,7 @@ IS [NOT] :: ---- a| -Extended xref:syntax/expressions.adoc#type-predicate-expressions[type predicate expressions]. +Extended xref:expressions/type-predicate.adoc[type predicate expressions]. Closed dynamic union types (`type1 \| type2 \| ...`) are now supported. For example, the following query which evaluates to true if a value is either of type `INTEGER` or `FLOAT`: [source, cypher, role="noheader"] @@ -109,7 +109,7 @@ IS [NOT] :: ---- a| -Extended xref:syntax/expressions.adoc#type-predicate-expressions[type predicate expressions]. +Extended xref:expressions/type-predicate.adoc[type predicate expressions]. The newly supported types are: * `NOTHING` @@ -282,7 +282,7 @@ IS [NOT] :: ---- a| -Added xref:syntax/expressions.adoc#type-predicate-expressions[type predicate expressions]. +Added xref:expressions/type-predicate.adoc[type predicate expressions]. The available types are: * `BOOLEAN` @@ -3757,7 +3757,7 @@ For versions below 5.0, use a pattern comprehension instead: ---- RETURN size([ (a)-[]->(b) \| a ]) ---- -See xref:functions/scalar.adoc#functions-size[size()] and xref:syntax/expressions.adoc#count-subqueries[Count Subqueries] for more details. +See xref:functions/scalar.adoc#functions-size[size()] and xref:expressions/subquery.adoc#count[Count Subqueries] for more details. |=== === Updated features diff --git a/modules/ROOT/pages/expressions/label.adoc b/modules/ROOT/pages/expressions/label.adoc index f9900170d..9aec21151 100644 --- a/modules/ROOT/pages/expressions/label.adoc +++ b/modules/ROOT/pages/expressions/label.adoc @@ -248,16 +248,6 @@ CREATE image:graph_label_expressions.svg[] -* xref:syntax/expressions.adoc#label-expressions-node-pattern-without-label-expressions[] -* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-single-node-label[] -* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-and-expression[] -* xref:syntax/expressions.adoc#label-expressions-note-pattern-with-or-expression[] -* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-not-expressions[] -* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-wildcard-expression[] -* xref:syntax/expressions.adoc#label-expressions-node-pattern-with-nested-label-expressions[] -* xref:syntax/expressions.adoc#label-expressions-where-clause-with-label-expression-as-predicate[] -* xref:syntax/expressions.adoc#label-expressions-with-return-clauses[] - [discrete] [[label-expressions-node-pattern-without-label-expressions]] diff --git a/modules/ROOT/pages/expressions/relationship.adoc b/modules/ROOT/pages/expressions/relationship.adoc index f9626cf82..55a274644 100644 --- a/modules/ROOT/pages/expressions/relationship.adoc +++ b/modules/ROOT/pages/expressions/relationship.adoc @@ -99,18 +99,6 @@ Label expressions cannot be combined with label syntax. For example, `:A:B&C` will throw an error. Instead, use either `:A&B&C` or `:A:B:C`. -*Examples:* - -* xref:syntax/expressions.adoc#relationship-type-expressions-pattern-without-relationship-type-expression[] -* xref:syntax/expressions.adoc#relationship-type-expressions-pattern-on-single-relationship-type[] -* xref:syntax/expressions.adoc#relationship-type-expressions-pattern-with-or-expression[] -* xref:syntax/expressions.adoc#relationship-type-expressions-pattern-with-not-expression[] -* xref:syntax/expressions.adoc#relationship-type-expressions-pattern-nested-type-expression[] -* xref:syntax/expressions.adoc#relationship-type-expressions-pattern-predicate-type-expression[] -* xref:syntax/expressions.adoc#relationship-type-expressions-pattern-with-return-type-expression[] -* xref:syntax/expressions.adoc#relationship-type-expressions-case-type-label-expression[] - - The following graph is used for the examples below: image:graph_relationship_type_expressions.svg[] diff --git a/modules/ROOT/pages/functions/predicate.adoc b/modules/ROOT/pages/functions/predicate.adoc index 156f66ffd..3cb2a3ae8 100644 --- a/modules/ROOT/pages/functions/predicate.adoc +++ b/modules/ROOT/pages/functions/predicate.adoc @@ -256,7 +256,7 @@ This query returns the `name` property of every `Person` node, along with a bool ==== The *function* `exists()` looks very similar to the *expression* `+EXISTS { ... }+`, but they are not related. -See xref::syntax/expressions.adoc#existential-subqueries[Using EXISTS subqueries] for more information. +See xref::expressions/subquery.adoc#existential[Using EXISTS subqueries] for more information. ==== diff --git a/modules/ROOT/pages/introduction/cypher_tutorial.adoc b/modules/ROOT/pages/introduction/cypher_tutorial.adoc index 399776c81..73a6f3320 100644 --- a/modules/ROOT/pages/introduction/cypher_tutorial.adoc +++ b/modules/ROOT/pages/introduction/cypher_tutorial.adoc @@ -727,7 +727,7 @@ Return type(r), m.title AS movies 2+d|Rows: 1 |=== -For more information about the different label expressions supported by Cypher, see the section on xref:syntax/expressions.adoc#label-expressions[label expressions]. +For more information about the different label expressions supported by Cypher, see the section on xref:patterns/reference.adoc#label-expressions[label expressions]. == Finding paths diff --git a/modules/ROOT/pages/keyword-glossary.adoc b/modules/ROOT/pages/keyword-glossary.adoc index e38cf5f0f..31eac4e87 100644 --- a/modules/ROOT/pages/keyword-glossary.adoc +++ b/modules/ROOT/pages/keyword-glossary.adoc @@ -1023,19 +1023,19 @@ xref::functions/temporal/index.adoc#functions-temporal-truncate-overview[Truncat |=== | Name | Description -| xref::syntax/expressions.adoc#query-syntax-case[CASE] +| xref::expressions/case.adoc[CASE] | A generic conditional expression, similar to if/else statements available in other languages. -| xref:syntax/expressions.adoc#collect-subqueries[COLLECT {...}] +| xref:expressions/subquery.adoc#collect[COLLECT {...}] | Creates a list with the rows returned by a subquery. -| xref:syntax/expressions.adoc#count-subqueries[COUNT {...}] +| xref:expressions/subquery.adoc#count[COUNT {...}] | Computes the number of results of a subquery. -| xref:syntax/expressions.adoc#existential-subqueries[EXISTS {...}] +| xref:expressions/subquery.adoc#existential[EXISTS {...}] | Evaluates the existence of a subquery. -| xref:syntax/expressions.adoc#type-predicate-expressions[IS :: `type`] +| xref:expressions/type-predicate.adoc#[IS :: `type`] | Verifies that an expression is of a certain type. |=== diff --git a/modules/ROOT/pages/patterns/index.adoc b/modules/ROOT/pages/patterns/index.adoc index cf9fbe1e2..7e9d0d2f4 100644 --- a/modules/ROOT/pages/patterns/index.adoc +++ b/modules/ROOT/pages/patterns/index.adoc @@ -4,7 +4,7 @@ Graph pattern matching sits at the very core of Cypher. It is the mechanism used to navigate, describe and extract data from a graph by applying a declarative pattern. Inside a xref:clauses/match.adoc[] clause, you can use graph patterns to define the data you are searching for and the data to return. -Graph pattern matching can also be used without a `MATCH` clause, in the subqueries xref::syntax/expressions.adoc#existential-subqueries[EXISTS], xref::syntax/expressions.adoc#count-subqueries[COUNT], and xref::syntax/expressions.adoc#collect-subqueries[COLLECT]. +Graph pattern matching can also be used without a `MATCH` clause, in the subqueries xref::expressions/subquery.adoc#existential[EXISTS], xref::expressions/subquery.adoc#count[COUNT], and xref::expressions/subquery.adoc#collect[COLLECT]. A graph pattern describes data using a syntax that is similar to how the nodes and relationships of a property graph are drawn on a whiteboard. On a whiteboard, nodes are drawn as circles and relationships are drawn as arrows. diff --git a/modules/ROOT/pages/patterns/primer.adoc b/modules/ROOT/pages/patterns/primer.adoc index d414720b4..c5257e456 100644 --- a/modules/ROOT/pages/patterns/primer.adoc +++ b/modules/ROOT/pages/patterns/primer.adoc @@ -178,7 +178,7 @@ RETURN station.name AS callingPoint |=== `WHERE` clauses inside node patterns can themselves include path patterns. -The following query using an xref::syntax/expressions.adoc#existential-subqueries[EXISTS subquery] to anchor on the last `Stop` in a sequence of `Stops`, and returns the departure times, arrival times and final destination of all services calling at `Denmark Hill`: +The following query using an xref::expressions/subquery.adoc#existential[EXISTS subquery] to anchor on the last `Stop` in a sequence of `Stops`, and returns the departure times, arrival times and final destination of all services calling at `Denmark Hill`: .Query [source, cypher] diff --git a/modules/ROOT/pages/patterns/reference.adoc b/modules/ROOT/pages/patterns/reference.adoc index 6dd3946fc..9165219f9 100644 --- a/modules/ROOT/pages/patterns/reference.adoc +++ b/modules/ROOT/pages/patterns/reference.adoc @@ -340,7 +340,7 @@ is equivalent to the following node pattern with a `WHERE` clause: (n WHERE n.p = valueExp1 AND n.q = valueExp2) ---- -The value expression can be any expression as listed in the chapter on xref:syntax/expressions.adoc[expressions], except for path patterns (which will throw a syntax error) and regular expressions (which will be treated as string literals). +The value expression can be any expression as listed in the chapter on xref:expressions/index.adoc[expressions], except for path patterns (which will throw a syntax error) and regular expressions (which will be treated as string literals). An empty property key-value expression matches all elements. Property key-value expressions can be combined with a `WHERE` clause. diff --git a/modules/ROOT/pages/syntax/operators.adoc b/modules/ROOT/pages/syntax/operators.adoc index 4a938aed9..2dca11960 100644 --- a/modules/ROOT/pages/syntax/operators.adoc +++ b/modules/ROOT/pages/syntax/operators.adoc @@ -406,9 +406,9 @@ For example, `1 > b` and `1 < b` are both false when b is NaN. *** xref::values-and-types/temporal.adoc[`ZONED TIME`] *** xref::values-and-types/temporal.adoc[`LOCAL TIME`] *** xref::values-and-types/temporal.adoc[`DURATION`] - *** xref::syntax/expressions.adoc#cypher-expressions-general[`STRING`] - *** xref::syntax/expressions.adoc#cypher-expressions-general[`BOOLEAN`] - *** Numbers: xref::syntax/expressions.adoc#cypher-expressions-general[`INTEGER`, `FLOAT`] + *** xref::expressions/index.adoc#expressions-general[`STRING`] + *** xref::expressions/index.adoc#expressions-general[`BOOLEAN`] + *** Numbers: xref::expressions/index.adoc#expressions-general[`INTEGER`, `FLOAT`] ** The value `null` is ordered after all other values. * *Ordering* of constructed type values: ** For the xref::values-and-types/property-structural-constructed.adoc#constructed-types[constructed types] (e.g. maps and lists), elements of the containers are compared pairwise for ordering and thus determine the ordering of two container types. diff --git a/modules/ROOT/pages/syntax/parsing.adoc b/modules/ROOT/pages/syntax/parsing.adoc index f875406a8..0f8c22ecc 100644 --- a/modules/ROOT/pages/syntax/parsing.adoc +++ b/modules/ROOT/pages/syntax/parsing.adoc @@ -16,7 +16,7 @@ Unicodes can generally be escaped as `\uxxx`. Additional documentation on escaping rules for string literals, names and regular expressions can be found here: -* xref::syntax/expressions.adoc#cypher-expressions-string-literals[String literal escape sequences] +* xref::expressions/index.adoc#expressions-string-literals[String literal escape sequences] * xref::syntax/naming.adoc#symbolic-names-escaping-rules[Using special characters in names] * xref::clauses/where.adoc#escaping-in-regular-expressions[Regular epxressions] diff --git a/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc b/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc index 35c11ba8d..6b6add9b8 100644 --- a/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc +++ b/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc @@ -17,7 +17,7 @@ The following data types are included in the property types category: `BOOLEAN`, * Property types can be returned from Cypher queries. * Property types can be used as xref::syntax/parameters.adoc[parameters]. * Property types can be stored as properties. -* Property types can be constructed with xref::syntax/expressions.adoc[Cypher literals]. +* Property types can be constructed with xref::expressions/index.adoc[Cypher literals]. Homogeneous lists of simple types can be stored as properties, although lists in general (see xref::values-and-types/property-structural-constructed.adoc#constructed-types[Constructed types]) cannot be stored as properties. Lists stored as properties cannot contain `null` values. @@ -35,7 +35,7 @@ The following data types are included in the structural types category: `NODE`, * Structural types can be returned from Cypher queries. * Structural types cannot be used as xref::syntax/parameters.adoc[parameters]. * Structural types cannot be stored as properties. -* Structural types cannot be constructed with xref::syntax/expressions.adoc[Cypher literals]. +* Structural types cannot be constructed with xref::expressions/index.adoc[Cypher literals]. The `NODE` data type includes: id, label(s), and a map of properties. Note that labels are not values, but a form of pattern syntax. @@ -59,7 +59,7 @@ The following data types are included in the constructed types category: `LIST` * Constructed types can be returned from Cypher queries. * Constructed types can be used as xref::syntax/parameters.adoc[parameters]. * Constructed types cannot be stored as properties (with the exception of homogenous lists). -* Constructed types can be constructed with xref::syntax/expressions.adoc[Cypher literals]. +* Constructed types can be constructed with xref::expressions/index.adoc[Cypher literals]. The `LIST` data type can be either a homogenous collection of simple values, or a heterogeneous, ordered collection of values, each of which can have any property, structural or constructed type. @@ -72,7 +72,7 @@ For more details, see xref::values-and-types/working-with-null.adoc[working with == Types and their synonyms The table below shows the types and their syntactic synonyms. -These types (and their synonyms) can be used in xref::syntax/expressions.adoc#type-predicate-expressions[type predicate expressions] and in xref::constraints/examples.adoc#constraints-examples-node-property-type[node] and xref::constraints/examples.adoc#constraints-examples-relationship-property-type[relationship] property type constraints. +These types (and their synonyms) can be used in xref::expressions/type-predicate.adoc[type predicate expressions] and in xref::constraints/examples.adoc#constraints-examples-node-property-type[node] and xref::constraints/examples.adoc#constraints-examples-relationship-property-type[relationship] property type constraints. However, not all types can be used in all places. [.synonyms, opts="header", cols="2a,2a"] diff --git a/modules/ROOT/pages/values-and-types/working-with-null.adoc b/modules/ROOT/pages/values-and-types/working-with-null.adoc index 883bd9b69..e91f62a0c 100644 --- a/modules/ROOT/pages/values-and-types/working-with-null.adoc +++ b/modules/ROOT/pages/values-and-types/working-with-null.adoc @@ -5,7 +5,7 @@ In Cypher, `null` is used to represent missing or undefined values. All data types in Cypher are nullable. -This means that xref::syntax/expressions.adoc#type-predicate-expressions-null[type predicate expressions] always return `true` for `null` values. +This means that xref::expressions/type-predicate.adoc#type-predicate-null[type predicate expressions] always return `true` for `null` values. Conceptually, `null` means **a missing or unknown value**, and it is treated somewhat differently from other values. For example, returning a property from a node that does not have said property produces `null`. From 5ba09742d5bc0fd6f97421454e68d1291ddd1afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 31 Aug 2023 13:53:51 +0200 Subject: [PATCH 03/24] big structural update --- modules/ROOT/content-nav.adoc | 38 +- modules/ROOT/pages/expressions/index.adoc | 2 - modules/ROOT/pages/expressions/subquery.adoc | 819 ---------------- modules/ROOT/pages/queries/advanced.adoc | 1 + modules/ROOT/pages/queries/basic.adoc | 877 ++++++++++++++++++ .../pages/{expressions => queries}/case.adoc | 2 +- modules/ROOT/pages/queries/concepts.adoc | 65 ++ modules/ROOT/pages/queries/expressions.adoc | 67 ++ modules/ROOT/pages/queries/index.adoc | 7 + .../call-subquery.adoc | 0 modules/ROOT/pages/subqueries/collect.adoc | 289 ++++++ modules/ROOT/pages/subqueries/count.adoc | 265 ++++++ .../ROOT/pages/subqueries/existential.adoc | 225 +++++ modules/ROOT/pages/subqueries/index.adoc | 40 + .../ROOT/pages/values-and-types/index.adoc | 3 +- .../type-predicate.adoc | 0 16 files changed, 1861 insertions(+), 839 deletions(-) delete mode 100644 modules/ROOT/pages/expressions/subquery.adoc create mode 100644 modules/ROOT/pages/queries/advanced.adoc create mode 100644 modules/ROOT/pages/queries/basic.adoc rename modules/ROOT/pages/{expressions => queries}/case.adoc (99%) create mode 100644 modules/ROOT/pages/queries/concepts.adoc create mode 100644 modules/ROOT/pages/queries/expressions.adoc create mode 100644 modules/ROOT/pages/queries/index.adoc rename modules/ROOT/pages/{clauses => subqueries}/call-subquery.adoc (100%) create mode 100644 modules/ROOT/pages/subqueries/collect.adoc create mode 100644 modules/ROOT/pages/subqueries/count.adoc create mode 100644 modules/ROOT/pages/subqueries/existential.adoc create mode 100644 modules/ROOT/pages/subqueries/index.adoc rename modules/ROOT/pages/{expressions => values-and-types}/type-predicate.adoc (100%) diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index c6308ff83..cf758da46 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -4,14 +4,18 @@ ** xref:introduction/cypher_neo4j.adoc[] ** xref:introduction/cypher_aura.adoc[] -* xref:syntax/index.adoc[] -** xref:syntax/parsing.adoc[] -** xref:syntax/naming.adoc[] -** xref:syntax/variables.adoc[] -** xref:syntax/reserved.adoc[] -** xref:syntax/parameters.adoc[] -** xref:syntax/operators.adoc[] -** xref:syntax/comments.adoc[] +* xref:queries/index.adoc[] +** xref:queries/concepts.adoc[] +** xref:queries/basic.adoc[] +** xref:queries/advanced.adoc[] +** xref:queries/case.adoc[] +** xref:queries/expressions.adoc[] + +* xref:subqueries/index.adoc[] +** xref:subqueries/call-subquery.adoc[] +** xref:subqueries/existential.adoc[] +** xref:subqueries/count.adoc[] +** xref:subqueries/collect.adoc[] * xref:clauses/index.adoc[] ** xref:clauses/clause_composition.adoc[] @@ -56,14 +60,7 @@ ** xref:values-and-types/lists.adoc[] ** xref:values-and-types/maps.adoc[] ** xref:values-and-types/casting-data.adoc[] - -* xref:expressions/index.adoc[] -** xref:expressions/case.adoc[] -** xref:expressions/subquery.adoc[] -** xref:expressions/type-predicate.adoc[] -** xref:expressions/label.adoc[] -** xref:expressions/relationship.adoc[] - +** xref:values-and-types/type-predicate.adoc[] * xref:functions/index.adoc[] ** xref:functions/predicate.adoc[] @@ -122,6 +119,15 @@ *** xref:administration/access-control/limitations.adoc[] *** xref:administration/access-control/privileges-immutable.adoc[] +* xref:syntax/index.adoc[] +** xref:syntax/parsing.adoc[] +** xref:syntax/naming.adoc[] +** xref:syntax/variables.adoc[] +** xref:syntax/reserved.adoc[] +** xref:syntax/parameters.adoc[] +** xref:syntax/operators.adoc[] +** xref:syntax/comments.adoc[] + * xref:deprecations-additions-removals-compatibility.adoc[] * xref:keyword-glossary.adoc[] diff --git a/modules/ROOT/pages/expressions/index.adoc b/modules/ROOT/pages/expressions/index.adoc index e1dc00b5d..67cac926d 100644 --- a/modules/ROOT/pages/expressions/index.adoc +++ b/modules/ROOT/pages/expressions/index.adoc @@ -7,8 +7,6 @@ More information about each of these can be found in the following sections: * xref:expressions/case.adoc[] * xref:expressions/subquery.adoc[] * xref:expressions/type-predicate.adoc[] -* xref:expressions/label.adoc[] -* xref:expressions/relationship.adoc[] [[expressions-overview]] == General overview diff --git a/modules/ROOT/pages/expressions/subquery.adoc b/modules/ROOT/pages/expressions/subquery.adoc deleted file mode 100644 index dd97d5e7b..000000000 --- a/modules/ROOT/pages/expressions/subquery.adoc +++ /dev/null @@ -1,819 +0,0 @@ -[[expressions-subquery]] -= Subquery expressions - -Cypher includes three different types of subquery expressions: - -* xref:expressions/subquery.adoc#existential[Existential subquery expressions] -* xref:expressions/subquery.adoc#count[Count subquery expressions] -* xref:expressions/subquery.adoc#collect[Collect subquery expressions] label:new[Introduced in 5.6] - -Subquery expressions can appear anywhere that an expression is valid. -A subquery has a scope, as indicated by the opening and closing braces, `{` and `}`. -Any variable that is defined in the outside scope can be referenced inside the subquery's own scope. -Variables introduced inside the subquery are not part of the outside scope and therefore can't be accessed on the outside. - -[[expressions-subquery-example]] - -== Example graph -The following graph is used for the examples below: - -//// -[source, cypher, role=test-setup] ----- -MATCH (n:A|B|C|D|E) DETACH DELETE n; -CREATE -(andy:Swedish:Person {name: 'Andy', age: 36}), -(timothy:Person {name: 'Timothy', nickname: 'Tim', age: 25}), -(peter:Person {name: 'Peter', nickname: 'Pete', 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]] -== `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 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. - -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-simple]] -=== Simple `EXISTS` subquery - -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: - - -[source, cypher] ----- -MATCH (person:Person) -WHERE EXISTS { - (person)-[:HAS_DOG]->(:Dog) -} -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(dog:Dog) - WHERE person.name = dog.name -} -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(dog:Dog) - WHERE EXISTS { - MATCH (dog)-[:HAS_TOY]->(toy:Toy) - WHERE toy.name = 'Banana' - } -} -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(:Dog) -} AS hasDog ----- - -[role="queryresult",options="header,footer",cols="2*(:Dog) - UNION - MATCH (person)-[:HAS_CAT]->(:Cat) - } AS hasPet ----- - -[role="queryresult",options="header,footer",cols="2*(d:Dog) - WHERE d.name = name -} -RETURN person.name AS name ----- - -.Error message -[source, output, role="noheader"] ----- -The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 90)) ----- - -New variables can be introduced into the subquery, as long as they use a different identifier. -In the example below, 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. - -[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 ----- - -[role="queryresult",options="header,footer",cols="1*(:Dog) - RETURN person.name -} -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(:Dog) } > 1 -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(dog:Dog) - WHERE person.name = dog.name -} = 1 -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(dog:Dog) - RETURN dog.name AS petName - UNION - MATCH (person)-[:HAS_CAT]->(cat:Cat) - RETURN cat.name AS petName - } AS numPets ----- - -[role="queryresult",options="header,footer",cols="2*(d:Dog) - WHERE d.name = name -} = 1 -RETURN person.name AS name ----- - -.Error message -[source, output, role="noheader"] ----- -The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 90)) ----- - -New variables can be introduced into the subquery, as long as they use a different identifier. -In the example below, 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. - -[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 ----- - -[role="queryresult",options="header,footer",cols="1*(:Dog) } as howManyDogs - ----- - -[role="queryresult",options="header,footer",cols="2*(:Dog) } -RETURN person.howManyDogs as howManyDogs - ----- - -[role="queryresult",options="header,footer",cols="1*(:Dog) } > 1 THEN "Doglover " + person.name - ELSE person.name - END AS result - ----- - -[role="queryresult",options="header,footer",cols="1*(:Dog) } AS numDogs, - avg(person.age) AS averageAge - ORDER BY numDogs - ----- - -[role="queryresult",options="header,footer",cols="2*(:Dog) - RETURN person.name -} = 1 -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(dog:Dog) RETURN dog.name } -RETURN person.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(dog:Dog) - WHERE r.since > 2017 - RETURN dog.name -} as youngDogs ----- - -[role="queryresult",options="header,footer",cols="2*(dog:Dog) - RETURN dog.name AS petName - UNION - MATCH (person)-[:HAS_CAT]->(cat:Cat) - RETURN cat.name AS petName - } AS petNames ----- - -[role="queryresult",options="header,footer",cols="2*(d:Dog {name: name}) - RETURN d.name -} as dogsOfTheYear ----- - -.Error message -[source, output, role="noheader"] ----- -The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 92)) ----- - -New variables can be introduced into the subquery, as long as they use a different identifier. -In the example below, 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. - -[source, cypher] ----- -MATCH (person:Person) -RETURN person.name AS name, COLLECT { - WITH 2018 AS yearOfTheDog - MATCH (person)-[r:HAS_DOG]->(d:Dog) - WHERE r.since = yearOfTheDog - RETURN d.name -} as dogsOfTheYear ----- - -[role="queryresult",options="header,footer",cols="2*(d:Dog) - MATCH (d)-[:HAS_TOY]->(t:Toy) - RETURN t.name - } as toyNames ----- - -[role="queryresult",options="header,footer",cols="2*(d:Dog) RETURN d.name } -RETURN person.dogNames as dogNames ----- - -[role="queryresult",options="header,footer",cols="1*(d:Dog) RETURN d.name } = [] THEN "No Dogs " + person.name - ELSE person.name - END AS result ----- - -[role="queryresult",options="header,footer",cols="1*(d:Dog) RETURN d.name } AS dogNames, - avg(person.age) AS averageAge - ORDER BY dogNames ----- - -[role="queryresult",options="header,footer",cols="2*(TheMatrix), +(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrix), +(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrix), +(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrix), +(LillyW)-[:DIRECTED]->(TheMatrix), +(LanaW)-[:DIRECTED]->(TheMatrix), +(JoelS)-[:PRODUCED]->(TheMatrix) + +CREATE (Emil:Person {name:"Emil Eifrem", born:1978}) +CREATE (Emil)-[:ACTED_IN {roles:["Emil"]}]->(TheMatrix) + +CREATE (TheMatrixReloaded:Movie {title:'The Matrix Reloaded', released:2003, tagline:'Free your mind'}) +CREATE +(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixReloaded), +(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixReloaded), +(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixReloaded), +(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixReloaded), +(LillyW)-[:DIRECTED]->(TheMatrixReloaded), +(LanaW)-[:DIRECTED]->(TheMatrixReloaded), +(JoelS)-[:PRODUCED]->(TheMatrixReloaded) + +CREATE (TheMatrixRevolutions:Movie {title:'The Matrix Revolutions', released:2003, tagline:'Everything that has a beginning has an end'}) +CREATE +(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixRevolutions), +(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixRevolutions), +(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixRevolutions), +(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixRevolutions), +(LillyW)-[:DIRECTED]->(TheMatrixRevolutions), +(LanaW)-[:DIRECTED]->(TheMatrixRevolutions), +(JoelS)-[:PRODUCED]->(TheMatrixRevolutions) + +CREATE (TheDevilsAdvocate:Movie {title:"The Devil's Advocate", released:1997, tagline:'Evil has its winning ways'}) +CREATE (Charlize:Person {name:'Charlize Theron', born:1975}) +CREATE (Al:Person {name:'Al Pacino', born:1940}) +CREATE (Taylor:Person {name:'Taylor Hackford', born:1944}) +CREATE +(Keanu)-[:ACTED_IN {roles:['Kevin Lomax']}]->(TheDevilsAdvocate), +(Charlize)-[:ACTED_IN {roles:['Mary Ann Lomax']}]->(TheDevilsAdvocate), +(Al)-[:ACTED_IN {roles:['John Milton']}]->(TheDevilsAdvocate), +(Taylor)-[:DIRECTED]->(TheDevilsAdvocate) + +CREATE (AFewGoodMen:Movie {title:"A Few Good Men", released:1992, tagline:"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth."}) +CREATE (TomC:Person {name:'Tom Cruise', born:1962}) +CREATE (JackN:Person {name:'Jack Nicholson', born:1937}) +CREATE (DemiM:Person {name:'Demi Moore', born:1962}) +CREATE (KevinB:Person {name:'Kevin Bacon', born:1958}) +CREATE (KieferS:Person {name:'Kiefer Sutherland', born:1966}) +CREATE (NoahW:Person {name:'Noah Wyle', born:1971}) +CREATE (CubaG:Person {name:'Cuba Gooding Jr.', born:1968}) +CREATE (KevinP:Person {name:'Kevin Pollak', born:1957}) +CREATE (JTW:Person {name:'J.T. Walsh', born:1943}) +CREATE (JamesM:Person {name:'James Marshall', born:1967}) +CREATE (ChristopherG:Person {name:'Christopher Guest', born:1948}) +CREATE (RobR:Person {name:'Rob Reiner', born:1947}) +CREATE (AaronS:Person {name:'Aaron Sorkin', born:1961}) +CREATE +(TomC)-[:ACTED_IN {roles:['Lt. Daniel Kaffee']}]->(AFewGoodMen), +(JackN)-[:ACTED_IN {roles:['Col. Nathan R. Jessup']}]->(AFewGoodMen), +(DemiM)-[:ACTED_IN {roles:['Lt. Cdr. JoAnne Galloway']}]->(AFewGoodMen), +(KevinB)-[:ACTED_IN {roles:['Capt. Jack Ross']}]->(AFewGoodMen), +(KieferS)-[:ACTED_IN {roles:['Lt. Jonathan Kendrick']}]->(AFewGoodMen), +(NoahW)-[:ACTED_IN {roles:['Cpl. Jeffrey Barnes']}]->(AFewGoodMen), +(CubaG)-[:ACTED_IN {roles:['Cpl. Carl Hammaker']}]->(AFewGoodMen), +(KevinP)-[:ACTED_IN {roles:['Lt. Sam Weinberg']}]->(AFewGoodMen), +(JTW)-[:ACTED_IN {roles:['Lt. Col. Matthew Andrew Markinson']}]->(AFewGoodMen), +(JamesM)-[:ACTED_IN {roles:['Pfc. Louden Downey']}]->(AFewGoodMen), +(ChristopherG)-[:ACTED_IN {roles:['Dr. Stone']}]->(AFewGoodMen), +(AaronS)-[:ACTED_IN {roles:['Man in Bar']}]->(AFewGoodMen), +(RobR)-[:DIRECTED]->(AFewGoodMen), +(AaronS)-[:WROTE]->(AFewGoodMen) + +CREATE (TopGun:Movie {title:"Top Gun", released:1986, tagline:'I feel the need, the need for speed.'}) +CREATE (KellyM:Person {name:'Kelly McGillis', born:1957}) +CREATE (ValK:Person {name:'Val Kilmer', born:1959}) +CREATE (AnthonyE:Person {name:'Anthony Edwards', born:1962}) +CREATE (TomS:Person {name:'Tom Skerritt', born:1933}) +CREATE (MegR:Person {name:'Meg Ryan', born:1961}) +CREATE (TonyS:Person {name:'Tony Scott', born:1944}) +CREATE (JimC:Person {name:'Jim Cash', born:1941}) +CREATE +(TomC)-[:ACTED_IN {roles:['Maverick']}]->(TopGun), +(KellyM)-[:ACTED_IN {roles:['Charlie']}]->(TopGun), +(ValK)-[:ACTED_IN {roles:['Iceman']}]->(TopGun), +(AnthonyE)-[:ACTED_IN {roles:['Goose']}]->(TopGun), +(TomS)-[:ACTED_IN {roles:['Viper']}]->(TopGun), +(MegR)-[:ACTED_IN {roles:['Carole']}]->(TopGun), +(TonyS)-[:DIRECTED]->(TopGun), +(JimC)-[:WROTE]->(TopGun) + +CREATE (JerryMaguire:Movie {title:'Jerry Maguire', released:2000, tagline:'The rest of his life begins now.'}) +CREATE (ReneeZ:Person {name:'Renee Zellweger', born:1969}) +CREATE (KellyP:Person {name:'Kelly Preston', born:1962}) +CREATE (JerryO:Person {name:"Jerry O'Connell", born:1974}) +CREATE (JayM:Person {name:'Jay Mohr', born:1970}) +CREATE (BonnieH:Person {name:'Bonnie Hunt', born:1961}) +CREATE (ReginaK:Person {name:'Regina King', born:1971}) +CREATE (JonathanL:Person {name:'Jonathan Lipnicki', born:1996}) +CREATE (CameronC:Person {name:'Cameron Crowe', born:1957}) +CREATE +(TomC)-[:ACTED_IN {roles:['Jerry Maguire']}]->(JerryMaguire), +(CubaG)-[:ACTED_IN {roles:['Rod Tidwell']}]->(JerryMaguire), +(ReneeZ)-[:ACTED_IN {roles:['Dorothy Boyd']}]->(JerryMaguire), +(KellyP)-[:ACTED_IN {roles:['Avery Bishop']}]->(JerryMaguire), +(JerryO)-[:ACTED_IN {roles:['Frank Cushman']}]->(JerryMaguire), +(JayM)-[:ACTED_IN {roles:['Bob Sugar']}]->(JerryMaguire), +(BonnieH)-[:ACTED_IN {roles:['Laurel Boyd']}]->(JerryMaguire), +(ReginaK)-[:ACTED_IN {roles:['Marcee Tidwell']}]->(JerryMaguire), +(JonathanL)-[:ACTED_IN {roles:['Ray Boyd']}]->(JerryMaguire), +(CameronC)-[:DIRECTED]->(JerryMaguire), +(CameronC)-[:PRODUCED]->(JerryMaguire), +(CameronC)-[:WROTE]->(JerryMaguire) + +CREATE (StandByMe:Movie {title:"Stand By Me", released:1986, tagline:"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of."}) +CREATE (RiverP:Person {name:'River Phoenix', born:1970}) +CREATE (CoreyF:Person {name:'Corey Feldman', born:1971}) +CREATE (WilW:Person {name:'Wil Wheaton', born:1972}) +CREATE (JohnC:Person {name:'John Cusack', born:1966}) +CREATE (MarshallB:Person {name:'Marshall Bell', born:1942}) +CREATE +(WilW)-[:ACTED_IN {roles:['Gordie Lachance']}]->(StandByMe), +(RiverP)-[:ACTED_IN {roles:['Chris Chambers']}]->(StandByMe), +(JerryO)-[:ACTED_IN {roles:['Vern Tessio']}]->(StandByMe), +(CoreyF)-[:ACTED_IN {roles:['Teddy Duchamp']}]->(StandByMe), +(JohnC)-[:ACTED_IN {roles:['Denny Lachance']}]->(StandByMe), +(KieferS)-[:ACTED_IN {roles:['Ace Merrill']}]->(StandByMe), +(MarshallB)-[:ACTED_IN {roles:['Mr. Lachance']}]->(StandByMe), +(RobR)-[:DIRECTED]->(StandByMe) + +CREATE (AsGoodAsItGets:Movie {title:'As Good as It Gets', released:1997, tagline:'A comedy from the heart that goes for the throat.'}) +CREATE (HelenH:Person {name:'Helen Hunt', born:1963}) +CREATE (GregK:Person {name:'Greg Kinnear', born:1963}) +CREATE (JamesB:Person {name:'James L. Brooks', born:1940}) +CREATE +(JackN)-[:ACTED_IN {roles:['Melvin Udall']}]->(AsGoodAsItGets), +(HelenH)-[:ACTED_IN {roles:['Carol Connelly']}]->(AsGoodAsItGets), +(GregK)-[:ACTED_IN {roles:['Simon Bishop']}]->(AsGoodAsItGets), +(CubaG)-[:ACTED_IN {roles:['Frank Sachs']}]->(AsGoodAsItGets), +(JamesB)-[:DIRECTED]->(AsGoodAsItGets) + +CREATE (WhatDreamsMayCome:Movie {title:'What Dreams May Come', released:1998, tagline:'After life there is more. The end is just the beginning.'}) +CREATE (AnnabellaS:Person {name:'Annabella Sciorra', born:1960}) +CREATE (MaxS:Person {name:'Max von Sydow', born:1929}) +CREATE (WernerH:Person {name:'Werner Herzog', born:1942}) +CREATE (Robin:Person {name:'Robin Williams', born:1951}) +CREATE (VincentW:Person {name:'Vincent Ward', born:1956}) +CREATE +(Robin)-[:ACTED_IN {roles:['Chris Nielsen']}]->(WhatDreamsMayCome), +(CubaG)-[:ACTED_IN {roles:['Albert Lewis']}]->(WhatDreamsMayCome), +(AnnabellaS)-[:ACTED_IN {roles:['Annie Collins-Nielsen']}]->(WhatDreamsMayCome), +(MaxS)-[:ACTED_IN {roles:['The Tracker']}]->(WhatDreamsMayCome), +(WernerH)-[:ACTED_IN {roles:['The Face']}]->(WhatDreamsMayCome), +(VincentW)-[:DIRECTED]->(WhatDreamsMayCome) + +CREATE (SnowFallingonCedars:Movie {title:'Snow Falling on Cedars', released:1999, tagline:'First loves last. Forever.'}) +CREATE (EthanH:Person {name:'Ethan Hawke', born:1970}) +CREATE (RickY:Person {name:'Rick Yune', born:1971}) +CREATE (JamesC:Person {name:'James Cromwell', born:1940}) +CREATE (ScottH:Person {name:'Scott Hicks', born:1953}) +CREATE +(EthanH)-[:ACTED_IN {roles:['Ishmael Chambers']}]->(SnowFallingonCedars), +(RickY)-[:ACTED_IN {roles:['Kazuo Miyamoto']}]->(SnowFallingonCedars), +(MaxS)-[:ACTED_IN {roles:['Nels Gudmundsson']}]->(SnowFallingonCedars), +(JamesC)-[:ACTED_IN {roles:['Judge Fielding']}]->(SnowFallingonCedars), +(ScottH)-[:DIRECTED]->(SnowFallingonCedars) + +CREATE (YouveGotMail:Movie {title:"You've Got Mail", released:1998, tagline:'At odds in life... in love on-line.'}) +CREATE (ParkerP:Person {name:'Parker Posey', born:1968}) +CREATE (DaveC:Person {name:'Dave Chappelle', born:1973}) +CREATE (SteveZ:Person {name:'Steve Zahn', born:1967}) +CREATE (TomH:Person {name:'Tom Hanks', born:1956}) +CREATE (NoraE:Person {name:'Nora Ephron', born:1941}) +CREATE +(TomH)-[:ACTED_IN {roles:['Joe Fox']}]->(YouveGotMail), +(MegR)-[:ACTED_IN {roles:['Kathleen Kelly']}]->(YouveGotMail), +(GregK)-[:ACTED_IN {roles:['Frank Navasky']}]->(YouveGotMail), +(ParkerP)-[:ACTED_IN {roles:['Patricia Eden']}]->(YouveGotMail), +(DaveC)-[:ACTED_IN {roles:['Kevin Jackson']}]->(YouveGotMail), +(SteveZ)-[:ACTED_IN {roles:['George Pappas']}]->(YouveGotMail), +(NoraE)-[:DIRECTED]->(YouveGotMail) + +CREATE (SleeplessInSeattle:Movie {title:'Sleepless in Seattle', released:1993, tagline:'What if someone you never met, someone you never saw, someone you never knew was the only someone for you?'}) +CREATE (RitaW:Person {name:'Rita Wilson', born:1956}) +CREATE (BillPull:Person {name:'Bill Pullman', born:1953}) +CREATE (VictorG:Person {name:'Victor Garber', born:1949}) +CREATE (RosieO:Person {name:"Rosie O'Donnell", born:1962}) +CREATE +(TomH)-[:ACTED_IN {roles:['Sam Baldwin']}]->(SleeplessInSeattle), +(MegR)-[:ACTED_IN {roles:['Annie Reed']}]->(SleeplessInSeattle), +(RitaW)-[:ACTED_IN {roles:['Suzy']}]->(SleeplessInSeattle), +(BillPull)-[:ACTED_IN {roles:['Walter']}]->(SleeplessInSeattle), +(VictorG)-[:ACTED_IN {roles:['Greg']}]->(SleeplessInSeattle), +(RosieO)-[:ACTED_IN {roles:['Becky']}]->(SleeplessInSeattle), +(NoraE)-[:DIRECTED]->(SleeplessInSeattle) + +CREATE (JoeVersustheVolcano:Movie {title:'Joe Versus the Volcano', released:1990, tagline:'A story of love, lava and burning desire.'}) +CREATE (JohnS:Person {name:'John Patrick Stanley', born:1950}) +CREATE (Nathan:Person {name:'Nathan Lane', born:1956}) +CREATE +(TomH)-[:ACTED_IN {roles:['Joe Banks']}]->(JoeVersustheVolcano), +(MegR)-[:ACTED_IN {roles:['DeDe', 'Angelica Graynamore', 'Patricia Graynamore']}]->(JoeVersustheVolcano), +(Nathan)-[:ACTED_IN {roles:['Baw']}]->(JoeVersustheVolcano), +(JohnS)-[:DIRECTED]->(JoeVersustheVolcano) + +CREATE (WhenHarryMetSally:Movie {title:'When Harry Met Sally', released:1998, tagline:'Can two friends sleep together and still love each other in the morning?'}) +CREATE (BillyC:Person {name:'Billy Crystal', born:1948}) +CREATE (CarrieF:Person {name:'Carrie Fisher', born:1956}) +CREATE (BrunoK:Person {name:'Bruno Kirby', born:1949}) +CREATE +(BillyC)-[:ACTED_IN {roles:['Harry Burns']}]->(WhenHarryMetSally), +(MegR)-[:ACTED_IN {roles:['Sally Albright']}]->(WhenHarryMetSally), +(CarrieF)-[:ACTED_IN {roles:['Marie']}]->(WhenHarryMetSally), +(BrunoK)-[:ACTED_IN {roles:['Jess']}]->(WhenHarryMetSally), +(RobR)-[:DIRECTED]->(WhenHarryMetSally), +(RobR)-[:PRODUCED]->(WhenHarryMetSally), +(NoraE)-[:PRODUCED]->(WhenHarryMetSally), +(NoraE)-[:WROTE]->(WhenHarryMetSally) + +CREATE (ThatThingYouDo:Movie {title:'That Thing You Do', released:1996, tagline:'In every life there comes a time when that thing you dream becomes that thing you do'}) +CREATE (LivT:Person {name:'Liv Tyler', born:1977}) +CREATE +(TomH)-[:ACTED_IN {roles:['Mr. White']}]->(ThatThingYouDo), +(LivT)-[:ACTED_IN {roles:['Faye Dolan']}]->(ThatThingYouDo), +(Charlize)-[:ACTED_IN {roles:['Tina']}]->(ThatThingYouDo), +(TomH)-[:DIRECTED]->(ThatThingYouDo) + +CREATE (TheReplacements:Movie {title:'The Replacements', released:2000, tagline:'Pain heals, Chicks dig scars... Glory lasts forever'}) +CREATE (Brooke:Person {name:'Brooke Langton', born:1970}) +CREATE (Gene:Person {name:'Gene Hackman', born:1930}) +CREATE (Orlando:Person {name:'Orlando Jones', born:1968}) +CREATE (Howard:Person {name:'Howard Deutch', born:1950}) +CREATE +(Keanu)-[:ACTED_IN {roles:['Shane Falco']}]->(TheReplacements), +(Brooke)-[:ACTED_IN {roles:['Annabelle Farrell']}]->(TheReplacements), +(Gene)-[:ACTED_IN {roles:['Jimmy McGinty']}]->(TheReplacements), +(Orlando)-[:ACTED_IN {roles:['Clifford Franklin']}]->(TheReplacements), +(Howard)-[:DIRECTED]->(TheReplacements) + +CREATE (RescueDawn:Movie {title:'RescueDawn', released:2006, tagline:"Based on the extraordinary true story of one man's fight for freedom"}) +CREATE (ChristianB:Person {name:'Christian Bale', born:1974}) +CREATE (ZachG:Person {name:'Zach Grenier', born:1954}) +CREATE +(MarshallB)-[:ACTED_IN {roles:['Admiral']}]->(RescueDawn), +(ChristianB)-[:ACTED_IN {roles:['Dieter Dengler']}]->(RescueDawn), +(ZachG)-[:ACTED_IN {roles:['Squad Leader']}]->(RescueDawn), +(SteveZ)-[:ACTED_IN {roles:['Duane']}]->(RescueDawn), +(WernerH)-[:DIRECTED]->(RescueDawn) + +CREATE (TheBirdcage:Movie {title:'The Birdcage', released:1996, tagline:'Come as you are'}) +CREATE (MikeN:Person {name:'Mike Nichols', born:1931}) +CREATE +(Robin)-[:ACTED_IN {roles:['Armand Goldman']}]->(TheBirdcage), +(Nathan)-[:ACTED_IN {roles:['Albert Goldman']}]->(TheBirdcage), +(Gene)-[:ACTED_IN {roles:['Sen. Kevin Keeley']}]->(TheBirdcage), +(MikeN)-[:DIRECTED]->(TheBirdcage) + +CREATE (Unforgiven:Movie {title:'Unforgiven', released:1992, tagline:"It's a hell of a thing, killing a man"}) +CREATE (RichardH:Person {name:'Richard Harris', born:1930}) +CREATE (ClintE:Person {name:'Clint Eastwood', born:1930}) +CREATE +(RichardH)-[:ACTED_IN {roles:['English Bob']}]->(Unforgiven), +(ClintE)-[:ACTED_IN {roles:['Bill Munny']}]->(Unforgiven), +(Gene)-[:ACTED_IN {roles:['Little Bill Daggett']}]->(Unforgiven), +(ClintE)-[:DIRECTED]->(Unforgiven) + +CREATE (JohnnyMnemonic:Movie {title:'Johnny Mnemonic', released:1995, tagline:'The hottest data on earth. In the coolest head in town'}) +CREATE (Takeshi:Person {name:'Takeshi Kitano', born:1947}) +CREATE (Dina:Person {name:'Dina Meyer', born:1968}) +CREATE (IceT:Person {name:'Ice-T', born:1958}) +CREATE (RobertL:Person {name:'Robert Longo', born:1953}) +CREATE +(Keanu)-[:ACTED_IN {roles:['Johnny Mnemonic']}]->(JohnnyMnemonic), +(Takeshi)-[:ACTED_IN {roles:['Takahashi']}]->(JohnnyMnemonic), +(Dina)-[:ACTED_IN {roles:['Jane']}]->(JohnnyMnemonic), +(IceT)-[:ACTED_IN {roles:['J-Bone']}]->(JohnnyMnemonic), +(RobertL)-[:DIRECTED]->(JohnnyMnemonic) + +CREATE (CloudAtlas:Movie {title:'Cloud Atlas', released:2012, tagline:'Everything is connected'}) +CREATE (HalleB:Person {name:'Halle Berry', born:1966}) +CREATE (JimB:Person {name:'Jim Broadbent', born:1949}) +CREATE (TomT:Person {name:'Tom Tykwer', born:1965}) +CREATE (DavidMitchell:Person {name:'David Mitchell', born:1969}) +CREATE (StefanArndt:Person {name:'Stefan Arndt', born:1961}) +CREATE +(TomH)-[:ACTED_IN {roles:['Zachry', 'Dr. Henry Goose', 'Isaac Sachs', 'Dermot Hoggins']}]->(CloudAtlas), +(Hugo)-[:ACTED_IN {roles:['Bill Smoke', 'Haskell Moore', 'Tadeusz Kesselring', 'Nurse Noakes', 'Boardman Mephi', 'Old Georgie']}]->(CloudAtlas), +(HalleB)-[:ACTED_IN {roles:['Luisa Rey', 'Jocasta Ayrs', 'Ovid', 'Meronym']}]->(CloudAtlas), +(JimB)-[:ACTED_IN {roles:['Vyvyan Ayrs', 'Captain Molyneux', 'Timothy Cavendish']}]->(CloudAtlas), +(TomT)-[:DIRECTED]->(CloudAtlas), +(LillyW)-[:DIRECTED]->(CloudAtlas), +(LanaW)-[:DIRECTED]->(CloudAtlas), +(DavidMitchell)-[:WROTE]->(CloudAtlas), +(StefanArndt)-[:PRODUCED]->(CloudAtlas) + +CREATE (TheDaVinciCode:Movie {title:'The Da Vinci Code', released:2006, tagline:'Break The Codes'}) +CREATE (IanM:Person {name:'Ian McKellen', born:1939}) +CREATE (AudreyT:Person {name:'Audrey Tautou', born:1976}) +CREATE (PaulB:Person {name:'Paul Bettany', born:1971}) +CREATE (RonH:Person {name:'Ron Howard', born:1954}) +CREATE +(TomH)-[:ACTED_IN {roles:['Dr. Robert Langdon']}]->(TheDaVinciCode), +(IanM)-[:ACTED_IN {roles:['Sir Leight Teabing']}]->(TheDaVinciCode), +(AudreyT)-[:ACTED_IN {roles:['Sophie Neveu']}]->(TheDaVinciCode), +(PaulB)-[:ACTED_IN {roles:['Silas']}]->(TheDaVinciCode), +(RonH)-[:DIRECTED]->(TheDaVinciCode) + +CREATE (VforVendetta:Movie {title:'V for Vendetta', released:2006, tagline:'Freedom! Forever!'}) +CREATE (NatalieP:Person {name:'Natalie Portman', born:1981}) +CREATE (StephenR:Person {name:'Stephen Rea', born:1946}) +CREATE (JohnH:Person {name:'John Hurt', born:1940}) +CREATE (BenM:Person {name: 'Ben Miles', born:1967}) +CREATE +(Hugo)-[:ACTED_IN {roles:['V']}]->(VforVendetta), +(NatalieP)-[:ACTED_IN {roles:['Evey Hammond']}]->(VforVendetta), +(StephenR)-[:ACTED_IN {roles:['Eric Finch']}]->(VforVendetta), +(JohnH)-[:ACTED_IN {roles:['High Chancellor Adam Sutler']}]->(VforVendetta), +(BenM)-[:ACTED_IN {roles:['Dascomb']}]->(VforVendetta), +(JamesM)-[:DIRECTED]->(VforVendetta), +(LillyW)-[:PRODUCED]->(VforVendetta), +(LanaW)-[:PRODUCED]->(VforVendetta), +(JoelS)-[:PRODUCED]->(VforVendetta), +(LillyW)-[:WROTE]->(VforVendetta), +(LanaW)-[:WROTE]->(VforVendetta) + +CREATE (SpeedRacer:Movie {title:'Speed Racer', released:2008, tagline:'Speed has no limits'}) +CREATE (EmileH:Person {name:'Emile Hirsch', born:1985}) +CREATE (JohnG:Person {name:'John Goodman', born:1960}) +CREATE (SusanS:Person {name:'Susan Sarandon', born:1946}) +CREATE (MatthewF:Person {name:'Matthew Fox', born:1966}) +CREATE (ChristinaR:Person {name:'Christina Ricci', born:1980}) +CREATE (Rain:Person {name:'Rain', born:1982}) +CREATE +(EmileH)-[:ACTED_IN {roles:['Speed Racer']}]->(SpeedRacer), +(JohnG)-[:ACTED_IN {roles:['Pops']}]->(SpeedRacer), +(SusanS)-[:ACTED_IN {roles:['Mom']}]->(SpeedRacer), +(MatthewF)-[:ACTED_IN {roles:['Racer X']}]->(SpeedRacer), +(ChristinaR)-[:ACTED_IN {roles:['Trixie']}]->(SpeedRacer), +(Rain)-[:ACTED_IN {roles:['Taejo Togokahn']}]->(SpeedRacer), +(BenM)-[:ACTED_IN {roles:['Cass Jones']}]->(SpeedRacer), +(LillyW)-[:DIRECTED]->(SpeedRacer), +(LanaW)-[:DIRECTED]->(SpeedRacer), +(LillyW)-[:WROTE]->(SpeedRacer), +(LanaW)-[:WROTE]->(SpeedRacer), +(JoelS)-[:PRODUCED]->(SpeedRacer) + +CREATE (NinjaAssassin:Movie {title:'Ninja Assassin', released:2009, tagline:'Prepare to enter a secret world of assassins'}) +CREATE (NaomieH:Person {name:'Naomie Harris'}) +CREATE +(Rain)-[:ACTED_IN {roles:['Raizo']}]->(NinjaAssassin), +(NaomieH)-[:ACTED_IN {roles:['Mika Coretti']}]->(NinjaAssassin), +(RickY)-[:ACTED_IN {roles:['Takeshi']}]->(NinjaAssassin), +(BenM)-[:ACTED_IN {roles:['Ryan Maslow']}]->(NinjaAssassin), +(JamesM)-[:DIRECTED]->(NinjaAssassin), +(LillyW)-[:PRODUCED]->(NinjaAssassin), +(LanaW)-[:PRODUCED]->(NinjaAssassin), +(JoelS)-[:PRODUCED]->(NinjaAssassin) + +CREATE (TheGreenMile:Movie {title:'The Green Mile', released:1999, tagline:"Walk a mile you'll never forget."}) +CREATE (MichaelD:Person {name:'Michael Clarke Duncan', born:1957}) +CREATE (DavidM:Person {name:'David Morse', born:1953}) +CREATE (SamR:Person {name:'Sam Rockwell', born:1968}) +CREATE (GaryS:Person {name:'Gary Sinise', born:1955}) +CREATE (PatriciaC:Person {name:'Patricia Clarkson', born:1959}) +CREATE (FrankD:Person {name:'Frank Darabont', born:1959}) +CREATE +(TomH)-[:ACTED_IN {roles:['Paul Edgecomb']}]->(TheGreenMile), +(MichaelD)-[:ACTED_IN {roles:['John Coffey']}]->(TheGreenMile), +(DavidM)-[:ACTED_IN {roles:['Brutus "Brutal" Howell']}]->(TheGreenMile), +(BonnieH)-[:ACTED_IN {roles:['Jan Edgecomb']}]->(TheGreenMile), +(JamesC)-[:ACTED_IN {roles:['Warden Hal Moores']}]->(TheGreenMile), +(SamR)-[:ACTED_IN {roles:['"Wild Bill" Wharton']}]->(TheGreenMile), +(GaryS)-[:ACTED_IN {roles:['Burt Hammersmith']}]->(TheGreenMile), +(PatriciaC)-[:ACTED_IN {roles:['Melinda Moores']}]->(TheGreenMile), +(FrankD)-[:DIRECTED]->(TheGreenMile) + +CREATE (FrostNixon:Movie {title:'Frost/Nixon', released:2008, tagline:'400 million people were waiting for the truth.'}) +CREATE (FrankL:Person {name:'Frank Langella', born:1938}) +CREATE (MichaelS:Person {name:'Michael Sheen', born:1969}) +CREATE (OliverP:Person {name:'Oliver Platt', born:1960}) +CREATE +(FrankL)-[:ACTED_IN {roles:['Richard Nixon']}]->(FrostNixon), +(MichaelS)-[:ACTED_IN {roles:['David Frost']}]->(FrostNixon), +(KevinB)-[:ACTED_IN {roles:['Jack Brennan']}]->(FrostNixon), +(OliverP)-[:ACTED_IN {roles:['Bob Zelnick']}]->(FrostNixon), +(SamR)-[:ACTED_IN {roles:['James Reston, Jr.']}]->(FrostNixon), +(RonH)-[:DIRECTED]->(FrostNixon) + +CREATE (Hoffa:Movie {title:'Hoffa', released:1992, tagline:"He didn't want law. He wanted justice."}) +CREATE (DannyD:Person {name:'Danny DeVito', born:1944}) +CREATE (JohnR:Person {name:'John C. Reilly', born:1965}) +CREATE +(JackN)-[:ACTED_IN {roles:['Hoffa']}]->(Hoffa), +(DannyD)-[:ACTED_IN {roles:['Robert "Bobby" Ciaro']}]->(Hoffa), +(JTW)-[:ACTED_IN {roles:['Frank Fitzsimmons']}]->(Hoffa), +(JohnR)-[:ACTED_IN {roles:['Peter "Pete" Connelly']}]->(Hoffa), +(DannyD)-[:DIRECTED]->(Hoffa) + +CREATE (Apollo13:Movie {title:'Apollo 13', released:1995, tagline:'Houston, we have a problem.'}) +CREATE (EdH:Person {name:'Ed Harris', born:1950}) +CREATE (BillPax:Person {name:'Bill Paxton', born:1955}) +CREATE +(TomH)-[:ACTED_IN {roles:['Jim Lovell']}]->(Apollo13), +(KevinB)-[:ACTED_IN {roles:['Jack Swigert']}]->(Apollo13), +(EdH)-[:ACTED_IN {roles:['Gene Kranz']}]->(Apollo13), +(BillPax)-[:ACTED_IN {roles:['Fred Haise']}]->(Apollo13), +(GaryS)-[:ACTED_IN {roles:['Ken Mattingly']}]->(Apollo13), +(RonH)-[:DIRECTED]->(Apollo13) + +CREATE (Twister:Movie {title:'Twister', released:1996, tagline:"Don't Breathe. Don't Look Back."}) +CREATE (PhilipH:Person {name:'Philip Seymour Hoffman', born:1967}) +CREATE (JanB:Person {name:'Jan de Bont', born:1943}) +CREATE +(BillPax)-[:ACTED_IN {roles:['Bill Harding']}]->(Twister), +(HelenH)-[:ACTED_IN {roles:['Dr. Jo Harding']}]->(Twister), +(ZachG)-[:ACTED_IN {roles:['Eddie']}]->(Twister), +(PhilipH)-[:ACTED_IN {roles:['Dustin "Dusty" Davis']}]->(Twister), +(JanB)-[:DIRECTED]->(Twister) + +CREATE (CastAway:Movie {title:'Cast Away', released:2000, tagline:'At the edge of the world, his journey begins.'}) +CREATE (RobertZ:Person {name:'Robert Zemeckis', born:1951}) +CREATE +(TomH)-[:ACTED_IN {roles:['Chuck Noland']}]->(CastAway), +(HelenH)-[:ACTED_IN {roles:['Kelly Frears']}]->(CastAway), +(RobertZ)-[:DIRECTED]->(CastAway) + +CREATE (OneFlewOvertheCuckoosNest:Movie {title:"One Flew Over the Cuckoo's Nest", released:1975, tagline:"If he's crazy, what does that make you?"}) +CREATE (MilosF:Person {name:'Milos Forman', born:1932}) +CREATE +(JackN)-[:ACTED_IN {roles:['Randle McMurphy']}]->(OneFlewOvertheCuckoosNest), +(DannyD)-[:ACTED_IN {roles:['Martini']}]->(OneFlewOvertheCuckoosNest), +(MilosF)-[:DIRECTED]->(OneFlewOvertheCuckoosNest) + +CREATE (SomethingsGottaGive:Movie {title:"Something's Gotta Give", released:2003}) +CREATE (DianeK:Person {name:'Diane Keaton', born:1946}) +CREATE (NancyM:Person {name:'Nancy Meyers', born:1949}) +CREATE +(JackN)-[:ACTED_IN {roles:['Harry Sanborn']}]->(SomethingsGottaGive), +(DianeK)-[:ACTED_IN {roles:['Erica Barry']}]->(SomethingsGottaGive), +(Keanu)-[:ACTED_IN {roles:['Julian Mercer']}]->(SomethingsGottaGive), +(NancyM)-[:DIRECTED]->(SomethingsGottaGive), +(NancyM)-[:PRODUCED]->(SomethingsGottaGive), +(NancyM)-[:WROTE]->(SomethingsGottaGive) + +CREATE (BicentennialMan:Movie {title:'Bicentennial Man', released:1999, tagline:"One robot's 200 year journey to become an ordinary man."}) +CREATE (ChrisC:Person {name:'Chris Columbus', born:1958}) +CREATE +(Robin)-[:ACTED_IN {roles:['Andrew Marin']}]->(BicentennialMan), +(OliverP)-[:ACTED_IN {roles:['Rupert Burns']}]->(BicentennialMan), +(ChrisC)-[:DIRECTED]->(BicentennialMan) + +CREATE (CharlieWilsonsWar:Movie {title:"Charlie Wilson's War", released:2007, tagline:"A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire."}) +CREATE (JuliaR:Person {name:'Julia Roberts', born:1967}) +CREATE +(TomH)-[:ACTED_IN {roles:['Rep. Charlie Wilson']}]->(CharlieWilsonsWar), +(JuliaR)-[:ACTED_IN {roles:['Joanne Herring']}]->(CharlieWilsonsWar), +(PhilipH)-[:ACTED_IN {roles:['Gust Avrakotos']}]->(CharlieWilsonsWar), +(MikeN)-[:DIRECTED]->(CharlieWilsonsWar) + +CREATE (ThePolarExpress:Movie {title:'The Polar Express', released:2004, tagline:'This Holiday Season... Believe'}) +CREATE +(TomH)-[:ACTED_IN {roles:['Hero Boy', 'Father', 'Conductor', 'Hobo', 'Scrooge', 'Santa Claus']}]->(ThePolarExpress), +(RobertZ)-[:DIRECTED]->(ThePolarExpress) + +CREATE (ALeagueofTheirOwn:Movie {title:'A League of Their Own', released:1992, tagline:'Once in a lifetime you get a chance to do something different.'}) +CREATE (Madonna:Person {name:'Madonna', born:1954}) +CREATE (GeenaD:Person {name:'Geena Davis', born:1956}) +CREATE (LoriP:Person {name:'Lori Petty', born:1963}) +CREATE (PennyM:Person {name:'Penny Marshall', born:1943}) +CREATE +(TomH)-[:ACTED_IN {roles:['Jimmy Dugan']}]->(ALeagueofTheirOwn), +(GeenaD)-[:ACTED_IN {roles:['Dottie Hinson']}]->(ALeagueofTheirOwn), +(LoriP)-[:ACTED_IN {roles:['Kit Keller']}]->(ALeagueofTheirOwn), +(RosieO)-[:ACTED_IN {roles:['Doris Murphy']}]->(ALeagueofTheirOwn), +(Madonna)-[:ACTED_IN {roles:['"All the Way" Mae Mordabito']}]->(ALeagueofTheirOwn), +(BillPax)-[:ACTED_IN {roles:['Bob Hinson']}]->(ALeagueofTheirOwn), +(PennyM)-[:DIRECTED]->(ALeagueofTheirOwn) + +CREATE (PaulBlythe:Person {name:'Paul Blythe'}) +CREATE (AngelaScope:Person {name:'Angela Scope'}) +CREATE (JessicaThompson:Person {name:'Jessica Thompson'}) +CREATE (JamesThompson:Person {name:'James Thompson'}) + +CREATE +(JessicaThompson)-[:REVIEWED {summary:'An amazing journey', rating:95}]->(CloudAtlas), +(JessicaThompson)-[:REVIEWED {summary:'Silly, but fun', rating:65}]->(TheReplacements), +(JamesThompson)-[:REVIEWED {summary:'The coolest football movie ever', rating:100}]->(TheReplacements), +(AngelaScope)-[:REVIEWED {summary:'Pretty funny at times', rating:62}]->(TheReplacements), +(JessicaThompson)-[:REVIEWED {summary:'Dark, but compelling', rating:85}]->(Unforgiven), +(JessicaThompson)-[:REVIEWED {summary:"Slapstick redeemed only by the Robin Williams and Gene Hackman's stellar performances", rating:45}]->(TheBirdcage), +(JessicaThompson)-[:REVIEWED {summary:'A solid romp', rating:68}]->(TheDaVinciCode), +(JamesThompson)-[:REVIEWED {summary:'Fun, but a little far fetched', rating:65}]->(TheDaVinciCode), +(JessicaThompson)-[:REVIEWED {summary:'You had me at Jerry', rating:92}]->(JerryMaguire) +---- +//// + +== Finding nodes + +The `MATCH` clause is used to find a specific pattern in the graph, such as a specific node. +The `RETURN` clause specifies what of the found graph pattern to return. + +For example, this query will find the nodes with `Person` label and the name `Keanu Reeves`, and return the `name` and `born` properties of the found nodes: + +.Query +[source, cypher] +---- +MATCH (keanu:Person {name:'Keanu Reeves'}) +RETURN keanu.name AS name, keanu.born AS born +---- + +.Result +[role="queryresult",options="header,footer",cols="2*= 1980 AND bornInEighties.born < 1990 +RETURN bornInEighties.name as name, bornInEighties.born as born ORDER BY born DESC +---- + +.Result +[role="queryresult",options="header,footer",cols="2*(m:Movie) +Return type(r), m.title AS movies +---- + +The graph returned shows that he has 13 outgoing relationships connected to 12 different `Movie` nodes (12 have the `ACTED_IN` type and one has the `DIRECTED` type). + +image::introduction_example1.svg[width="500",role="middle"] + +.Result +[role="queryresult",options="header,footer",cols="2*(m:Movie) +Return type(r), m.title AS movies +---- + +.Result +[role="queryresult",options="header,footer",cols="2*(m:Movie)<-[:ACTED_IN]-(coActors:Person), + (coActors)-[:ACTED_IN]->(m2:Movie)<-[:ACTED_IN]-(tom:Person {name:'Tom Hanks'}) +RETURN DISTINCT coActors.name AS coActors +---- + +.Result +[role="queryresult",options="header,footer",cols="1* Introduction to Cypher] + +The examples below use the clauses xref:clauses/match.adoc[] and clauses/return.adoc[] to find and display the desired graph patterns. +To learn more about these and many other Cypher clauses, see the section on xref:clauses/index.adoc[]. + +== Nodes + +The data entities in a Neo4j knowledge graph are referred to as nodes. +The below query demonstrates the fundamental concepts when querying nodes with Cypher: + +[source, cypher] +---- +MATCH (p:Person {name:'Keanu Reeves'}) +RETURN p.born AS birthYear +---- + +Nodes are referred to in Cypher using brackets`()`. +Everything inside the brackets is referred to as a node pattern. + +This includes: +* A `Person` label. +A node may have none, one, or several node labels, and they are used as tags to enable the retrieval of specific data entities. +It also includes a `name` property set to `Keanu Reeves`. +Properties are defined within curly brackets, `{}`, and are used to provide nodes with specific information. + +The node pattern includes a variable, `p`. +Variables are used to bind a particular node pattern in order that they may be retrieved by subsequent clauses. + +In this query, the first `MATCH` clause finds all `Person`nodes in the graph with the `name` property set to `Keanu Reeves`, and binds them to the variable `p`. +The variable `p` is then passed along to the next `RETURN` clause which returns the value of a different property (`born`) belonging to the same bound node. + +== Relationships + +Nodes in a graph can be connected with relationships. +A relationship must have a start node and an end node. +Relationships are represented in Cypher with dashes (`-`) and arrows (e.g. `-->`). + +The below query demonstrates the fundamental concepts when querying relationships with Cypher: + +[source, cypher] +---- +MATCH (p:Person {name: 'Keanu Reeves'})-[r:ACTED_IN {roles: "Neo"}]->(c:Movie) +RETURN count(r) +---- + +Unlike nodes, information within a relationship pattern must be enclosed by square brackets. +In this case, the bound relationship pattern from the `Person` nodes to the `Car` nodes in the graph must have the relationship type `OWNS` with the property `since` set to earlier than `2022`. + +Note that while nodes can have several labels, relationships can only have one type. + +== Patterns + +Graph patterns consists of nodes and relationships and sits at the very core of Cypher. +By applying a declarative path pattern in a query, you can find simple or complex patterns of fixed or varied lengths. +For example: + + + +(m:Person)-[:KNOWS]->(n:Person) WHERE m.born < n.born){1,5} + diff --git a/modules/ROOT/pages/queries/expressions.adoc b/modules/ROOT/pages/queries/expressions.adoc new file mode 100644 index 000000000..5ea040ce2 --- /dev/null +++ b/modules/ROOT/pages/queries/expressions.adoc @@ -0,0 +1,67 @@ += Note on allowed expressions in Cypher queries + +An expression in Cypher can be: + +* A decimal (integer or float) literal: `13`, `-40000`, `3.14`. +* A decimal (integer or float) literal in scientific notation: `6.022E23`. +* A hexadecimal integer literal (starting with `0x`): `0x13af`, `0xFC3A9`, `-0x66eff`. +* An octal integer literal (starting with `0o`): `0o1372`, `-0o5671`. +* A string literal: `'Hello'`, `"World"`. +* A float literal: `Inf`, `Infinity`, `NaN` +* A boolean literal: `true`, `false`. +* A variable: `n`, `x`, `rel`, `myFancyVariable`, `++`A name with weird stuff in it[]!`++`. +* A property: `n.prop`, `x.prop`, `rel.thisProperty`, `++myFancyVariable.`(weird property name)`++`. +* A dynamic property: `n["prop"]`, `rel[n.city + n.zip]`, `map[coll[0]]`. +* A parameter: `$param`, `$0`. +* A list of expressions: `['a', 'b']`, `[1, 2, 3]`, `['a', 2, n.property, $param]`, `[]`. +* A function call: `length(p)`, `nodes(p)`. +* An aggregate function: `avg(x.prop)`, `+count(*)+`. +* A path-pattern: `+(a)-[r]->(b)+`, `+(a)-[r]-(b)+`, `+(a)--(b)+`, `+(a)-->()<--(b)+`. +* An operator application: `1 + 2`, `3 < 4`. +* A predicate expression is an expression that returns `true` or `false`: `a.prop = 'Hello'`, `length(p) > 10`, `a.name IS NOT NULL`. +* A special case of predicates are label and relationship type expressions: `(n:A|B)`, `()-[r:R1|R2]->()`. +* A subquery expression. For example: +`EXISTS { + MATCH (n)-[r]->(p) + WHERE p.name = 'Sven' +}`. +* A regular expression: `a.name =~ 'Tim.*'`. +* A case-sensitive string matching expression: `a.surname STARTS WITH 'Sven'`, `a.surname ENDS WITH 'son'` or `a.surname CONTAINS 'son'`. +* A `CASE` expression. + +[NOTE] +==== +Most expressions in Cypher evaluate to `null` if any of their inner expressions are `null`. +Notable exceptions are the operators `IS NULL`, `IS NOT NULL`, and the type predicate expression. +==== + +[[expressions-string-literals]] +== Note on string literals + +String literals can contain the following escape sequences: + +[options="header", cols=">1,<2"] +|=================== +|Escape sequence|Character +|`\t`|Tab +|`\b`|Backspace +|`\n`|Newline +|`\r`|Carriage return +|`\f`|Form feed +|`\'`|Single quote +|`\"`|Double quote +|`\\`|Backslash +|`\uxxxx`|Unicode UTF-16 code point (4 hex digits must follow the `\u`) +|=================== + +[NOTE] +==== +Using regular expressions with unsanitized user input makes you vulnerable to Cypher injection. +Consider using xref:syntax/parameters.adoc[parameters] instead. +==== + +[[expressions-number-literals]] +== Note on number literals + +Any number literal may contain an underscore `_` between digits. +There may be an underscore between the `0x` or `0o` and the digits for hexadecimal and octal literals. diff --git a/modules/ROOT/pages/queries/index.adoc b/modules/ROOT/pages/queries/index.adoc new file mode 100644 index 000000000..0cd382129 --- /dev/null +++ b/modules/ROOT/pages/queries/index.adoc @@ -0,0 +1,7 @@ += Queries + +* xref:queries/concepts.adoc[] +* xref:queries/basic.adoc[] +* xref:queries/advanced.adoc[] +* xref:queries/case.adoc[] +* xref:queries/expressions.adoc[] \ No newline at end of file diff --git a/modules/ROOT/pages/clauses/call-subquery.adoc b/modules/ROOT/pages/subqueries/call-subquery.adoc similarity index 100% rename from modules/ROOT/pages/clauses/call-subquery.adoc rename to modules/ROOT/pages/subqueries/call-subquery.adoc diff --git a/modules/ROOT/pages/subqueries/collect.adoc b/modules/ROOT/pages/subqueries/collect.adoc new file mode 100644 index 000000000..f630fce93 --- /dev/null +++ b/modules/ROOT/pages/subqueries/collect.adoc @@ -0,0 +1,289 @@ +[[collect]] += `COLLECT` subqueries + +_This feature was introduced in Neo4j 5.6._ + +A `COLLECT` subquery expression can be used to create a list with the rows returned by a given subquery. + +Any non-writing query is allowed. +`COLLECT` subqueries differ from `COUNT` and `EXISTS` subqueries in that the final `RETURN` clause is mandatory. +The `RETURN` clause must return exactly one column. + +[[collect-simple]] +== Simple `COLLECT` subquery + +Variables introduced by the outside scope can be used in the `COLLECT` subquery without importing them. +In this regard, `COLLECT` 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 the dog named `Ozzy`: + +[source, cypher] +---- +MATCH (person:Person) +WHERE 'Ozzy' IN COLLECT { MATCH (person)-[:HAS_DOG]->(dog:Dog) RETURN dog.name } +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(dog:Dog) + WHERE r.since > 2017 + RETURN dog.name +} as youngDogs +---- + +[role="queryresult",options="header,footer",cols="2*(dog:Dog) + RETURN dog.name AS petName + UNION + MATCH (person)-[:HAS_CAT]->(cat:Cat) + RETURN cat.name AS petName + } AS petNames +---- + +[role="queryresult",options="header,footer",cols="2*(d:Dog {name: name}) + RETURN d.name +} as dogsOfTheYear +---- + +.Error message +[source, output, role="noheader"] +---- +The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 92)) +---- + +New variables can be introduced into the subquery, as long as they use a different identifier. +In the example below, 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. + +[source, cypher] +---- +MATCH (person:Person) +RETURN person.name AS name, COLLECT { + WITH 2018 AS yearOfTheDog + MATCH (person)-[r:HAS_DOG]->(d:Dog) + WHERE r.since = yearOfTheDog + RETURN d.name +} as dogsOfTheYear +---- + +[role="queryresult",options="header,footer",cols="2*(d:Dog) + MATCH (d)-[:HAS_TOY]->(t:Toy) + RETURN t.name + } as toyNames +---- + +[role="queryresult",options="header,footer",cols="2*(d:Dog) RETURN d.name } +RETURN person.dogNames as dogNames +---- + +[role="queryresult",options="header,footer",cols="1*(d:Dog) RETURN d.name } = [] THEN "No Dogs " + person.name + ELSE person.name + END AS result +---- + +[role="queryresult",options="header,footer",cols="1*(d:Dog) RETURN d.name } AS dogNames, + avg(person.age) AS averageAge + ORDER BY dogNames +---- + +[role="queryresult",options="header,footer",cols="2*(:Dog) } > 1 +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(dog:Dog) + WHERE person.name = dog.name +} = 1 +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(dog:Dog) + RETURN dog.name AS petName + UNION + MATCH (person)-[:HAS_CAT]->(cat:Cat) + RETURN cat.name AS petName + } AS numPets +---- + +[role="queryresult",options="header,footer",cols="2*(d:Dog) + WHERE d.name = name +} = 1 +RETURN person.name AS name +---- + +.Error message +[source, output, role="noheader"] +---- +The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 90)) +---- + +New variables can be introduced into the subquery, as long as they use a different identifier. +In the example below, 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. + +[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 +---- + +[role="queryresult",options="header,footer",cols="1*(:Dog) } as howManyDogs + +---- + +[role="queryresult",options="header,footer",cols="2*(:Dog) } +RETURN person.howManyDogs as howManyDogs + +---- + +[role="queryresult",options="header,footer",cols="1*(:Dog) } > 1 THEN "Doglover " + person.name + ELSE person.name + END AS result + +---- + +[role="queryresult",options="header,footer",cols="1*(:Dog) } AS numDogs, + avg(person.age) AS averageAge + ORDER BY numDogs + +---- + +[role="queryresult",options="header,footer",cols="2*(:Dog) + RETURN person.name +} = 1 +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(:Dog) +} +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(dog:Dog) + WHERE person.name = dog.name +} +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(dog:Dog) + WHERE EXISTS { + MATCH (dog)-[:HAS_TOY]->(toy:Toy) + WHERE toy.name = 'Banana' + } +} +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(:Dog) +} AS hasDog +---- + +[role="queryresult",options="header,footer",cols="2*(:Dog) + UNION + MATCH (person)-[:HAS_CAT]->(:Cat) + } AS hasPet +---- + +[role="queryresult",options="header,footer",cols="2*(d:Dog) + WHERE d.name = name +} +RETURN person.name AS name +---- + +.Error message +[source, output, role="noheader"] +---- +The variable `name` is shadowing a variable with the same name from the outer scope and needs to be renamed (line 4, column 20 (offset: 90)) +---- + +New variables can be introduced into the subquery, as long as they use a different identifier. +In the example below, 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. + +[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 +---- + +[role="queryresult",options="header,footer",cols="1*(:Dog) + RETURN person.name +} +RETURN person.name AS name +---- + +[role="queryresult",options="header,footer",cols="1*(: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[] + + + + + diff --git a/modules/ROOT/pages/values-and-types/index.adoc b/modules/ROOT/pages/values-and-types/index.adoc index 4978093d5..8f429f49c 100644 --- a/modules/ROOT/pages/values-and-types/index.adoc +++ b/modules/ROOT/pages/values-and-types/index.adoc @@ -13,4 +13,5 @@ More information about the data values and types supported by Cypher can be foun * xref::values-and-types/working-with-null.adoc[] * xref::values-and-types/lists.adoc[] * xref::values-and-types/maps.adoc[] -* xref::values-and-types/casting-data.adoc[] \ No newline at end of file +* xref::values-and-types/casting-data.adoc[] +* xref::values-and-types/type-predicate.adoc[] \ No newline at end of file diff --git a/modules/ROOT/pages/expressions/type-predicate.adoc b/modules/ROOT/pages/values-and-types/type-predicate.adoc similarity index 100% rename from modules/ROOT/pages/expressions/type-predicate.adoc rename to modules/ROOT/pages/values-and-types/type-predicate.adoc From 03d63fc0c6cb9a9da3ccb3e775b9a87c9310b4c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 6 Sep 2023 11:15:50 +0200 Subject: [PATCH 04/24] add core concepts page, further rearchitecture --- modules/ROOT/content-nav.adoc | 12 ++-- modules/ROOT/pages/queries/basic.adoc | 16 +++--- modules/ROOT/pages/queries/concepts.adoc | 70 +++++++++++++++--------- 3 files changed, 59 insertions(+), 39 deletions(-) diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index cf758da46..fd9c84514 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -7,16 +7,9 @@ * xref:queries/index.adoc[] ** xref:queries/concepts.adoc[] ** xref:queries/basic.adoc[] -** xref:queries/advanced.adoc[] ** xref:queries/case.adoc[] ** xref:queries/expressions.adoc[] -* xref:subqueries/index.adoc[] -** xref:subqueries/call-subquery.adoc[] -** xref:subqueries/existential.adoc[] -** xref:subqueries/count.adoc[] -** xref:subqueries/collect.adoc[] - * xref:clauses/index.adoc[] ** xref:clauses/clause_composition.adoc[] ** xref:clauses/match.adoc[] @@ -45,6 +38,11 @@ ** xref:clauses/transaction-clauses.adoc#query-listing-transactions[SHOW TRANSACTIONS] ** xref:clauses/transaction-clauses.adoc#query-terminate-transactions[TERMINATE TRANSACTIONS] +* xref:subqueries/index.adoc[] +** xref:subqueries/call-subquery.adoc[] +** xref:subqueries/existential.adoc[] +** xref:subqueries/count.adoc[] +** xref:subqueries/collect.adoc[] * xref:patterns/index.adoc[] ** xref:patterns/concepts.adoc[] diff --git a/modules/ROOT/pages/queries/basic.adoc b/modules/ROOT/pages/queries/basic.adoc index 65c87c460..be4622480 100644 --- a/modules/ROOT/pages/queries/basic.adoc +++ b/modules/ROOT/pages/queries/basic.adoc @@ -1,9 +1,11 @@ [[cypher-tutorial]] = Basic queries -:description: This section provides an overview of Cypher, and goes through a short Cypher tutorial based on the Neo4j movie database. +:description: This section provides an overview of some basic Cypher queries using the Neo4j movie database. -In this short tutorial, users will learn how to create, query, and delete a property graph database using Cypher. -The tutorial uses the xref:https://github.com/neo4j-graph-examples/movies/tree/main/documentation[Neo4j movie database]. +This page contains information about how to create, query, and delete a graph database using Cypher. +For more advanced queries, see the section on xref:subqueries/index.adoc[]. + +The examples below uses the publicly available xref:https://github.com/neo4j-graph-examples/movies/tree/main/documentation[Neo4j movie database]. == Creating a data model @@ -16,13 +18,13 @@ image::introduction_schema.svg[width="800",role="middle"] It includes two types of node labels: -* `Person` nodes, which have the following properties: `name` (string) and `born` (integer). -* `Movie` nodes, which have the following properties: `title` (string), `released` (integer), and `tagline` (string). +* `Person` nodes, which have the following properties: `name` and `born`. +* `Movie` nodes, which have the following properties: `title`, `released`, and `tagline`. The data model also contains five different relationship types between the `Person` and `Movie` nodes: `ACTED_IN`, `DIRECTED`, `PRODUCED`, `WROTE`, and `REVIEWED`. Two of the relationship types have properties: -* The `ACTED_IN` relationship type, which has the `roles` property (string). -* The `REVIEWED` relationship type, which has a `summary` property (string) and a `rating` property (float). +* The `ACTED_IN` relationship type, which has the `roles` property. +* The `REVIEWED` relationship type, which has a `summary` property and a `rating` property. _To learn more about data modelling for graph databases, enroll in the free https://graphacademy.neo4j.com/courses/modeling-fundamentals/[Graph Data Modelling Fundamentals] course offered by GraphAcademy._ diff --git a/modules/ROOT/pages/queries/concepts.adoc b/modules/ROOT/pages/queries/concepts.adoc index efab44bbe..024a5e972 100644 --- a/modules/ROOT/pages/queries/concepts.adoc +++ b/modules/ROOT/pages/queries/concepts.adoc @@ -1,3 +1,6 @@ +:description: this page describes the three fundamental concepts of a Cypher query: nodes, relationships, and patterns. + +[[core-concepts]] = Core concepts Cypher queries match patterns in a graph. @@ -5,61 +8,78 @@ This page provides a brief overview of three core concepts used to construct Cyp For a more detailed overview, see the link:{neo4j-docs-base-uri}/getting-started/{page-version}/cypher-intro/[Getting Started Guide -> Introduction to Cypher] -The examples below use the clauses xref:clauses/match.adoc[] and clauses/return.adoc[] to find and display the desired graph patterns. +The examples below use the clauses xref:clauses/match.adoc[] and xref:clauses/return.adoc[] to find and return the desired graph patterns. To learn more about these and many other Cypher clauses, see the section on xref:clauses/index.adoc[]. +[[core-concepts-nodes]] == Nodes -The data entities in a Neo4j knowledge graph are referred to as nodes. -The below query demonstrates the fundamental concepts when querying nodes with Cypher: +The data entities in a Neo4j knowledge graph are called nodes. +Nodes are referred to in Cypher using brackets`()`. [source, cypher] ---- -MATCH (p:Person {name:'Keanu Reeves'}) -RETURN p.born AS birthYear +MATCH (n:Person {name:'Anna'}) +RETURN n.born AS birthYear ---- -Nodes are referred to in Cypher using brackets`()`. -Everything inside the brackets is referred to as a node pattern. +In the above example, the node includes the following: -This includes: -* A `Person` label. +* A `Person` *label*. A node may have none, one, or several node labels, and they are used as tags to enable the retrieval of specific data entities. -It also includes a `name` property set to `Keanu Reeves`. -Properties are defined within curly brackets, `{}`, and are used to provide nodes with specific information. - -The node pattern includes a variable, `p`. -Variables are used to bind a particular node pattern in order that they may be retrieved by subsequent clauses. +* A `name` *property* set to `Anna`. +Properties are defined within curly brackets, `{}`, and are used to provide nodes with specific information, which can then also be matched for and further improve the ability to retrieve data. +* A *variable*, `n`. +Variables are used to bind specified nodes in order that they may be retrieved in subsequent clauses. -In this query, the first `MATCH` clause finds all `Person`nodes in the graph with the `name` property set to `Keanu Reeves`, and binds them to the variable `p`. -The variable `p` is then passed along to the next `RETURN` clause which returns the value of a different property (`born`) belonging to the same bound node. +In this example, the first `MATCH` clause finds all `Person` nodes in the graph with the `name` property set to `Anna`, and binds them to the variable `n`. +The variable `n` is then passed along to the subsequent `RETURN` clause, which returns the value of a different property (`born`) belonging to the same bound node. +[[core-concepts-relationships]] == Relationships Nodes in a graph can be connected with relationships. A relationship must have a start node and an end node. -Relationships are represented in Cypher with dashes (`-`) and arrows (e.g. `-->`). - -The below query demonstrates the fundamental concepts when querying relationships with Cypher: +Relationships are represented in Cypher with dashes (`-`) and arrows (e.g. `-->`) indicating the direction of a relationship. [source, cypher] ---- -MATCH (p:Person {name: 'Keanu Reeves'})-[r:ACTED_IN {roles: "Neo"}]->(c:Movie) -RETURN count(r) +MATCH (n:Person {name: 'Anna'})-[r:KNOWS {since < 2020}]->(friend:Person}) +RETURN count(friend) As numberOfFriends ---- Unlike nodes, information within a relationship pattern must be enclosed by square brackets. -In this case, the bound relationship pattern from the `Person` nodes to the `Car` nodes in the graph must have the relationship type `OWNS` with the property `since` set to earlier than `2022`. +In this case, all outgoing relationships from the start node must be of the type `KNOWS` and have the property `since` set to less than `2020`. +The xref:functions/aggregating.adoc#functions-count[count()] function is used in the `RETURN` clause to count all nodes bound by the `friend` variable in the preceding `MATCH` clause (i.e. how many friends Anna has known since before 2020). Note that while nodes can have several labels, relationships can only have one type. +[[core-concepts-patterns]] == Patterns -Graph patterns consists of nodes and relationships and sits at the very core of Cypher. +Graph patterns consists of nodes and relationships. +Exploring these graph patterns sits at the very core of Cypher. By applying a declarative path pattern in a query, you can find simple or complex patterns of fixed or varied lengths. -For example: +[source, cypher] +---- +MATCH (n:Person {name: 'Anna'})-[:KNOWS]-{1,5}(friends:Person WHERE n.born < friends.born) +RETURN DISTINCT friends.name AS youngerFriends +---- + +This example uses a xref:patterns/concepts.adoc#quantified-relationships[quantified relationship] to find all paths of a specific length (between `1` and `5` hops) traversing only relationships of type `KNOWS` from the start node `Anna` to other `Person` nodes with a lower `born` property (as defined by the xref:clauses/where.adoc[] clause). +The xref:syntax/operators.adoc#syntax-using-the-distinct-operator[DISTINCT] operator is used to ensure that the `RETURN` clause only returns unique nodes. + +Patterns can also be assigned variables. +For example, the below query binds a whole path pattern, which matches the xref:patterns/concepts.adoc#shortest-path[shortest path] from `Anna` to another `Person` node in the graph with the `nationality` property set to `Canadian`. +In this case, the `RETURN` clause returns the full path pattern between the two nodes. +[source, cypher] +---- +MATCH p = shortestPath((n:Person {name:'Anna'})-[:KNOWS*]-(canadian:Person {nationality: 'Canadian'})) +RETURN p +---- -(m:Person)-[:KNOWS]->(n:Person) WHERE m.born < n.born){1,5} +For more information about graph patterns, see the section on xref:patterns/index.adoc[]. +More information about constructing basic queries can be found in the xref:queries/basic.adoc[next section]. \ No newline at end of file From 6d3714f15f93738496d44e3c39dd811133d4c8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:12:06 +0200 Subject: [PATCH 05/24] fix case --- modules/ROOT/images/case_graph.svg | 1 + modules/ROOT/images/graph3.svg | 98 --------------- modules/ROOT/pages/queries/case.adoc | 180 ++++++++++++--------------- 3 files changed, 78 insertions(+), 201 deletions(-) create mode 100644 modules/ROOT/images/case_graph.svg delete mode 100644 modules/ROOT/images/graph3.svg diff --git a/modules/ROOT/images/case_graph.svg b/modules/ROOT/images/case_graph.svg new file mode 100644 index 000000000..98bfc6ba1 --- /dev/null +++ b/modules/ROOT/images/case_graph.svg @@ -0,0 +1 @@ +KNOWSKNOWSKNOWSKNOWSMARRIEDPersonname:'Alice'age:38eyes:'brown'Personname:'Charlie'age:53eyes:'green'Personname:'Bob'age:25eyes:'blue'Personname:'Daniel'eyes:'brown'Personname:'Eskil'age:41eyes:'blue' diff --git a/modules/ROOT/images/graph3.svg b/modules/ROOT/images/graph3.svg deleted file mode 100644 index a8a1c2697..000000000 --- a/modules/ROOT/images/graph3.svg +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - -L - - - -N0 - -A - -name = 'Alice' -age = 38 -eyes = 'brown' - - - -N2 - -C - -name = 'Charlie' -age = 53 -eyes = 'green' - - - -N0->N2 - - -KNOWS - - - -N1 - -B - -name = 'Bob' -age = 25 -eyes = 'blue' - - - -N0->N1 - - -KNOWS - - - -N3 - -D - -name = 'Daniel' -eyes = 'brown' - - - -N2->N3 - - -KNOWS - - - -N1->N3 - - -KNOWS - - - -N4 - -E - -eyes = 'blue' -array = ['one', 'two', 'three'] -name = 'Eskil' -age = 41 - - - -N1->N4 - - -MARRIED - - - diff --git a/modules/ROOT/pages/queries/case.adoc b/modules/ROOT/pages/queries/case.adoc index 36b1b2294..94002e1a1 100644 --- a/modules/ROOT/pages/queries/case.adoc +++ b/modules/ROOT/pages/queries/case.adoc @@ -1,46 +1,48 @@ -[[expressions-case]] +[[case]] = Conditional expressions (CASE) -Generic conditional expressions may be expressed using the `CASE` construct. +Generic conditional expressions can be expressed in Cypher using the `CASE` construct. Two variants of `CASE` exist within Cypher: the simple form, which allows an expression to be compared against multiple values, and the generic form, which allows multiple conditional statements to be expressed. [NOTE] ==== -`CASE` can only be used as part of `RETURN` or `WITH` if you want to use the result in the succeeding clause or statement. +`CASE` can only be used as part of xref:clauses/return.adoc[] or xref:clauses/with.adoc[] if you want to use the result in a subsequent clause. ==== -[[expressions-case-example]] +[[case-example]] == Example graph The following graph is used for the examples below: -//// +image:case_graph.svg[width="500",role="middle"] + +To recreate the graph, run the following query against an empty Neo4j database: + [source, cypher, role=test-setup] ---- CREATE - (alice:A {name:'Alice', age: 38, eyes: 'brown'}), - (bob:B {name: 'Bob', age: 25, eyes: 'blue'}), - (charlie:C {name: 'Charlie', age: 53, eyes: 'green'}), - (daniel:D {name: 'Daniel', eyes: 'brown'}), - (eskil:E {name: 'Eskil', age: 41, eyes: 'blue', array: ['one', 'two', 'three']}), + (alice:Person {name:'Alice', age: 38, eyes: 'brown'}), + (bob:Person {name: 'Bob', age: 25, eyes: 'blue'}), + (charlie:Person {name: 'Charlie', age: 53, eyes: 'green'}), + (daniel:Person {name: 'Daniel', eyes: 'brown'}), + (eskil:Person {name: 'Eskil', age: 41, eyes: 'blue'}), (alice)-[:KNOWS]->(bob), (alice)-[:KNOWS]->(charlie), (bob)-[:KNOWS]->(daniel), (charlie)-[:KNOWS]->(daniel), (bob)-[:MARRIED]->(eskil) ---- -//// - -image:graph3.svg[] -[[expressions-case-simple]] +[[case-simple]] == Simple `CASE` The simple `CASE` form is used to compare an expression against multiple values. -The expression is calculated, and compared in order with the `WHEN` clauses until a match is found. -If no match is found, the expression in the `ELSE` clause is returned. -However, if there is no `ELSE` case and no match is found, `null` will be returned. +The expressions are evaluated by the `WHEN` operator until a `MATCH` is found. +If no match is found, the expression in the `ELSE` operator is returned. +If there is no `ELSE` case and no match is found, `null` will be returned. +[[case-simple-syntax]] +=== Syntax [source, syntax] ---- @@ -51,7 +53,6 @@ CASE test END ---- - *Arguments:* [options="header"] |=== @@ -70,6 +71,8 @@ END | If no match is found, `default` is returned. |=== +[[case-simple-examples]] +=== Example [source, cypher] ---- @@ -84,24 +87,25 @@ END AS result [role="queryresult",options="header,footer",cols="1*+ -| +"Eskil"+ | +31+ +| n.name | age_10_years_ago +| "Alice" | 28 +| "Bob" | 15 +| "Charlie" | 43 +| "Daniel" | null +| "Eskil" | 31 2+d|Rows: 5 |=== -The corrected query, behaving as expected, is given by the following generic `CASE` form: +The below query is rewritten to use the generic `CASE` form: [source, cypher] ---- MATCH (n) RETURN n.name, CASE - WHEN n.age IS NULL THEN -1 + WHEN null THEN -1 ELSE n.age - 10 END AS age_10_years_ago ---- -We now see that the `age_10_years_ago` correctly returns `-1` for the node named `Daniel`. +In this example, because each row is evaluated individually in the generic `CASE` form, the `WHEN null THEN -1` branch evaluates to `true`. +The resulting `age` of `Daniel` is therefore `-1`. [role="queryresult",options="header,footer",cols="2*+ -| +"Eskil"+ | +31+ +| n.name | n.colourCode +| "Alice" | 2 +| "Bob" | 1 +| "Charlie" | 3 +| "Daniel" | 2 +| "Eskil" | 1 2+d|Rows: 5 |=== -====== +For more information about using the `SET` clause, see xref::clauses/set.adoc[SET]. From b63168d08bf19c70ff2d4c977550b5e935fe2502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 7 Sep 2023 15:43:20 +0200 Subject: [PATCH 06/24] clean-up --- modules/ROOT/content-nav.adoc | 2 +- modules/ROOT/pages/queries/advanced.adoc | 1 - modules/ROOT/pages/queries/basic.adoc | 4 +- modules/ROOT/pages/queries/concepts.adoc | 13 ++-- modules/ROOT/pages/queries/expressions.adoc | 67 ++++++++++++--------- modules/ROOT/pages/queries/index.adoc | 6 +- 6 files changed, 51 insertions(+), 42 deletions(-) delete mode 100644 modules/ROOT/pages/queries/advanced.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index fd9c84514..300d7e7c3 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -7,7 +7,7 @@ * xref:queries/index.adoc[] ** xref:queries/concepts.adoc[] ** xref:queries/basic.adoc[] -** xref:queries/case.adoc[] +** xref:queries/expressions.adoc[] ** xref:queries/expressions.adoc[] * xref:clauses/index.adoc[] diff --git a/modules/ROOT/pages/queries/advanced.adoc b/modules/ROOT/pages/queries/advanced.adoc deleted file mode 100644 index d10e49a75..000000000 --- a/modules/ROOT/pages/queries/advanced.adoc +++ /dev/null @@ -1 +0,0 @@ -= Advanced queries \ No newline at end of file diff --git a/modules/ROOT/pages/queries/basic.adoc b/modules/ROOT/pages/queries/basic.adoc index be4622480..e93300bec 100644 --- a/modules/ROOT/pages/queries/basic.adoc +++ b/modules/ROOT/pages/queries/basic.adoc @@ -876,4 +876,6 @@ To delete all nodes and relationships in a graph, run the following query: ---- MATCH (n) DETACH DELETE n ----- \ No newline at end of file +---- + +For more complex queries, see xref:subqueries/index.adoc[]. \ No newline at end of file diff --git a/modules/ROOT/pages/queries/concepts.adoc b/modules/ROOT/pages/queries/concepts.adoc index 024a5e972..f9d6a6593 100644 --- a/modules/ROOT/pages/queries/concepts.adoc +++ b/modules/ROOT/pages/queries/concepts.adoc @@ -3,12 +3,11 @@ [[core-concepts]] = Core concepts -Cypher queries match patterns in a graph. -This page provides a brief overview of three core concepts used to construct Cypher queries: nodes, relationships, and patterns. +Fundamentally, a Neo4j graph database consists of three core entities: nodes, relationships, and patterns. +Cypher queries are constructed to either match or create these entities in a graph. +Having a basic understanding of what nodes, relationships, and patterns are in a graph is therefore crucial in order to construct Cypher queries. -For a more detailed overview, see the link:{neo4j-docs-base-uri}/getting-started/{page-version}/cypher-intro/[Getting Started Guide -> Introduction to Cypher] - -The examples below use the clauses xref:clauses/match.adoc[] and xref:clauses/return.adoc[] to find and return the desired graph patterns. +The below examples use the xref:clauses/match.adoc[] and xref:clauses/return.adoc[] clauses to find and return the desired graph patterns. To learn more about these and many other Cypher clauses, see the section on xref:clauses/index.adoc[]. [[core-concepts-nodes]] @@ -67,7 +66,7 @@ MATCH (n:Person {name: 'Anna'})-[:KNOWS]-{1,5}(friends:Person WHERE n.born < fri RETURN DISTINCT friends.name AS youngerFriends ---- -This example uses a xref:patterns/concepts.adoc#quantified-relationships[quantified relationship] to find all paths of a specific length (between `1` and `5` hops) traversing only relationships of type `KNOWS` from the start node `Anna` to other `Person` nodes with a lower `born` property (as defined by the xref:clauses/where.adoc[] clause). +This example uses a xref:patterns/concepts.adoc#quantified-relationships[quantified relationship] to find all paths of a specific length (between `1` and `5` 'hops') traversing only relationships of type `KNOWS` from the start node `Anna` to other `Person` nodes with a lower `born` property (as defined by the xref:clauses/where.adoc[] clause). The xref:syntax/operators.adoc#syntax-using-the-distinct-operator[DISTINCT] operator is used to ensure that the `RETURN` clause only returns unique nodes. Patterns can also be assigned variables. @@ -81,5 +80,3 @@ RETURN p ---- For more information about graph patterns, see the section on xref:patterns/index.adoc[]. - -More information about constructing basic queries can be found in the xref:queries/basic.adoc[next section]. \ No newline at end of file diff --git a/modules/ROOT/pages/queries/expressions.adoc b/modules/ROOT/pages/queries/expressions.adoc index 5ea040ce2..a4598d772 100644 --- a/modules/ROOT/pages/queries/expressions.adoc +++ b/modules/ROOT/pages/queries/expressions.adoc @@ -1,14 +1,15 @@ -= Note on allowed expressions in Cypher queries += Cypher expressions -An expression in Cypher can be: +This page contains examples of allowed expressions in Cypher. + +[NOTE] +==== +Most expressions in Cypher evaluate to `null` if any of their inner expressions are `null`. +Notable exceptions are the operators `IS NULL`, `IS NOT NULL`, and the type predicate expression. +==== + +== General -* A decimal (integer or float) literal: `13`, `-40000`, `3.14`. -* A decimal (integer or float) literal in scientific notation: `6.022E23`. -* A hexadecimal integer literal (starting with `0x`): `0x13af`, `0xFC3A9`, `-0x66eff`. -* An octal integer literal (starting with `0o`): `0o1372`, `-0o5671`. -* A string literal: `'Hello'`, `"World"`. -* A float literal: `Inf`, `Infinity`, `NaN` -* A boolean literal: `true`, `false`. * A variable: `n`, `x`, `rel`, `myFancyVariable`, `++`A name with weird stuff in it[]!`++`. * A property: `n.prop`, `x.prop`, `rel.thisProperty`, `++myFancyVariable.`(weird property name)`++`. * A dynamic property: `n["prop"]`, `rel[n.city + n.zip]`, `map[coll[0]]`. @@ -18,25 +19,38 @@ An expression in Cypher can be: * An aggregate function: `avg(x.prop)`, `+count(*)+`. * A path-pattern: `+(a)-[r]->(b)+`, `+(a)-[r]-(b)+`, `+(a)--(b)+`, `+(a)-->()<--(b)+`. * An operator application: `1 + 2`, `3 < 4`. -* A predicate expression is an expression that returns `true` or `false`: `a.prop = 'Hello'`, `length(p) > 10`, `a.name IS NOT NULL`. -* A special case of predicates are label and relationship type expressions: `(n:A|B)`, `()-[r:R1|R2]->()`. -* A subquery expression. For example: -`EXISTS { - MATCH (n)-[r]->(p) - WHERE p.name = 'Sven' -}`. +* A subquery expression. * A regular expression: `a.name =~ 'Tim.*'`. -* A case-sensitive string matching expression: `a.surname STARTS WITH 'Sven'`, `a.surname ENDS WITH 'son'` or `a.surname CONTAINS 'son'`. * A `CASE` expression. [NOTE] ==== -Most expressions in Cypher evaluate to `null` if any of their inner expressions are `null`. -Notable exceptions are the operators `IS NULL`, `IS NOT NULL`, and the type predicate expression. +Using regular expressions with unsanitized user input makes you vulnerable to Cypher injection. +Consider using xref:syntax/parameters.adoc[parameters] instead. ==== + +== Numerical + +* A decimal (integer or float) literal: `13`, `-40000`, `3.14`. +* A decimal (integer or float) literal in scientific notation: `6.022E23`. +* A hexadecimal integer literal (starting with `0x`): `0x13af`, `0xFC3A9`, `-0x66eff`. +* An octal integer literal (starting with `0o`): `0o1372`, `-0o5671`. +* A float literal: `Inf`, `Infinity`, `NaN`. + +[NOTE] +==== +Any number literal may contain an underscore `_` between digits. +There may be an underscore between the `0x` or `0o` and the digits for hexadecimal and octal literals. +==== + +== String + +* A string literal: `'Hello'`, `"World"`. +* A case-sensitive string matching expression: `a.surname STARTS WITH 'Sven'`, `a.surname ENDS WITH 'son'` or `a.surname CONTAINS 'son'`. + [[expressions-string-literals]] -== Note on string literals +=== String literal escape sequences String literals can contain the following escape sequences: @@ -54,14 +68,9 @@ String literals can contain the following escape sequences: |`\uxxxx`|Unicode UTF-16 code point (4 hex digits must follow the `\u`) |=================== -[NOTE] -==== -Using regular expressions with unsanitized user input makes you vulnerable to Cypher injection. -Consider using xref:syntax/parameters.adoc[parameters] instead. -==== +== Boolean -[[expressions-number-literals]] -== Note on number literals +* A boolean literal: `true`, `false`. +* A predicate expression is an expression that returns `true` or `false`: `a.prop = 'Hello'`, `length(p) > 10`, `a.name IS NOT NULL`. +* Label and relationship type expressions: `(n:A|B)`, `()-[r:R1|R2]->()`. -Any number literal may contain an underscore `_` between digits. -There may be an underscore between the `0x` or `0o` and the digits for hexadecimal and octal literals. diff --git a/modules/ROOT/pages/queries/index.adoc b/modules/ROOT/pages/queries/index.adoc index 0cd382129..f65ef62ae 100644 --- a/modules/ROOT/pages/queries/index.adoc +++ b/modules/ROOT/pages/queries/index.adoc @@ -1,7 +1,9 @@ = Queries +This section provides a brief overview of the core concepts of a Cypher query (nodes, relationships, and patterns), and examples of how to query a Neo4j graph. +It also contains information about expressions used in a Cypher query. + * xref:queries/concepts.adoc[] * xref:queries/basic.adoc[] -* xref:queries/advanced.adoc[] +* xref:queries/expressions.adoc[] * xref:queries/case.adoc[] -* xref:queries/expressions.adoc[] \ No newline at end of file From 3b6502db904d5ae72a7e23151f016a8a65aca413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Thu, 7 Sep 2023 16:45:23 +0200 Subject: [PATCH 07/24] first update on subqueries --- .../images/graph_expression_subqueries.svg | 121 ------------------ modules/ROOT/images/subqueries_graph.svg | 1 + .../ROOT/pages/subqueries/call-subquery.adoc | 10 +- modules/ROOT/pages/subqueries/collect.adoc | 24 +++- modules/ROOT/pages/subqueries/count.adoc | 21 +++ .../ROOT/pages/subqueries/existential.adoc | 34 ++++- modules/ROOT/pages/subqueries/index.adoc | 42 +----- 7 files changed, 81 insertions(+), 172 deletions(-) delete mode 100644 modules/ROOT/images/graph_expression_subqueries.svg create mode 100644 modules/ROOT/images/subqueries_graph.svg diff --git a/modules/ROOT/images/graph_expression_subqueries.svg b/modules/ROOT/images/graph_expression_subqueries.svg deleted file mode 100644 index 0b116c781..000000000 --- a/modules/ROOT/images/graph_expression_subqueries.svg +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - -H - - - -Andy - -Swedish, Person - -age = 36 - name = 'Andy' - - - -AndyDog - -Dog - -name = 'Andy' - - - -Andy->AndyDog - - -HAS_DOG -  since = 2016 - - - -Timothy - -Person - -age = 25 - name = 'Timothy' - nickname = 'Tim' - - - -Mittens - -Cat - -name = 'Mittens' - - - -Timothy->Mittens - - -HAS_CAT -  since = 2019 - - - -Peter - -Person - -age = 25 - name = 'Peter' - nickname = 'Pete' - - - -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/images/subqueries_graph.svg b/modules/ROOT/images/subqueries_graph.svg new file mode 100644 index 000000000..ca8c8b786 --- /dev/null +++ b/modules/ROOT/images/subqueries_graph.svg @@ -0,0 +1 @@ +HAS_DOGsince:2016HAS_CATsince:2019HAS_DOGsince:2018HAS_DOGsince:2010HAS TOYPersonSwedishname:'Andy'age:36Dogname:'Andy'Personname:'Timothy'age:25nickname:'Tim'Catname:'Mittens'Personname:'Peterage:25nickname:'Pete'Dogname:'Ozzy'Dogname:'Fido'Toyname:'Banana' \ No newline at end of file diff --git a/modules/ROOT/pages/subqueries/call-subquery.adoc b/modules/ROOT/pages/subqueries/call-subquery.adoc index a98458dd0..9a0cfb7fa 100644 --- a/modules/ROOT/pages/subqueries/call-subquery.adoc +++ b/modules/ROOT/pages/subqueries/call-subquery.adoc @@ -1,15 +1,9 @@ :description: The `+CALL {}+` clause evaluates a subquery that returns some values. [[query-call-subquery]] -= +CALL {}+ (subquery) += CALL {} -[abstract] --- -The `+CALL {}+` clause evaluates a subquery that returns some values. --- - -`CALL` allows to execute subqueries, i.e. queries inside of other queries. -Subqueries allow you to compose queries, which is especially useful when working with `UNION` or aggregations. +The `CALL` clause is used to call a subquery. [TIP] ==== diff --git a/modules/ROOT/pages/subqueries/collect.adoc b/modules/ROOT/pages/subqueries/collect.adoc index f630fce93..c3aed4bb2 100644 --- a/modules/ROOT/pages/subqueries/collect.adoc +++ b/modules/ROOT/pages/subqueries/collect.adoc @@ -1,6 +1,5 @@ [[collect]] = `COLLECT` subqueries - _This feature was introduced in Neo4j 5.6._ A `COLLECT` subquery expression can be used to create a list with the rows returned by a given subquery. @@ -9,6 +8,27 @@ Any non-writing query is allowed. `COLLECT` subqueries differ from `COUNT` and `EXISTS` subqueries in that the final `RETURN` clause is mandatory. The `RETURN` clause must return exactly one column. +[[collect-example]] +== Example graph + +The following graph is used for the examples below: + +image:subqueries_graph.svg[] + +To recreate the graph, run the following query against an empty Neo4j database: + +[source, cypher, role=test-setup] +---- +CREATE +(andy:Swedish:Person {name: 'Andy', age: 36}), +(timothy:Person {name: 'Timothy', nickname: 'Tim', age: 25}), +(peter:Person {name: 'Peter', nickname: 'Pete', 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'}) +---- + [[collect-simple]] == Simple `COLLECT` subquery @@ -88,7 +108,7 @@ RETURN [[collect-with]] -=== `COLLECT` subquery with `WITH` +== `COLLECT` subquery with `WITH` Variables from the outside scope are visible for the entire subquery, even when using a `WITH` clause. To avoid confusion, shadowing of these variables is not allowed. diff --git a/modules/ROOT/pages/subqueries/count.adoc b/modules/ROOT/pages/subqueries/count.adoc index 7bce4d7cb..58e7812b3 100644 --- a/modules/ROOT/pages/subqueries/count.adoc +++ b/modules/ROOT/pages/subqueries/count.adoc @@ -9,6 +9,27 @@ even if a final `RETURN` clause is used. One exception to this is that for a `DI 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-example]] +== Example graph + +The following graph is used for the examples below: + +image:subqueries_graph.svg[] + +To recreate the graph, run the following query against an empty Neo4j database: + +[source, cypher, role=test-setup] +---- +CREATE +(andy:Swedish:Person {name: 'Andy', age: 36}), +(timothy:Person {name: 'Timothy', nickname: 'Tim', age: 25}), +(peter:Person {name: 'Peter', nickname: 'Pete', 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'}) +---- + [[count-simple]] == Simple `COUNT` subquery diff --git a/modules/ROOT/pages/subqueries/existential.adoc b/modules/ROOT/pages/subqueries/existential.adoc index 93b717dfa..2a8102cdb 100644 --- a/modules/ROOT/pages/subqueries/existential.adoc +++ b/modules/ROOT/pages/subqueries/existential.adoc @@ -1,17 +1,39 @@ [[existential]] = `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 xref::clauses/where.adoc#filter-on-patterns[path pattern] but is more powerful because it allows you to use `MATCH` and `WHERE` clauses internally. +An `EXISTS` subquery can be used to find out if a specified pattern exists at least once in the graph. +It serves the same purpose as a xref::clauses/where.adoc#filter-on-patterns[path pattern] but it 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`. +If the `EXISTS` 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, +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 cases where the `EXISTS` 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 `EXISTS` consists of only a pattern and an optional `WHERE` clause. + + +[[expressions-subquery-example]] +== Example graph + +The following graph is used for the examples below: + +image:subqueries_graph.svg[] + +To recreate the graph, run the following query against an empty Neo4j database: + +[source, cypher, role=test-setup] +---- +CREATE +(andy:Swedish:Person {name: 'Andy', age: 36}), +(timothy:Person {name: 'Timothy', nickname: 'Tim', age: 25}), +(peter:Person {name: 'Peter', nickname: 'Pete', 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'}) +---- [[existential-simple]] == Simple `EXISTS` subquery diff --git a/modules/ROOT/pages/subqueries/index.adoc b/modules/ROOT/pages/subqueries/index.adoc index 9e8468e61..f3407f106 100644 --- a/modules/ROOT/pages/subqueries/index.adoc +++ b/modules/ROOT/pages/subqueries/index.adoc @@ -1,40 +1,12 @@ = Subqueries -Cypher includes three different types of subquery expressions: - -* xref:subqueries/call-subquery.adoc[] -* xref:subqueries/existential.acoc[Existential subquery expressions] -* xref:subqueries/count.adoc[Count subquery expressions] -* xref:subqueries/collect.adoc[Collect subquery expressions] label:new[Introduced in 5.6] - -Subquery expressions can appear anywhere that an expression is valid. -A subquery has a scope, as indicated by the opening and closing braces, `{` and `}`. +A Cypher subquery is called from an enclosing outer query, and executes within its own scope, as defined by `{` and `}`. Any variable that is defined in the outside scope can be referenced inside the subquery's own scope. -Variables introduced inside the subquery are not part of the outside scope and therefore can't be accessed on the outside. - -[[expressions-subquery-example]] - -== Example graph -The following graph is used for the examples below: - -//// -[source, cypher, role=test-setup] ----- -MATCH (n:A|B|C|D|E) DETACH DELETE n; -CREATE -(andy:Swedish:Person {name: 'Andy', age: 36}), -(timothy:Person {name: 'Timothy', nickname: 'Tim', age: 25}), -(peter:Person {name: 'Peter', nickname: 'Pete', 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[] - - - +However, variables introduced inside the subquery are not part of the outside scope and therefore cannot be accessed outside of the subquery. +Cypher includes four different types of subqueries: +* xref:subqueries/call-subquery.adoc[] +* xref:subqueries/existential.adoc[EXISTS subqueries] +* xref:subqueries/count.adoc[COUNT subqueries] +* xref:subqueries/collect.adoc[COLLECT subqueries] label:new[Introduced in 5.6] From 60a895b70f6212c0a177bc6e0168676262610f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 8 Sep 2023 12:39:55 +0200 Subject: [PATCH 08/24] add separate transactions page, remove plus signs, and more --- modules/ROOT/content-nav.adoc | 1 + modules/ROOT/images/call_subquery_graph.svg | 1 + .../images/graph_call_subquery_clause.svg | 71 -- .../ROOT/pages/subqueries/call-subquery.adoc | 722 ++---------------- modules/ROOT/pages/subqueries/collect.adoc | 79 +- modules/ROOT/pages/subqueries/count.adoc | 60 +- .../ROOT/pages/subqueries/existential.adoc | 51 +- modules/ROOT/pages/subqueries/index.adoc | 18 +- .../subqueries-in-transactions.adoc | 555 ++++++++++++++ 9 files changed, 755 insertions(+), 803 deletions(-) create mode 100644 modules/ROOT/images/call_subquery_graph.svg delete mode 100644 modules/ROOT/images/graph_call_subquery_clause.svg create mode 100644 modules/ROOT/pages/subqueries/subqueries-in-transactions.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index 300d7e7c3..7af194536 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -40,6 +40,7 @@ * xref:subqueries/index.adoc[] ** xref:subqueries/call-subquery.adoc[] +** xref:subqueries/subqueries-in-transactions.adoc[] ** xref:subqueries/existential.adoc[] ** xref:subqueries/count.adoc[] ** xref:subqueries/collect.adoc[] diff --git a/modules/ROOT/images/call_subquery_graph.svg b/modules/ROOT/images/call_subquery_graph.svg new file mode 100644 index 000000000..d5f2f8437 --- /dev/null +++ b/modules/ROOT/images/call_subquery_graph.svg @@ -0,0 +1 @@ +CHILD_OFFRIEND_OFPersonChildname:'Alice'age:20PersonParentname:'Charlie'age:65Personname:'Bob'age:25Personname:'Dora'age:30Countercount:0 \ No newline at end of file diff --git a/modules/ROOT/images/graph_call_subquery_clause.svg b/modules/ROOT/images/graph_call_subquery_clause.svg deleted file mode 100644 index 17483f2b5..000000000 --- a/modules/ROOT/images/graph_call_subquery_clause.svg +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - -L - - - -N0 - -Person, Child - -age = 20 -name = 'Alice' - - - -N2 - -Person, Parent - -age = 65 -name = 'Charlie' - - - -N0->N2 - - -CHILD_OF - - - -N1 - -Person - -age = 27 -name = 'Bob' - - - -N0->N1 - - -FRIEND_OF - - - -N3 - -Person - -age = 30 -name = 'Dora' - - - -N4 - -Counter - -count = 0 - - - diff --git a/modules/ROOT/pages/subqueries/call-subquery.adoc b/modules/ROOT/pages/subqueries/call-subquery.adoc index 9a0cfb7fa..a48d1d683 100644 --- a/modules/ROOT/pages/subqueries/call-subquery.adoc +++ b/modules/ROOT/pages/subqueries/call-subquery.adoc @@ -1,16 +1,28 @@ :description: The `+CALL {}+` clause evaluates a subquery that returns some values. -[[query-call-subquery]] +[[call]] = CALL {} The `CALL` clause is used to call a subquery. +Unlike other subqueries in Cypher, it can be used to perform changes to the database (e.g. xref:clauses/create.adoc[] new nodes), and it requires an importing xref:clauses/with.adoc[] clause. -[TIP] +[NOTE] ==== The `CALL` clause is also used for calling procedures. For descriptions of the `CALL` clause in this context, refer to xref::clauses/call.adoc[`CALL` procedure]. ==== +[[call-syntax]] +== Syntax + +[source, syntax] +---- +CALL LCURLY regularQuery RCURLY subqueryInTransactionsParameters? +---- + +[[call-returning-unit]] +== Returning subqueries and unit subqueries + Subqueries which end in a `RETURN` statement are called _returning subqueries_ while subqueries without such a return statement are called _unit subqueries_. A subquery is evaluated for each incoming input row. @@ -18,39 +30,35 @@ Every output row of a _returning subquery_ is combined with the input row to bui That means that a returning subquery will influence the number of rows. If the subquery does not return any rows, there will be no rows available after the subquery. -_Unit subqueries_ on the other hand are called for their side-effects and not for their results and do therefore not influence the results of the enclosing query. - -There are restrictions on how subqueries interact with the enclosing query: +_Unit subqueries_, on the other hand, are called for their ability to alter the graph with clauses such as `CREATE`, `MERGE`, `SET`, and `DELETE`. +They not explicitly return anything, and this means that the number of rows present after the subquery is the same as was going into the subquery. -* A subquery can only refer to variables from the enclosing query if they are explicitly imported. -* A subquery cannot return variables with the same names as variables in the enclosing query. -* All variables that are returned from a subquery are afterwards available in the enclosing query. +[[call-example-graph]] +== Example graph The following graph is used for the examples below: -image:graph_call_subquery_clause.svg[] +image::call_subquery_graph.svg[] To recreate the graph, run the following query in an empty Neo4j database: [source, cypher, role=test-setup] ---- CREATE - (a:Person:Child {age: 20, name: 'Alice'}), - (b:Person {age: 27, name: 'Bob'}), - (c:Person:Parent {age: 65, name: 'Charlie'}), - (d:Person {age: 30, name: 'Dora'}) + (a:Person:Child {name: 'Alice', age: 20}), + (b:Person {name: 'Bob', age: 27}), + (c:Person:Parent {name: 'Charlie', age: 65}), + (d:Person {name: 'Dora', age: 30}) CREATE (a)-[:FRIEND_OF]->(b) CREATE (a)-[:CHILD_OF]->(c) CREATE (:Counter {count: 0}) ---- - [[call-semantics]] == Semantics A `CALL` clause is executed once for each incoming row. - .Execute for each incoming row ====== @@ -69,10 +77,10 @@ RETURN innerReturn .Result [role="queryresult",options="header,footer",cols="m"] |=== -| +innerReturn+ -| +'hello'+ -| +'hello'+ -| +'hello'+ +| innerReturn +| 'hello' +| 'hello' +| 'hello' d|Rows:3 |=== ====== @@ -103,13 +111,13 @@ RETURN [role="queryresult",options="header,footer",cols=""2*(b), - (a)-[:CHILD_OF]->(c) ----- -//// - -[[subquery-correlated-aggregation]] +[[call-aggregation-imported-variables]] == Aggregation on imported variables Aggregations in subqueries are scoped to the subquery evaluation, also for imported variables. @@ -395,565 +355,41 @@ RETURN p.name, youngerPersonsCount [role="queryresult",options="header,footer",cols="2*>. -Canceling that outer transaction will cancel the inner ones. - - -.+Loading CSV data in transactions+ -====== -This example uses a CSV file and the `LOAD CSV` clause to import more data to the example graph. -It creates nodes in separate transactions using `+CALL { ... } IN TRANSACTIONS+`: - -.friends.csv -[source, csv, role="noheader" filename="friends.csv"] ----- -1,Bill,26 -2,Max,27 -3,Anna,22 -4,Gladys,29 -5,Summer,24 ----- - -.Query -[source, cypher] ----- -LOAD CSV FROM 'file:///friends.csv' AS line -CALL { - WITH line - CREATE (:Person {name: line[1], age: toInteger(line[2])}) -} IN TRANSACTIONS ----- - -.Result -[role="queryresult",options="footer",cols="1* 100 -CALL { - WITH n - DETACH DELETE n -} IN TRANSACTIONS ----- - -.Result -[role="queryresult",options="footer",cols="1*>. - -After each execution of the inner query finishes (successfully or not), a status value is created that records information about the execution and the transaction that executed it: - -* If the inner execution produces one or more rows as output, then a binding to this status value is added to each row, under the selected variable name. -* If the inner execution fails then a single row is produced containing a binding to this status value under the selected variable, and null bindings for all variables that should have been returned by the inner query (if any). - -The status value is a map value with the following fields: - -* `started`: `true` when the inner transaction was started, `false` otherwise. -* `committed`, true when the inner transaction changes were successfully committed, false otherwise. -* `transactionId`: the inner transaction id, or null if the transaction was not started. -* `errorMessage`, the inner transaction error message, or null in case of no error. - -Example of reporting status with `ON ERROR CONTINUE`: - -.Query -[source, cypher, indent=0, role=test-result-skip] ----- -UNWIND [1, 0, 2, 4] AS i -CALL { - WITH i - CREATE (n:Person {num: 100/i}) // Note, fails when i = 0 - RETURN n -} IN TRANSACTIONS - OF 1 ROW - ON ERROR CONTINUE - REPORT STATUS AS s -RETURN n.num, s; ----- - -.Result -[role="queryresult",options="header,footer",cols="2* 2 - RETURN l AS largeLists -} -RETURN largeLists ----- +[[call-unit-subqueries]] +== Unit subqueries and side-effects -.Error message -[source, error] ----- -Importing WITH should consist only of simple references to outside variables. -WHERE is not allowed. ----- +Unit subqueries are called for their ability to alter the graph with updating clauses. +They do not impact the amount of rows returned by the enclosing query. -A solution to this restriction, necessary for any filtering or ordering of an importing `WITH` clause, is to declare a second `WITH` clause after the importing `WITH` clause. -This second `WITH` clause will act as a regular `WITH` clause. -For example, the following query will not throw an error: +This example query creates five clones of each existing person. +As the subquery is a unit subquery, it does not change the number of rows of the enclosing query. .Query [source, cypher] ---- -UNWIND [[1,2],[1,2,3,4],[1,2,3,4,5]] AS l +MATCH (p:Person) CALL { - WITH l - WITH size(l) AS size, l AS l - WHERE size > 2 - RETURN l AS largeLists + WITH p + UNWIND range (1, 5) AS i + CREATE (:Person {name: p.name}) } -RETURN largeLists +RETURN count(*) ---- .Result [role="queryresult",options="header,footer",cols="1*(d:Dog) RETURN d.name } AS dogNames, [role="queryresult",options="header,footer",cols="2*(:Dog) } as howManyDogs [role="queryresult",options="header,footer",cols="2*(:Dog) } AS numDogs, [role="queryresult",options="header,footer",cols="2*>. +Canceling that outer transaction will cancel the inner ones. + + +.+Loading CSV data in transactions+ +====== +This example uses a CSV file and the `LOAD CSV` clause to import more data to the example graph. +It creates nodes in separate transactions using `+CALL { ... } IN TRANSACTIONS+`: + +.friends.csv +[source, csv, role="noheader" filename="friends.csv"] +---- +1,Bill,26 +2,Max,27 +3,Anna,22 +4,Gladys,29 +5,Summer,24 +---- + +.Query +[source, cypher] +---- +LOAD CSV FROM 'file:///friends.csv' AS line +CALL { + WITH line + CREATE (:Person {name: line[1], age: toInteger(line[2])}) +} IN TRANSACTIONS +---- + +.Result +[role="queryresult",options="footer",cols="1* 100 +CALL { + WITH n + DETACH DELETE n +} IN TRANSACTIONS +---- + +.Result +[role="queryresult",options="footer",cols="1*>. + +After each execution of the inner query finishes (successfully or not), a status value is created that records information about the execution and the transaction that executed it: + +* If the inner execution produces one or more rows as output, then a binding to this status value is added to each row, under the selected variable name. +* If the inner execution fails then a single row is produced containing a binding to this status value under the selected variable, and null bindings for all variables that should have been returned by the inner query (if any). + +The status value is a map value with the following fields: + +* `started`: `true` when the inner transaction was started, `false` otherwise. +* `committed`, true when the inner transaction changes were successfully committed, false otherwise. +* `transactionId`: the inner transaction id, or null if the transaction was not started. +* `errorMessage`, the inner transaction error message, or null in case of no error. + +Example of reporting status with `ON ERROR CONTINUE`: + +.Query +[source, cypher, indent=0, role=test-result-skip] +---- +UNWIND [1, 0, 2, 4] AS i +CALL { + WITH i + CREATE (n:Person {num: 100/i}) // Note, fails when i = 0 + RETURN n +} IN TRANSACTIONS + OF 1 ROW + ON ERROR CONTINUE + REPORT STATUS AS s +RETURN n.num, s; +---- + +.Result +[role="queryresult",options="header,footer",cols="2* 2 + RETURN l AS largeLists +} +RETURN largeLists +---- + +.Error message +[source, error] +---- +Importing WITH should consist only of simple references to outside variables. +WHERE is not allowed. +---- + +A solution to this restriction, necessary for any filtering or ordering of an importing `WITH` clause, is to declare a second `WITH` clause after the importing `WITH` clause. +This second `WITH` clause will act as a regular `WITH` clause. +For example, the following query will not throw an error: + +.Query +[source, cypher] +---- +UNWIND [[1,2],[1,2,3,4],[1,2,3,4,5]] AS l +CALL { + WITH l + WITH size(l) AS size, l AS l + WHERE size > 2 + RETURN l AS largeLists +} +RETURN largeLists +---- + +.Result +[role="queryresult",options="header,footer",cols="1* Date: Fri, 8 Sep 2023 14:33:42 +0200 Subject: [PATCH 09/24] type predicates --- .../graph_expression_type_predicate.svg | 40 ------------------- .../type_predicate_expression_graph.svg | 1 + modules/ROOT/pages/subqueries/collect.adoc | 4 +- .../values-and-types/type-predicate.adoc | 17 +++++--- 4 files changed, 14 insertions(+), 48 deletions(-) delete mode 100644 modules/ROOT/images/graph_expression_type_predicate.svg create mode 100644 modules/ROOT/images/type_predicate_expression_graph.svg diff --git a/modules/ROOT/images/graph_expression_type_predicate.svg b/modules/ROOT/images/graph_expression_type_predicate.svg deleted file mode 100644 index e0ef5ddf7..000000000 --- a/modules/ROOT/images/graph_expression_type_predicate.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - -H - - - -Alice - -Person - - name = 'Alice' - age = 14 - - - -Bob - -Person - - name = 'Bob' - age = '20' - - - -Charlie - -Person - - name = 'Charlie' - age = 21 - - - diff --git a/modules/ROOT/images/type_predicate_expression_graph.svg b/modules/ROOT/images/type_predicate_expression_graph.svg new file mode 100644 index 000000000..200d41d07 --- /dev/null +++ b/modules/ROOT/images/type_predicate_expression_graph.svg @@ -0,0 +1 @@ +Personname:'Alice'age:18Personname:'Bob'age:'20'Personname:'Charlie'age:21 \ No newline at end of file diff --git a/modules/ROOT/pages/subqueries/collect.adoc b/modules/ROOT/pages/subqueries/collect.adoc index 1d9092198..ea96f7836 100644 --- a/modules/ROOT/pages/subqueries/collect.adoc +++ b/modules/ROOT/pages/subqueries/collect.adoc @@ -174,7 +174,7 @@ See a few examples below of how `COLLECT` can be used in different positions wit [[collect-return]] -==== Using `COLLECT` in `RETURN` +=== Using `COLLECT` in `RETURN` [source, cypher] ---- @@ -264,7 +264,7 @@ RETURN COLLECT { MATCH (person)-[:HAS_DOG]->(d:Dog) RETURN d.name } AS dogNames, [[collect-differentiation]] -=== Using `COLLECT` vs `collect()` +== Using `COLLECT` vs `collect()` `COLLECT` does not handle `null` values in the same way that function `collect()` does. The `collect()` function automatically removes `null` values. diff --git a/modules/ROOT/pages/values-and-types/type-predicate.adoc b/modules/ROOT/pages/values-and-types/type-predicate.adoc index fbd58ea47..72ee4a5a4 100644 --- a/modules/ROOT/pages/values-and-types/type-predicate.adoc +++ b/modules/ROOT/pages/values-and-types/type-predicate.adoc @@ -1,18 +1,24 @@ [[type-predicate]] -= Type predicate expressions += Type predicate expression _This feature was introduced in Neo4j 5.9._ A type predicate expression can be used to verify the type of a variable, literal, property or other Cypher expression. -[source, syntax, role="noheader", indent=0] +[[type-predicate-syntax]] +== Syntax + +[source, syntax] ---- IS :: ---- -Where `` is any Cypher expression and `` is a Cypher Type. +Where `` is any Cypher expression and `` is a Cypher type. For all available Cypher types, see the section on xref::values-and-types/property-structural-constructed.adoc#types-synonyms[types and their synonyms]. +[[type-predicate-regular]] +== Verify the type of a Cypher expression + [source, cypher] ---- UNWIND [42, true, 'abc'] AS val @@ -123,14 +129,13 @@ A graph containing the following nodes is used for the example below: //// [source, cypher, role=test-setup] ---- -MATCH (n) DETACH DELETE n; -CREATE (:Person {name: 'Alice', age:14}), +CREATE (:Person {name: 'Alice', age:18}), (:Person {name:'Bob', age:'20'}), (:Person {name:'Charlie', age:21}), ---- //// -image:graph_expression_type_predicate.svg[] +image::type_predicate_expression_graph.svg[] The following query finds all `Person` nodes with an `age` property that is an `INTEGER` with a greater value than `18`. From 190b996529e2244c7804f8fe3e33b697ee3707ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 8 Sep 2023 15:24:25 +0200 Subject: [PATCH 10/24] fix xref 2 --- modules/ROOT/content-nav.adoc | 1 - modules/ROOT/pages/clauses/call.adoc | 2 +- .../pages/clauses/clause_composition.adoc | 2 +- modules/ROOT/pages/clauses/delete.adoc | 2 +- modules/ROOT/pages/clauses/index.adoc | 6 +- modules/ROOT/pages/clauses/load-csv.adoc | 4 +- modules/ROOT/pages/clauses/match.adoc | 2 +- .../pages/clauses/transaction-clauses.adoc | 2 +- modules/ROOT/pages/clauses/use.adoc | 2 +- modules/ROOT/pages/clauses/where.adoc | 2 +- ...ions-additions-removals-compatibility.adoc | 10 +- modules/ROOT/pages/expressions/index.adoc | 78 --- modules/ROOT/pages/expressions/label.adoc | 524 ------------------ .../ROOT/pages/expressions/relationship.adoc | 325 ----------- modules/ROOT/pages/functions/predicate.adoc | 2 +- .../ROOT/pages/introduction/cypher_neo4j.adoc | 2 +- modules/ROOT/pages/keyword-glossary.adoc | 14 +- modules/ROOT/pages/patterns/index.adoc | 2 +- modules/ROOT/pages/patterns/primer.adoc | 2 +- modules/ROOT/pages/patterns/reference.adoc | 2 +- modules/ROOT/pages/queries/expressions.adoc | 5 +- modules/ROOT/pages/subqueries/collect.adoc | 2 +- modules/ROOT/pages/subqueries/count.adoc | 7 +- .../ROOT/pages/subqueries/existential.adoc | 4 +- modules/ROOT/pages/syntax/operators.adoc | 6 +- modules/ROOT/pages/syntax/parsing.adoc | 2 +- .../property-structural-constructed.adoc | 8 +- .../values-and-types/working-with-null.adoc | 2 +- 28 files changed, 49 insertions(+), 973 deletions(-) delete mode 100644 modules/ROOT/pages/expressions/index.adoc delete mode 100644 modules/ROOT/pages/expressions/label.adoc delete mode 100644 modules/ROOT/pages/expressions/relationship.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index 7af194536..7166a366d 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -27,7 +27,6 @@ ** xref:clauses/remove.adoc[] ** xref:clauses/foreach.adoc[] ** xref:clauses/merge.adoc[] -** xref:clauses/call-subquery.adoc[] ** xref:clauses/call.adoc[] ** xref:clauses/union.adoc[] ** xref:clauses/use.adoc[] diff --git a/modules/ROOT/pages/clauses/call.adoc b/modules/ROOT/pages/clauses/call.adoc index 9535abc57..4e00f0628 100644 --- a/modules/ROOT/pages/clauses/call.adoc +++ b/modules/ROOT/pages/clauses/call.adoc @@ -16,7 +16,7 @@ Procedures are called using the `CALL` clause. [TIP] ==== The `CALL` clause is also used to evaluate a subquery. -For descriptions of the `CALL` clause in this context, refer to xref::clauses/call-subquery.adoc[`CALL {}` (subquery)]. +For descriptions of the `CALL` clause in this context, refer to xref::subqueries/call-subquery.adoc[`CALL {}` (subquery)]. ==== Each procedure call needs to specify all required procedure arguments. diff --git a/modules/ROOT/pages/clauses/clause_composition.adoc b/modules/ROOT/pages/clauses/clause_composition.adoc index c1240cd51..6833d7d92 100644 --- a/modules/ROOT/pages/clauses/clause_composition.adoc +++ b/modules/ROOT/pages/clauses/clause_composition.adoc @@ -560,7 +560,7 @@ the graph made by the `CREATE`. [[cypher-clause-composition-call-queries]] == Queries with `CALL {}` subqueries -Subqueries inside a xref::clauses/call-subquery.adoc[`CALL {}`] clause are evaluated for each incoming input row. +Subqueries inside a xref::subqueries/call-subquery.adoc[`CALL {}`] clause are evaluated for each incoming input row. This means that write clauses inside a subquery can get executed more than once. The different invocations of the subquery are executed in turn, in the order of the incoming input rows. diff --git a/modules/ROOT/pages/clauses/delete.adoc b/modules/ROOT/pages/clauses/delete.adoc index 9f1e5cd34..de29778a5 100644 --- a/modules/ROOT/pages/clauses/delete.adoc +++ b/modules/ROOT/pages/clauses/delete.adoc @@ -125,5 +125,5 @@ Deleted 3 nodes, deleted 1 relationship [NOTE] ==== This query is not for deleting large amounts of data, but is useful when experimenting with small example datasets. -When deleting large amounts of data, instead use xref::clauses/call-subquery.adoc#delete-with-call-in-transactions[CALL { ... } IN TRANSACTIONS]. +When deleting large amounts of data, instead use xref::subqueries/call-subquery.adoc#delete-with-call-in-transactions[CALL { ... } IN TRANSACTIONS]. ==== \ No newline at end of file diff --git a/modules/ROOT/pages/clauses/index.adoc b/modules/ROOT/pages/clauses/index.adoc index 0a46cd206..f301c9d6a 100644 --- a/modules/ROOT/pages/clauses/index.adoc +++ b/modules/ROOT/pages/clauses/index.adoc @@ -132,10 +132,10 @@ m| xref::clauses/call.adoc[CALL ... [YIELD ... ]] |=== |Clause |Description -m| xref::clauses/call-subquery.adoc[CALL { ... }] +m| xref::subqueries/call-subquery.adoc[CALL { ... }] | Evaluates a subquery, typically used for post-union processing or aggregations. -m| xref::clauses/call-subquery.adoc#subquery-call-in-transactions[CALL { ... } IN TRANSACTIONS] +m| xref::subqueries/subqueries-in-transactions.adoc[CALL { ... } IN TRANSACTIONS] a| Evaluates a subquery in separate transactions. Typically used when modifying or importing large amounts of data. @@ -183,7 +183,7 @@ m| xref::clauses/use.adoc[USE] m| xref::clauses/load-csv.adoc[LOAD CSV] | Use when importing data from CSV files. -m| xref::clauses/call-subquery.adoc#subquery-call-in-transactions[CALL { ... } IN TRANSACTIONS] +m| xref::subqueries/subqueries-in-transactions.adoc[CALL { ... } IN TRANSACTIONS] | This clause may be used to prevent an out-of-memory error from occurring when importing large amounts of data using `LOAD CSV`. |=== diff --git a/modules/ROOT/pages/clauses/load-csv.adoc b/modules/ROOT/pages/clauses/load-csv.adoc index 3231d26e7..7f63f0ce2 100644 --- a/modules/ROOT/pages/clauses/load-csv.adoc +++ b/modules/ROOT/pages/clauses/load-csv.adoc @@ -20,7 +20,7 @@ Alternatively, `LOAD CSV` also supports accessing CSV files via _HTTPS_, _HTTP_, and _FTP_. * `LOAD CSV` supports resources compressed with _gzip_ and _Deflate_. Additionally `LOAD CSV` supports locally stored CSV files compressed with _ZIP_. * `LOAD CSV` will follow _HTTP_ redirects but for security reasons it will not follow redirects that changes the protocol, for example if the redirect is going from _HTTPS_ to _HTTP_. -* `LOAD CSV` is often used in conjunction with the query clause xref:clauses/call-subquery#subquery-call-in-transactions[`CALL { ... } IN TRANSACTIONS`]. +* `LOAD CSV` is often used in conjunction with the subquery xref:subqueries/subqueries-in-transactions.adoc[`CALL { ... } IN TRANSACTIONS`]. .Configuration settings for file URLs @@ -223,7 +223,7 @@ This reduces the memory overhead of the transaction state. [NOTE] ==== The query clause `CALL { ... } IN TRANSACTIONS` is only allowed in xref::introduction/cypher_neo4j.adoc#cypher-neo4j-transactions[implicit (auto-commit or `:auto`) transactions]. -For more information, see xref:clauses/call-subquery#subquery-call-in-transactions[Subqueries in transactions]. +For more information, see xref:subqueries/subqueries-in-transactions[Subqueries in transactions]. ==== .+artists.csv+ diff --git a/modules/ROOT/pages/clauses/match.adoc b/modules/ROOT/pages/clauses/match.adoc index 1ab8ba836..4195ac4bd 100644 --- a/modules/ROOT/pages/clauses/match.adoc +++ b/modules/ROOT/pages/clauses/match.adoc @@ -286,7 +286,7 @@ Returns all actors who `ACTED_IN` the movie `Wall Street`. |Rows: 3 |=== -Read more about xref:expressions/relationship.adoc[relationship type expressions]. +Read more about xref:patterns/concepts.adoc#relationship-patterns[relationship type expressions]. [[match-on-multiple-rel-types]] === Match on multiple relationship types diff --git a/modules/ROOT/pages/clauses/transaction-clauses.adoc b/modules/ROOT/pages/clauses/transaction-clauses.adoc index 5f119db3d..6135229cf 100644 --- a/modules/ROOT/pages/clauses/transaction-clauses.adoc +++ b/modules/ROOT/pages/clauses/transaction-clauses.adoc @@ -71,7 +71,7 @@ m| DURATION m| outerTransactionId a| The ID of this transaction's outer transaction, if such exists, otherwise an empty string. -For details, see xref::clauses/call-subquery.adoc#subquery-call-in-transactions[`CALL { ... } IN TRANSACTIONS`]. +For details, see xref:subqueries/subqueries-in-transactions.adoc[`CALL { ... } IN TRANSACTIONS`]. m| STRING m| metaData diff --git a/modules/ROOT/pages/clauses/use.adoc b/modules/ROOT/pages/clauses/use.adoc index 073ee288a..fcc4ec609 100644 --- a/modules/ROOT/pages/clauses/use.adoc +++ b/modules/ROOT/pages/clauses/use.adoc @@ -50,7 +50,7 @@ CALL { } ---- + -In subqueries, a `USE` clause may appear as the second clause, if directly following an xref::clauses/call-subquery.adoc#subquery-correlated-importing[importing `WITH` clause]. +In subqueries, a `USE` clause may appear as the second clause, if directly following an xref::subqueries/call-subquery.adoc#call-importing-variables[importing `WITH` clause]. When executing queries against a composite database, the `USE` clause must only refer to graphs that are part of the current composite database. diff --git a/modules/ROOT/pages/clauses/where.adoc b/modules/ROOT/pages/clauses/where.adoc index f770f79a8..d9b3a7302 100644 --- a/modules/ROOT/pages/clauses/where.adoc +++ b/modules/ROOT/pages/clauses/where.adoc @@ -424,7 +424,7 @@ The `name`, `age`, and `email` values for `Peter` are returned because his email Note that the regular expression constructs in link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html[Java regular expressions] are applied only after resolving the escaped character sequences in the given -xref::expressions/index.adoc#expressions-string-literals[string literal]. +xref::queries/expressions#expressions-string-literals[string literal]. It is sometimes necessary to add additional backslashes to express regular expression constructs. This list clarifies the combination of these two definitions, containing the original escape sequence and the resulting character in the regular expression: diff --git a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc index cf982b78d..c3e9fe841 100644 --- a/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc +++ b/modules/ROOT/pages/deprecations-additions-removals-compatibility.adoc @@ -51,7 +51,7 @@ IS [NOT] :: ---- a| -Extended xref:expressions/type-predicate.adoc[type predicate expressions]. +Extended xref:values-and-types/type-predicate.adoc[type predicate expressions]. Closed dynamic union types (`type1 \| type2 \| ...`) are now supported. For example, the following query which evaluates to true if a value is either of type `INTEGER` or `FLOAT`: [source, cypher, role="noheader"] @@ -109,7 +109,7 @@ IS [NOT] :: ---- a| -Extended xref:expressions/type-predicate.adoc[type predicate expressions]. +Extended xref:values-and-types/type-predicate.adoc[type predicate expressions]. The newly supported types are: * `NOTHING` @@ -282,7 +282,7 @@ IS [NOT] :: ---- a| -Added xref:expressions/type-predicate.adoc[type predicate expressions]. +Added xref:values-and-types/type-predicate.adoc[type predicate expressions]. The available types are: * `BOOLEAN` @@ -2122,7 +2122,7 @@ CALL { a| New clause for evaluating a subquery in separate transactions. Typically used when modifying or importing large amounts of data. -See xref:clauses/call-subquery.adoc#subquery-call-in-transactions[CALL +++{ ... }+++ IN TRANSACTIONS]. +See xref:subqueries/subqueries-in-transactions.adoc[CALL +++{ ... }+++ IN TRANSACTIONS]. a| label:syntax[] @@ -3757,7 +3757,7 @@ For versions below 5.0, use a pattern comprehension instead: ---- RETURN size([ (a)-[]->(b) \| a ]) ---- -See xref:functions/scalar.adoc#functions-size[size()] and xref:expressions/subquery.adoc#count[Count Subqueries] for more details. +See xref:functions/scalar.adoc#functions-size[size()] and xref:subqueries/count.adoc[Count Subqueries] for more details. |=== === Updated features diff --git a/modules/ROOT/pages/expressions/index.adoc b/modules/ROOT/pages/expressions/index.adoc deleted file mode 100644 index 67cac926d..000000000 --- a/modules/ROOT/pages/expressions/index.adoc +++ /dev/null @@ -1,78 +0,0 @@ -[[expressions]] -= Expressions - -Cypher includes a several different types of expressions. -More information about each of these can be found in the following sections: - -* xref:expressions/case.adoc[] -* xref:expressions/subquery.adoc[] -* xref:expressions/type-predicate.adoc[] - -[[expressions-overview]] -== General overview - -An expression in Cypher can be: - -* A decimal (integer or float) literal: `13`, `-40000`, `3.14`. -* A decimal (integer or float) literal in scientific notation: `6.022E23`. -* A hexadecimal integer literal (starting with `0x`): `0x13af`, `0xFC3A9`, `-0x66eff`. -* An octal integer literal (starting with `0o`): `0o1372`, `-0o5671`. -* A string literal: `'Hello'`, `"World"`. -* A float literal: `Inf`, `Infinity`, `NaN` -* A boolean literal: `true`, `false`. -* A variable: `n`, `x`, `rel`, `myFancyVariable`, `++`A name with weird stuff in it[]!`++`. -* A property: `n.prop`, `x.prop`, `rel.thisProperty`, `++myFancyVariable.`(weird property name)`++`. -* A dynamic property: `n["prop"]`, `rel[n.city + n.zip]`, `map[coll[0]]`. -* A parameter: `$param`, `$0`. -* A list of expressions: `['a', 'b']`, `[1, 2, 3]`, `['a', 2, n.property, $param]`, `[]`. -* A function call: `length(p)`, `nodes(p)`. -* An aggregate function: `avg(x.prop)`, `+count(*)+`. -* A path-pattern: `+(a)-[r]->(b)+`, `+(a)-[r]-(b)+`, `+(a)--(b)+`, `+(a)-->()<--(b)+`. -* An operator application: `1 + 2`, `3 < 4`. -* A predicate expression is an expression that returns `true` or `false`: `a.prop = 'Hello'`, `length(p) > 10`, `a.name IS NOT NULL`. -* A special case of predicates are label and relationship type expressions: `(n:A|B)`, `()-[r:R1|R2]->()`. -* A subquery expression. For example: -`EXISTS { - MATCH (n)-[r]->(p) - WHERE p.name = 'Sven' -}`. -* A regular expression: `a.name =~ 'Tim.*'`. -* A case-sensitive string matching expression: `a.surname STARTS WITH 'Sven'`, `a.surname ENDS WITH 'son'` or `a.surname CONTAINS 'son'`. -* A `CASE` expression. - -[NOTE] -==== -Most expressions in Cypher evaluate to `null` if any of their inner expressions are `null`. -Notable exceptions are the operators `IS NULL`, `IS NOT NULL`, and the type predicate expression. -==== - -[[expressions-string-literals]] -== Note on string literals - -String literals can contain the following escape sequences: - -[options="header", cols=">1,<2"] -|=================== -|Escape sequence|Character -|`\t`|Tab -|`\b`|Backspace -|`\n`|Newline -|`\r`|Carriage return -|`\f`|Form feed -|`\'`|Single quote -|`\"`|Double quote -|`\\`|Backslash -|`\uxxxx`|Unicode UTF-16 code point (4 hex digits must follow the `\u`) -|=================== - -[NOTE] -==== -Using regular expressions with unsanitized user input makes you vulnerable to Cypher injection. -Consider using xref:syntax/parameters.adoc[parameters] instead. -==== - -[[expressions-number-literals]] -== Note on number literals - -Any number literal may contain an underscore `_` between digits. -There may be an underscore between the `0x` or `0o` and the digits for hexadecimal and octal literals. diff --git a/modules/ROOT/pages/expressions/label.adoc b/modules/ROOT/pages/expressions/label.adoc deleted file mode 100644 index 9aec21151..000000000 --- a/modules/ROOT/pages/expressions/label.adoc +++ /dev/null @@ -1,524 +0,0 @@ -[[label-expressions]] -= Label expressions - -In earlier versions of Neo4j, label expressions for nodes had a single colon operator that represented the `AND` operator. -With the release of version 5.0, a new label expression with an extended set of logical operators is being introduced, in addition to the single colon operator. -It is important to note that you cannot mix these different types of label expression syntax. -For more information, see <>. - -Label expressions evaluate to `true` or `false` when applied to the set of labels for a node. - -Assuming no other filters are applied, then a label expression evaluating to `true` means the node is matched. - -The following table displays whether the label expression matches the relationship: - -.Label expression matches -[cols="^3,^2,^2,^2,^2,^2,^2,^2,^2"] -|=== -| -8+^|*Node* - -|*Label expression* | `()` | `(:A)` | `(:B)` | `(:C)` | `(:A:B)` | `(:A:C)` | `(:B:C)` | `(:A:B:C)` -| `()` -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} - -| `(:A)` -| -| {check-mark} -| -| -| {check-mark} -| {check-mark} -| -| {check-mark} - -| `(:A&B)` -| -| -| -| -| {check-mark} -| -| -| {check-mark} - -| `(:A\|B)` -| -| {check-mark} -| {check-mark} -| -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} - -| `(:!A)` -| {check-mark} -| -| {check-mark} -| {check-mark} -| -| -| {check-mark} -| - -| `(:!!A)` -| -| {check-mark} -| -| -| {check-mark} -| {check-mark} -| -| {check-mark} - -| `(:A&!A)` -| -| -| -| -| -| -| -| - -| `(:A\|!A)` -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} - -| `(:%)` -| -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} - -| `(:!%)` -| {check-mark} -| -| -| -| -| -| -| - -| `(:%\|!%)` -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} - -| `(:%&!%)` -| -| -| -| -| -| -| -| - -| `(:A&%)` -| -| {check-mark} -| -| -| {check-mark} -| {check-mark} -| -| {check-mark} - -| `(:A\|%)` -| -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} -| {check-mark} - -| `(:(A&B)&!(B&C))` -| -| -| -| -| {check-mark} -| -| -| - -| `(:!(A&%)&%)` -| -| -| {check-mark} -| {check-mark} -| -| -| {check-mark} -| - -|=== - - -[[syntax-restrictions-label]] -== Restrictions on using the different types of label expression syntax - -Neo4j version 5.0 introduced an ampersand operator, which is equivalent to the colon conjunction operator. -Mixing the colon conjunction operator with any of the new label expression operators in the same clause will raise a syntax error. - -For example, each of the following clauses will raise syntax errors: - -* `MATCH (n:A|B:C)` -* `MATCH (n:A:B)-[]-(m:(A&B)|C)` -* `MATCH (n:A:B)--(m), (n)-->(o:(A&B)|C)` -* `RETURN n:A&B, n:A:B` -* `MATCH (n:A:B)-[]-(m) WHERE m:(A&B)|C` - -In earlier versions of Neo4j (version 4.4 and earlier), relationship type expressions only had the pipe operator. -As the pipe operator will continue to act as an `OR` operator, it can continue to be used alongside the new operators. - -To make it easier to use the new syntax when extending existing queries, using the different syntax types in separate clauses will be supported. - -For example, the following query will not raise a syntax error: - -[source, cypher, role=noplay] ----- -MATCH (m:A:B:C)-[]->() -MATCH (n:(A&B)|C)-[]->(m) -RETURN m,n ----- - -Queries that exclusively use syntax from earlier versions of Neo4j (version 4.4 and earlier) will continue to be supported. - -For example, the following will not raise a syntax error: - -[source, cypher, role=noplay] ----- -MATCH (m:A:B:C)-[:S|T]->() -RETURN - CASE - WHEN m:D:E THEN m.p - ELSE null - END AS result ----- - -[[label-expressions-examples]] -=== Examples - -The following graph is used for the examples below: - -//// -[source, cypher, role=test-setup] ----- -MATCH (n:Toy|Cat|Dog|Person|Swedish) DETACH DELETE n; - -CREATE - (:A {name:'Alice'}), - (:B {name:'Bob'}), - (:C {name:'Charlie'}), - (:A:B {name:'Daniel'}), - (:A:C {name:'Eskil'}), - (:B:C {name:'Frank'}), - (:A:B:C {name:'George'}), - ({name:'Henry'}) ----- -//// - -image:graph_label_expressions.svg[] - - -[discrete] -[[label-expressions-node-pattern-without-label-expressions]] -=== Node pattern without label expressions - -A node pattern without a label expression returns all nodes in the graph, including nodes without labels. - -.+Label expression+ -====== - -[source, cypher] ----- -MATCH (n) -RETURN n.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(:B), - (:C)-[:R2 {name:'Studies'}]->(:D), - (:E)-[:R3 {name:'Parents'}]->(:F) ----- -//// - -[[relationship-type-expressions-pattern-without-relationship-type-expression]] -== Relationship pattern without relationship type expression - -A relationship pattern without a relationship type expression returns all relationships in the graph. - - -.Relationship type expressions -====== - -[source, cypher] ----- -MATCH ()-[r]->() -RETURN r.name as name ----- - -[role="queryresult",options="header,footer",cols="1*() -RETURN r.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*() -RETURN r.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*() -RETURN r.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*() -RETURN r.name as name ----- - -[role="queryresult",options="header,footer",cols="1*(m) -WHERE r:R1|R2 -RETURN r.name AS name ----- - -[role="queryresult",options="header,footer",cols="1*(m) -RETURN r:R1|R2 AS result ----- - -[role="queryresult",options="header,footer",cols="1*(m) -RETURN -CASE - WHEN n:A&B THEN 1 - WHEN r:!R1&!R2 THEN 2 - ELSE -1 -END AS result ----- - -[role="queryresult",options="header,footer",cols="1*(:Dog) } AS numDogs, _This feature was introduced in Neo4j 5.3._ -`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]. +`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 xref::subqueries/call-subquery.adoc[`CALL` subqueries]. Any variables returned in a `COUNT` subquery will not be available after the subquery. [source, cypher] diff --git a/modules/ROOT/pages/subqueries/existential.adoc b/modules/ROOT/pages/subqueries/existential.adoc index dfe8e7117..50004a53c 100644 --- a/modules/ROOT/pages/subqueries/existential.adoc +++ b/modules/ROOT/pages/subqueries/existential.adoc @@ -46,7 +46,7 @@ CREATE == Simple `EXISTS` subquery 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]. +In this regard, `EXISTS` subqueries are different from `CALL` subqueries, xref::subqueries/call-subquery.adoc#call-importing-variables[which do require importing]. The following example shows this: @@ -232,7 +232,7 @@ RETURN person.name AS name _This feature was introduced in Neo4j 5.3._ `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]. +need to be aliased, which is different compared to xref::subqueries/call-subquery.adoc[`CALL` subqueries]. Any variables returned in an `EXISTS` subquery will not be available after the subquery. [source, cypher] diff --git a/modules/ROOT/pages/syntax/operators.adoc b/modules/ROOT/pages/syntax/operators.adoc index 6d085fd34..614b9872f 100644 --- a/modules/ROOT/pages/syntax/operators.adoc +++ b/modules/ROOT/pages/syntax/operators.adoc @@ -406,9 +406,9 @@ For example, `1 > b` and `1 < b` are both false when b is NaN. *** xref::values-and-types/temporal.adoc[`ZONED TIME`] *** xref::values-and-types/temporal.adoc[`LOCAL TIME`] *** xref::values-and-types/temporal.adoc[`DURATION`] - *** xref::expressions/index.adoc#expressions-general[`STRING`] - *** xref::expressions/index.adoc#expressions-general[`BOOLEAN`] - *** Numbers: xref::expressions/index.adoc#expressions-general[`INTEGER`, `FLOAT`] + *** xref::queries/expressions.adoc#string[`STRING`] + *** xref::queries/expressions.adoc#boolean[`BOOLEAN`] + *** Numbers: xref::queries/expressions.adoc#numerical[`INTEGER`, `FLOAT`] ** The value `null` is ordered after all other values. * *Ordering* of constructed type values: ** For the xref::values-and-types/property-structural-constructed.adoc#constructed-types[constructed types] (e.g. maps and lists), elements of the containers are compared pairwise for ordering and thus determine the ordering of two container types. diff --git a/modules/ROOT/pages/syntax/parsing.adoc b/modules/ROOT/pages/syntax/parsing.adoc index 0f8c22ecc..492146f48 100644 --- a/modules/ROOT/pages/syntax/parsing.adoc +++ b/modules/ROOT/pages/syntax/parsing.adoc @@ -16,7 +16,7 @@ Unicodes can generally be escaped as `\uxxx`. Additional documentation on escaping rules for string literals, names and regular expressions can be found here: -* xref::expressions/index.adoc#expressions-string-literals[String literal escape sequences] +* xref::queries/expressions.adoc#expressions-string-literals[String literal escape sequences] * xref::syntax/naming.adoc#symbolic-names-escaping-rules[Using special characters in names] * xref::clauses/where.adoc#escaping-in-regular-expressions[Regular epxressions] diff --git a/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc b/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc index 6b6add9b8..a490fefc0 100644 --- a/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc +++ b/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc @@ -17,7 +17,7 @@ The following data types are included in the property types category: `BOOLEAN`, * Property types can be returned from Cypher queries. * Property types can be used as xref::syntax/parameters.adoc[parameters]. * Property types can be stored as properties. -* Property types can be constructed with xref::expressions/index.adoc[Cypher literals]. +* Property types can be constructed with xref::queries/expressions.adoc[Cypher literals]. Homogeneous lists of simple types can be stored as properties, although lists in general (see xref::values-and-types/property-structural-constructed.adoc#constructed-types[Constructed types]) cannot be stored as properties. Lists stored as properties cannot contain `null` values. @@ -35,7 +35,7 @@ The following data types are included in the structural types category: `NODE`, * Structural types can be returned from Cypher queries. * Structural types cannot be used as xref::syntax/parameters.adoc[parameters]. * Structural types cannot be stored as properties. -* Structural types cannot be constructed with xref::expressions/index.adoc[Cypher literals]. +* Structural types cannot be constructed with xref::queries/expressions.adoc[Cypher literals]. The `NODE` data type includes: id, label(s), and a map of properties. Note that labels are not values, but a form of pattern syntax. @@ -59,7 +59,7 @@ The following data types are included in the constructed types category: `LIST` * Constructed types can be returned from Cypher queries. * Constructed types can be used as xref::syntax/parameters.adoc[parameters]. * Constructed types cannot be stored as properties (with the exception of homogenous lists). -* Constructed types can be constructed with xref::expressions/index.adoc[Cypher literals]. +* Constructed types can be constructed with xref::queries/expressions.adoc[Cypher literals]. The `LIST` data type can be either a homogenous collection of simple values, or a heterogeneous, ordered collection of values, each of which can have any property, structural or constructed type. @@ -72,7 +72,7 @@ For more details, see xref::values-and-types/working-with-null.adoc[working with == Types and their synonyms The table below shows the types and their syntactic synonyms. -These types (and their synonyms) can be used in xref::expressions/type-predicate.adoc[type predicate expressions] and in xref::constraints/examples.adoc#constraints-examples-node-property-type[node] and xref::constraints/examples.adoc#constraints-examples-relationship-property-type[relationship] property type constraints. +These types (and their synonyms) can be used in xref::values-and-types/type-predicate.adoc[type predicate expressions] and in xref::constraints/examples.adoc#constraints-examples-node-property-type[node] and xref::constraints/examples.adoc#constraints-examples-relationship-property-type[relationship] property type constraints. However, not all types can be used in all places. [.synonyms, opts="header", cols="2a,2a"] diff --git a/modules/ROOT/pages/values-and-types/working-with-null.adoc b/modules/ROOT/pages/values-and-types/working-with-null.adoc index e91f62a0c..7c96a40fa 100644 --- a/modules/ROOT/pages/values-and-types/working-with-null.adoc +++ b/modules/ROOT/pages/values-and-types/working-with-null.adoc @@ -5,7 +5,7 @@ In Cypher, `null` is used to represent missing or undefined values. All data types in Cypher are nullable. -This means that xref::expressions/type-predicate.adoc#type-predicate-null[type predicate expressions] always return `true` for `null` values. +This means that xref::values-and-types/type-predicate.adoc#type-predicate-null[type predicate expressions] always return `true` for `null` values. Conceptually, `null` means **a missing or unknown value**, and it is treated somewhat differently from other values. For example, returning a property from a node that does not have said property produces `null`. From 01c5587061397681970716bf7844674dd8af97f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 12 Sep 2023 15:17:35 +0200 Subject: [PATCH 11/24] first post-review corrections --- modules/ROOT/content-nav.adoc | 2 +- modules/ROOT/pages/queries/basic.adoc | 54 ++++++++++--------- modules/ROOT/pages/queries/case.adoc | 53 +++++++++--------- modules/ROOT/pages/queries/concepts.adoc | 18 +++---- modules/ROOT/pages/queries/expressions.adoc | 33 ++++++------ .../ROOT/pages/subqueries/call-subquery.adoc | 45 ++++++++++------ modules/ROOT/pages/subqueries/collect.adoc | 6 +-- modules/ROOT/pages/subqueries/index.adoc | 9 +--- .../values-and-types/type-predicate.adoc | 7 +-- 9 files changed, 120 insertions(+), 107 deletions(-) diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index 7166a366d..b729a223d 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -8,7 +8,7 @@ ** xref:queries/concepts.adoc[] ** xref:queries/basic.adoc[] ** xref:queries/expressions.adoc[] -** xref:queries/expressions.adoc[] +** xref:queries/case.adoc[] * xref:clauses/index.adoc[] ** xref:clauses/clause_composition.adoc[] diff --git a/modules/ROOT/pages/queries/basic.adoc b/modules/ROOT/pages/queries/basic.adoc index e93300bec..3d11a1849 100644 --- a/modules/ROOT/pages/queries/basic.adoc +++ b/modules/ROOT/pages/queries/basic.adoc @@ -559,8 +559,7 @@ RETURN keanu.name AS name, keanu.born AS born |=== | name | born -| "Keanu Reeves" -| 1964 +| "Keanu Reeves" | 1964 2+d|Rows: 1 |=== @@ -593,7 +592,7 @@ LIMIT 5 == Note on clause composition Similar to SQL, Cypher queries are constructed using various clauses which are chained together to feed intermediate results between each other. -Each clause has as input the state of the graph and a table of intermediate results consisting of the current variables. +Each clause has as input the state of the graph and a table of intermediate results consisting of the referenced variables. The first clause takes as input the state of the graph before the query and an empty table of intermediate results. The output of a clause is a new state of the graph and a new table of intermediate results, serving as input to the next clause. The output of the last clause is the result of the query. @@ -603,7 +602,7 @@ Note that if one of the clauses returns an empty table of intermediate results, For example, by replacing a `MATCH` clause with xref:clauses/optional-match.adoc[OPTIONAL MATCH].) In the below example, the first `MATCH` clause finds all nodes with the `Person` label. -The second clause will then filter those nodes to find all `Person` nodes who were bron in the 1980s. +The second clause will then filter those nodes to find all `Person` nodes who were born in the 1980s. The final clause returns the result in a descending chronological order. .Query @@ -611,7 +610,8 @@ The final clause returns the result in a descending chronological order. ---- MATCH (bornInEighties:Person) WHERE bornInEighties.born >= 1980 AND bornInEighties.born < 1990 -RETURN bornInEighties.name as name, bornInEighties.born as born ORDER BY born DESC +RETURN bornInEighties.name as name, bornInEighties.born as born +ORDER BY born DESC ---- .Result @@ -668,17 +668,17 @@ The below query searches the graph for outgoing relationships from the `Tom Hank [source, cypher] ---- MATCH (tom:Person {name:'Tom Hanks'})-[r]->(m:Movie) -Return type(r), m.title AS movies +RETURN type(r) AS type, m.title AS movie ---- -The graph returned shows that he has 13 outgoing relationships connected to 12 different `Movie` nodes (12 have the `ACTED_IN` type and one has the `DIRECTED` type). +The returbed table shows that he has 13 outgoing relationships connected to 12 different `Movie` nodes (12 have the `ACTED_IN` type and one has the `DIRECTED` type). image::introduction_example1.svg[width="500",role="middle"] .Result [role="queryresult",options="header,footer",cols="2*(m:Movie) -Return type(r), m.title AS movies +MATCH (:Person {name:'Tom Hanks'})-[r:!ACTED_IN]->(m:Movie) +Return type(r) AS type, m.title AS movies ---- .Result [role="queryresult",options="header,footer",cols="2*`) indicating the direction of a relationship. [source, cypher] ---- -MATCH (n:Person {name: 'Anna'})-[r:KNOWS {since < 2020}]->(friend:Person}) +MATCH (:Person {name: 'Anna'})-[r:KNOWS WHERE r.since < 2020}]->(friend:Person}) RETURN count(friend) As numberOfFriends ---- Unlike nodes, information within a relationship pattern must be enclosed by square brackets. In this case, all outgoing relationships from the start node must be of the type `KNOWS` and have the property `since` set to less than `2020`. -The xref:functions/aggregating.adoc#functions-count[count()] function is used in the `RETURN` clause to count all nodes bound by the `friend` variable in the preceding `MATCH` clause (i.e. how many friends Anna has known since before 2020). +The xref:functions/aggregating.adoc#functions-count[count()] aggregate function is used in the `RETURN` clause to count all nodes bound by the `friend` variable in the preceding `MATCH` clause (i.e. how many friends Anna has known since before 2020). Note that while nodes can have several labels, relationships can only have one type. @@ -62,11 +62,11 @@ By applying a declarative path pattern in a query, you can find simple or comple [source, cypher] ---- -MATCH (n:Person {name: 'Anna'})-[:KNOWS]-{1,5}(friends:Person WHERE n.born < friends.born) -RETURN DISTINCT friends.name AS youngerFriends +MATCH (n:Person {name: 'Anna'})-[:KNOWS]-{1,5}(friend:Person WHERE n.born < friend.born) +RETURN DISTINCT friend.name AS youngerFriends ---- -This example uses a xref:patterns/concepts.adoc#quantified-relationships[quantified relationship] to find all paths of a specific length (between `1` and `5` 'hops') traversing only relationships of type `KNOWS` from the start node `Anna` to other `Person` nodes with a lower `born` property (as defined by the xref:clauses/where.adoc[] clause). +This example uses a xref:patterns/concepts.adoc#quantified-relationships[quantified relationship] to find all paths of a specific length (between `1` and `5` "hops") traversing only relationships of type `KNOWS` from the start node `Anna` to other `Person` nodes with a lower `born` property (as defined by the xref:clauses/where.adoc[] clause). The xref:syntax/operators.adoc#syntax-using-the-distinct-operator[DISTINCT] operator is used to ensure that the `RETURN` clause only returns unique nodes. Patterns can also be assigned variables. @@ -75,7 +75,7 @@ In this case, the `RETURN` clause returns the full path pattern between the two [source, cypher] ---- -MATCH p = shortestPath((n:Person {name:'Anna'})-[:KNOWS*]-(canadian:Person {nationality: 'Canadian'})) +MATCH p=shortestPath((:Person {name:'Anna'})-[:KNOWS*]-(:Person {nationality: 'Canadian'})) RETURN p ---- diff --git a/modules/ROOT/pages/queries/expressions.adoc b/modules/ROOT/pages/queries/expressions.adoc index 383a11aa9..155ecec3d 100644 --- a/modules/ROOT/pages/queries/expressions.adoc +++ b/modules/ROOT/pages/queries/expressions.adoc @@ -5,51 +5,51 @@ This page contains examples of allowed expressions in Cypher. [NOTE] ==== Most expressions in Cypher evaluate to `null` if any of their inner expressions are `null`. -Notable exceptions are the operators `IS NULL`, `IS NOT NULL`, and the type predicate expression. +Notable exceptions are the operators `IS NULL`, `IS NOT NULL`, and the xref:values-and-types/type-predicate.adoc[type predicate expressions]. ==== [[general]] == General -* A variable: `n`, `x`, `rel`, `myFancyVariable`, `++`A name with weird stuff in it[]!`++`. -* A property: `n.prop`, `x.prop`, `rel.thisProperty`, `++myFancyVariable.`(weird property name)`++`. +* A variable: `n`, `x`, `rel`, `myFancyVariable`, `++`A name with special characters in it[]!`++`. +* A property: `n.prop`, `x.prop`, `rel.thisProperty`, `++myFancyVariable.`(special property name)`++`. * A dynamic property: `n["prop"]`, `rel[n.city + n.zip]`, `map[coll[0]]`. * A parameter: `$param`, `$0`. * A list of expressions: `['a', 'b']`, `[1, 2, 3]`, `['a', 2, n.property, $param]`, `[]`. * A function call: `length(p)`, `nodes(p)`. -* An aggregate function: `avg(x.prop)`, `+count(*)+`. +* An aggregate function call: `avg(x.prop)`, `+count(*)+`. * A path-pattern: `+(a)-[r]->(b)+`, `+(a)-[r]-(b)+`, `+(a)--(b)+`, `+(a)-->()<--(b)+`. * An operator application: `1 + 2`, `3 < 4`. -* A subquery expression. +* A subquery expression: `COUNT {}`, `COLLECT {}`, `EXISTS {}`, `CALL {}`. * A regular expression: `a.name =~ 'Tim.*'`. * A `CASE` expression. [NOTE] ==== -Using regular expressions with unsanitized user input makes you vulnerable to Cypher injection. +Using regular expressions with unsanitized user input may make your application vulnerable to Cypher injection. Consider using xref:syntax/parameters.adoc[parameters] instead. ==== [[numerical]] == Numerical -* A decimal (integer or float) literal: `13`, `-40000`, `3.14`. -* A decimal (integer or float) literal in scientific notation: `6.022E23`. -* A hexadecimal integer literal (starting with `0x`): `0x13af`, `0xFC3A9`, `-0x66eff`. -* An octal integer literal (starting with `0o`): `0o1372`, `-0o5671`. -* A float literal: `Inf`, `Infinity`, `NaN`. +* A numeric (`INTEGER` or `FLOAT`) literal: `13`, `-40000`, `3.14`. +* A numeric (`INTEGER` or `FLOAT`) literal in scientific notation: `6.022E23`. +* A hexadecimal `INTEGER` literal (starting with `0x`): `0x13af`, `0xFC3A9`, `-0x66eff`. +* An octal `INTEGER` literal (starting with `0o`): `0o1372`, `-0o5671`. +* A `FLOAT` literal: `Inf`, `Infinity`, `NaN`. [NOTE] ==== -Any number literal may contain an underscore `_` between digits. +Any numeric literal may contain an underscore `_` between digits. There may be an underscore between the `0x` or `0o` and the digits for hexadecimal and octal literals. ==== [[string]] == String -* A string literal: `'Hello'`, `"World"`. -* A case-sensitive string matching expression: `a.surname STARTS WITH 'Sven'`, `a.surname ENDS WITH 'son'` or `a.surname CONTAINS 'son'`. +* A `STRING` literal: `'Hello'`, `"World"`. +* A case-sensitive `STRING` matching expression: `a.surname STARTS WITH 'Sven'`, `a.surname ENDS WITH 'son'` or `a.surname CONTAINS 'son'`. [[expressions-string-literals]] === String literal escape sequences @@ -73,7 +73,6 @@ String literals can contain the following escape sequences: [[boolean]] == Boolean -* A boolean literal: `true`, `false`. -* A predicate expression is an expression that returns `true` or `false`: `a.prop = 'Hello'`, `length(p) > 10`, `a.name IS NOT NULL`. +* A `BOOLEAN` literal: `true`, `false`. +* A predicate expression is an expression that returns a `BOOLEAN` value, `true`, `false` or `null`: `a.prop = 'Hello'`, `length(p) > 10`, `a.name IS NOT NULL`. * Label and relationship type expressions: `(n:A|B)`, `()-[r:R1|R2]->()`. - diff --git a/modules/ROOT/pages/subqueries/call-subquery.adoc b/modules/ROOT/pages/subqueries/call-subquery.adoc index a48d1d683..955027b6d 100644 --- a/modules/ROOT/pages/subqueries/call-subquery.adoc +++ b/modules/ROOT/pages/subqueries/call-subquery.adoc @@ -3,9 +3,15 @@ [[call]] = CALL {} -The `CALL` clause is used to call a subquery. +The `CALL` clause can be used to invoke a subquery. Unlike other subqueries in Cypher, it can be used to perform changes to the database (e.g. xref:clauses/create.adoc[] new nodes), and it requires an importing xref:clauses/with.adoc[] clause. +There are restrictions on how `CALL` subqueries interact with enclosing queries: + +* A subquery can only refer to variables from the enclosing query if they are explicitly imported. +* A subquery cannot return variables with the same names as variables in the enclosing query. +* All variables that are returned from a subquery are afterwards available in the enclosing query. + [NOTE] ==== The `CALL` clause is also used for calling procedures. @@ -23,7 +29,7 @@ CALL LCURLY regularQuery RCURLY subqueryInTransactionsParameters? [[call-returning-unit]] == Returning subqueries and unit subqueries -Subqueries which end in a `RETURN` statement are called _returning subqueries_ while subqueries without such a return statement are called _unit subqueries_. +Subqueries which end with a `RETURN` statement are called _returning subqueries_ while subqueries without such a return statement are called _unit subqueries_. A subquery is evaluated for each incoming input row. Every output row of a _returning subquery_ is combined with the input row to build the result of the subquery. @@ -31,7 +37,7 @@ That means that a returning subquery will influence the number of rows. If the subquery does not return any rows, there will be no rows available after the subquery. _Unit subqueries_, on the other hand, are called for their ability to alter the graph with clauses such as `CREATE`, `MERGE`, `SET`, and `DELETE`. -They not explicitly return anything, and this means that the number of rows present after the subquery is the same as was going into the subquery. +They do not explicitly return anything, and this means that the number of rows present after the subquery is the same as was going into the subquery. [[call-example-graph]] == Example graph @@ -57,12 +63,12 @@ CREATE (:Counter {count: 0}) [[call-semantics]] == Semantics -A `CALL` clause is executed once for each incoming row. +A `CALL` subquery is executed once for each incoming row. .Execute for each incoming row ====== -The `CALL` clause executes three times, one for each row that the `UNWIND` clause outputs. +The `CALL` subquery executes three times, one for each row that the `UNWIND` clause outputs. .Query [source, cypher] @@ -129,6 +135,12 @@ RETURN == Importing variables into subqueries Variables are imported into a subquery using an importing `WITH` clause. + +[NOTE] +==== +References to a variable in the outer scope that were not imported will introduce a new variable. +==== + As the subquery is evaluated for each incoming input row, the imported variables get bound to the corresponding values from the input row in each evaluation. .Query @@ -169,7 +181,7 @@ If a query result depends on the order of execution of subqueries, an `ORDER BY` This query creates a linked list of all `:Person` nodes in order of ascending age. -The `CALL` clause is relying on the incoming row ordering to ensure that a correctly linked list is created, thus the incoming rows must be ordered with a preceding `ORDER BY` clause. +The `CALL` clause is relying on the incoming row ordering to ensure that a correctly ordered linked list is created, thus the incoming rows must be ordered with a preceding `ORDER BY` clause. .Query [source, cypher] @@ -178,16 +190,15 @@ MATCH (person:Person) WITH person ORDER BY person.age ASC LIMIT 1 SET person:ListHead WITH * -MATCH (next: Person) - WHERE NOT next:ListHead -WITH next ORDER BY next.age +MATCH (nextPerson: Person&!ListHead) +WITH nextPerson ORDER BY nextPerson.age CALL { - WITH next + WITH nextPerson MATCH (current:ListHead) REMOVE current:ListHead - SET next:ListHead - CREATE(current)-[r:IS_YOUNGER_THAN]->(next) - RETURN current AS from, next AS to + SET nextPerson:ListHead + CREATE(current)-[:IS_YOUNGER_THAN]->(nextPerson) + RETURN current AS from, nextPerson AS to } RETURN from.name AS name, @@ -214,7 +225,7 @@ RETURN [[call-post-union]] == Post-union processing -Subqueries can be used to process the results of a `UNION` query further. +Call subqueries can be used to process the results of a `UNION` query further. This example query finds the youngest and the oldest person in the database and orders them by name. .Query @@ -244,7 +255,7 @@ ORDER BY p.name 2+d|Rows: 2 |=== -If different parts of a result should be matched differently, with some aggregation over the whole results, subqueries need to be used. +If different parts of a result should be matched differently, with some aggregation over the whole result, subqueries need to be used. This example query finds friends and/or parents for each person. Subsequently the number of friends and parents are counted together. @@ -279,7 +290,7 @@ RETURN DISTINCT p.name, count(other) [[call-aggregation]] == Aggregations -Returning subqueries change the number of results of the query: The result of the `CALL` clause is the combined result of evaluating the subquery for each input row. +Returning subqueries change the number of results of the query: The result of the `CALL` subquery is the combined result of evaluating the subquery for each input row. The following example finds the name of each person and the names of their friends: @@ -307,7 +318,7 @@ RETURN p.name, friend The number of results of the subquery changed the number of results of the enclosing query: Instead of 4 rows, one for each node), there are now 2 rows which were found for Alice and Bob respectively. No rows are returned for Charlie and Dora since they have no friends in our example graph. We can also use subqueries to perform isolated aggregations. In this example we count the number of relationships each person has. -As we get one row from each evaluation of the subquery, the number of rows is the same, before and after the `CALL` clause: +As we get one row from each evaluation of the subquery, the number of rows is the same, before and after the `CALL` subquery: .Query [source, cypher] diff --git a/modules/ROOT/pages/subqueries/collect.adoc b/modules/ROOT/pages/subqueries/collect.adoc index c9f2a1b1a..60382be15 100644 --- a/modules/ROOT/pages/subqueries/collect.adoc +++ b/modules/ROOT/pages/subqueries/collect.adoc @@ -63,7 +63,7 @@ RETURN person.name AS name [[collect-where]] == `COLLECT` subquery with `WHERE` clause -A `WHERE` clause can be used inside the `COLLECT` pattern. +A `WHERE` clause can be used inside the `COLLECT` subquery. Variables introduced by the `MATCH` clause and the outside scope can be used in the inner scope. [source, cypher] @@ -255,7 +255,7 @@ RETURN COLLECT { MATCH (person)-[:HAS_DOG]->(d:Dog) RETURN d.name } AS dogNames, [role="queryresult",options="header,footer",cols="2*(d:Dog) RETURN d.name } AS dogNames, [[collect-differentiation]] == Using `COLLECT` vs `collect()` -`COLLECT` does not handle `null` values in the same way that function `collect()` does. +`COLLECT` does not handle `null` values in the same way that the aggregating function `collect()` does. The `collect()` function automatically removes `null` values. `COLLECT` will not remove `null` values automatically. However, they can be removed by adding a filtering step in the subquery. diff --git a/modules/ROOT/pages/subqueries/index.adoc b/modules/ROOT/pages/subqueries/index.adoc index 8dc05d3f4..236e76e96 100644 --- a/modules/ROOT/pages/subqueries/index.adoc +++ b/modules/ROOT/pages/subqueries/index.adoc @@ -1,13 +1,8 @@ = Subqueries A Cypher subquery is called from an enclosing outer query, and executes within its own scope, as defined by `{` and `}`. -A subquery can appear anywhere in a query that an expression is valid. - -There are restrictions on how subqueries interact with the enclosing query: - -* A subquery can only refer to variables from the enclosing query if they are explicitly imported. -* A subquery cannot return variables with the same names as variables in the enclosing query. -* All variables that are returned from a subquery are afterwards available in the enclosing query. +Subqueries can appear anywhere in a query that an expression is valid. +Any variable that is defined in the outside scope can be referenced inside the subquery’s own scope. Variables introduced inside the subquery are not part of the outside scope and therefore cannot be accessed on the outside. For more information, see the following sections: diff --git a/modules/ROOT/pages/values-and-types/type-predicate.adoc b/modules/ROOT/pages/values-and-types/type-predicate.adoc index 72ee4a5a4..2fa06c582 100644 --- a/modules/ROOT/pages/values-and-types/type-predicate.adoc +++ b/modules/ROOT/pages/values-and-types/type-predicate.adoc @@ -57,8 +57,9 @@ RETURN val, val IS NOT :: STRING AS notString [[type-predicate-null]] == Type predicate expressions for null -As of Neo4j 5.10, a Cypher type includes the `null` value unless it is explicitly appended with `NOT NULL`. -Therefore, `IS ::` returns `true` for all expressions evaluating to `null`, unless they explicitly remove it. +Cypher types includes the `null` value. +Since Neo4j 5.10, type predicate expressions can be appended with `NOT NULL`. +This means that `IS ::` returns `true` for all expressions evaluating to `null`, unless `NOT NULL` is appended. [source, cypher] ---- @@ -162,7 +163,7 @@ Types not storable in properties, such as `MAP`, will return `false` when checke For numerical values passed in as parameters, Cypher does not take the size of the number into account. Cypher will therefore regard any exact numerical parameter as an `INTEGER` regardless of its declared size. For example, an `INT16` or an `INT32` passed through from a client programming language will both be treated by Cypher as an `INTEGER`. -Note that any exact numerical parameter used must fit within the range of an `INT 64`. +Note that any exact numerical parameter used must fit within the range of an `INT64`. [source, cypher, role=test-skip] ---- From 653b849970cbffcd6aed06ca9becfa71ff05483d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 12 Sep 2023 15:21:48 +0200 Subject: [PATCH 12/24] fix --- modules/ROOT/pages/queries/concepts.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/queries/concepts.adoc b/modules/ROOT/pages/queries/concepts.adoc index aa62969b9..7074d1933 100644 --- a/modules/ROOT/pages/queries/concepts.adoc +++ b/modules/ROOT/pages/queries/concepts.adoc @@ -43,7 +43,7 @@ Relationships are represented in Cypher with dashes (`-`) and arrows (e.g. `-->` [source, cypher] ---- -MATCH (:Person {name: 'Anna'})-[r:KNOWS WHERE r.since < 2020}]->(friend:Person}) +MATCH (:Person {name: 'Anna'})-[r:KNOWS WHERE r.since < 2020]->(friend:Person) RETURN count(friend) As numberOfFriends ---- From 538a680f3975edbc6ce4cc42477914ac1e394c24 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 Sep 2023 11:28:30 +0200 Subject: [PATCH 13/24] major clean up, and delete tutorial file from intro --- modules/ROOT/content-nav.adoc | 1 - .../pages/introduction/cypher_tutorial.adoc | 877 ------------------ modules/ROOT/pages/introduction/index.adoc | 1 - modules/ROOT/pages/queries/basic.adoc | 13 +- modules/ROOT/pages/queries/case.adoc | 1 + modules/ROOT/pages/queries/concepts.adoc | 3 +- modules/ROOT/pages/queries/expressions.adoc | 1 + modules/ROOT/pages/queries/index.adoc | 3 +- .../ROOT/pages/subqueries/call-subquery.adoc | 83 +- modules/ROOT/pages/subqueries/collect.adoc | 13 +- modules/ROOT/pages/subqueries/count.adoc | 19 +- .../ROOT/pages/subqueries/existential.adoc | 11 +- modules/ROOT/pages/subqueries/index.adoc | 3 +- .../subqueries-in-transactions.adoc | 48 +- .../values-and-types/type-predicate.adoc | 4 +- 15 files changed, 92 insertions(+), 989 deletions(-) delete mode 100644 modules/ROOT/pages/introduction/cypher_tutorial.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index b729a223d..c69cac9f5 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -1,6 +1,5 @@ * xref:introduction/index.adoc[] ** xref:introduction/cypher_overview.adoc[] -** xref:introduction/cypher_tutorial.adoc[] ** xref:introduction/cypher_neo4j.adoc[] ** xref:introduction/cypher_aura.adoc[] diff --git a/modules/ROOT/pages/introduction/cypher_tutorial.adoc b/modules/ROOT/pages/introduction/cypher_tutorial.adoc deleted file mode 100644 index fcd0cf77d..000000000 --- a/modules/ROOT/pages/introduction/cypher_tutorial.adoc +++ /dev/null @@ -1,877 +0,0 @@ -[[cypher-tutorial]] -= Tutorial -:description: This section provides an overview of Cypher, and goes through a short Cypher tutorial based on the Neo4j movie database. - -In this short tutorial, users will learn how to create, query, and delete a property graph database using Cypher. -The tutorial uses the xref:https://github.com/neo4j-graph-examples/movies/tree/main/documentation[Neo4j movie database]. - -== Creating a data model - -Before creating a property graph database, it is important to develop an appropriate data model. -This will provide structure to the data, and allow users of the graph to efficiently retrieve the information they are looking for. - -The following data model is used for the Neo4j data model: - -image::introduction_schema.svg[width="800",role="middle"] - -It includes two types of node labels: - -* `Person` nodes, which have the following properties: `name` (`STRING`) and `born` (`INTEGER`). -* `Movie` nodes, which have the following properties: `title` (`STRING`), `released` (`INTEGER`), and `tagline` (`STRING`). - -The data model also contains five different relationship types between the `Person` and `Movie` nodes: `ACTED_IN`, `DIRECTED`, `PRODUCED`, `WROTE`, and `REVIEWED`. Two of the relationship types have properties: - -* The `ACTED_IN` relationship type, which has the `roles` property (`STRING`). -* The `REVIEWED` relationship type, which has a `summary` property (`STRING`) and a `rating` property (`FLOAT`). - -_To learn more about data modelling for graph databases, enroll in the free -https://graphacademy.neo4j.com/courses/modeling-fundamentals/[Graph Data Modelling Fundamentals] course offered by GraphAcademy._ - -== Creating a property graph database - -The complete Cypher query to create the Neo4j movie database, can be found https://github.com/neo4j-graph-examples/movies/blob/main/scripts/movies.cypher[here]. -To create the full graph, run the full query against an empty Neo4j database. - -//// -[source, cypher, role=test-setup] ----- -CREATE (TheMatrix:Movie {title:'The Matrix', released:1999, tagline:'Welcome to the Real World'}) -CREATE (Keanu:Person {name:'Keanu Reeves', born:1964}) -CREATE (Carrie:Person {name:'Carrie-Anne Moss', born:1967}) -CREATE (Laurence:Person {name:'Laurence Fishburne', born:1961}) -CREATE (Hugo:Person {name:'Hugo Weaving', born:1960}) -CREATE (LillyW:Person {name:'Lilly Wachowski', born:1967}) -CREATE (LanaW:Person {name:'Lana Wachowski', born:1965}) -CREATE (JoelS:Person {name:'Joel Silver', born:1952}) -CREATE -(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrix), -(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrix), -(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrix), -(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrix), -(LillyW)-[:DIRECTED]->(TheMatrix), -(LanaW)-[:DIRECTED]->(TheMatrix), -(JoelS)-[:PRODUCED]->(TheMatrix) - -CREATE (Emil:Person {name:"Emil Eifrem", born:1978}) -CREATE (Emil)-[:ACTED_IN {roles:["Emil"]}]->(TheMatrix) - -CREATE (TheMatrixReloaded:Movie {title:'The Matrix Reloaded', released:2003, tagline:'Free your mind'}) -CREATE -(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixReloaded), -(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixReloaded), -(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixReloaded), -(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixReloaded), -(LillyW)-[:DIRECTED]->(TheMatrixReloaded), -(LanaW)-[:DIRECTED]->(TheMatrixReloaded), -(JoelS)-[:PRODUCED]->(TheMatrixReloaded) - -CREATE (TheMatrixRevolutions:Movie {title:'The Matrix Revolutions', released:2003, tagline:'Everything that has a beginning has an end'}) -CREATE -(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixRevolutions), -(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixRevolutions), -(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixRevolutions), -(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixRevolutions), -(LillyW)-[:DIRECTED]->(TheMatrixRevolutions), -(LanaW)-[:DIRECTED]->(TheMatrixRevolutions), -(JoelS)-[:PRODUCED]->(TheMatrixRevolutions) - -CREATE (TheDevilsAdvocate:Movie {title:"The Devil's Advocate", released:1997, tagline:'Evil has its winning ways'}) -CREATE (Charlize:Person {name:'Charlize Theron', born:1975}) -CREATE (Al:Person {name:'Al Pacino', born:1940}) -CREATE (Taylor:Person {name:'Taylor Hackford', born:1944}) -CREATE -(Keanu)-[:ACTED_IN {roles:['Kevin Lomax']}]->(TheDevilsAdvocate), -(Charlize)-[:ACTED_IN {roles:['Mary Ann Lomax']}]->(TheDevilsAdvocate), -(Al)-[:ACTED_IN {roles:['John Milton']}]->(TheDevilsAdvocate), -(Taylor)-[:DIRECTED]->(TheDevilsAdvocate) - -CREATE (AFewGoodMen:Movie {title:"A Few Good Men", released:1992, tagline:"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth."}) -CREATE (TomC:Person {name:'Tom Cruise', born:1962}) -CREATE (JackN:Person {name:'Jack Nicholson', born:1937}) -CREATE (DemiM:Person {name:'Demi Moore', born:1962}) -CREATE (KevinB:Person {name:'Kevin Bacon', born:1958}) -CREATE (KieferS:Person {name:'Kiefer Sutherland', born:1966}) -CREATE (NoahW:Person {name:'Noah Wyle', born:1971}) -CREATE (CubaG:Person {name:'Cuba Gooding Jr.', born:1968}) -CREATE (KevinP:Person {name:'Kevin Pollak', born:1957}) -CREATE (JTW:Person {name:'J.T. Walsh', born:1943}) -CREATE (JamesM:Person {name:'James Marshall', born:1967}) -CREATE (ChristopherG:Person {name:'Christopher Guest', born:1948}) -CREATE (RobR:Person {name:'Rob Reiner', born:1947}) -CREATE (AaronS:Person {name:'Aaron Sorkin', born:1961}) -CREATE -(TomC)-[:ACTED_IN {roles:['Lt. Daniel Kaffee']}]->(AFewGoodMen), -(JackN)-[:ACTED_IN {roles:['Col. Nathan R. Jessup']}]->(AFewGoodMen), -(DemiM)-[:ACTED_IN {roles:['Lt. Cdr. JoAnne Galloway']}]->(AFewGoodMen), -(KevinB)-[:ACTED_IN {roles:['Capt. Jack Ross']}]->(AFewGoodMen), -(KieferS)-[:ACTED_IN {roles:['Lt. Jonathan Kendrick']}]->(AFewGoodMen), -(NoahW)-[:ACTED_IN {roles:['Cpl. Jeffrey Barnes']}]->(AFewGoodMen), -(CubaG)-[:ACTED_IN {roles:['Cpl. Carl Hammaker']}]->(AFewGoodMen), -(KevinP)-[:ACTED_IN {roles:['Lt. Sam Weinberg']}]->(AFewGoodMen), -(JTW)-[:ACTED_IN {roles:['Lt. Col. Matthew Andrew Markinson']}]->(AFewGoodMen), -(JamesM)-[:ACTED_IN {roles:['Pfc. Louden Downey']}]->(AFewGoodMen), -(ChristopherG)-[:ACTED_IN {roles:['Dr. Stone']}]->(AFewGoodMen), -(AaronS)-[:ACTED_IN {roles:['Man in Bar']}]->(AFewGoodMen), -(RobR)-[:DIRECTED]->(AFewGoodMen), -(AaronS)-[:WROTE]->(AFewGoodMen) - -CREATE (TopGun:Movie {title:"Top Gun", released:1986, tagline:'I feel the need, the need for speed.'}) -CREATE (KellyM:Person {name:'Kelly McGillis', born:1957}) -CREATE (ValK:Person {name:'Val Kilmer', born:1959}) -CREATE (AnthonyE:Person {name:'Anthony Edwards', born:1962}) -CREATE (TomS:Person {name:'Tom Skerritt', born:1933}) -CREATE (MegR:Person {name:'Meg Ryan', born:1961}) -CREATE (TonyS:Person {name:'Tony Scott', born:1944}) -CREATE (JimC:Person {name:'Jim Cash', born:1941}) -CREATE -(TomC)-[:ACTED_IN {roles:['Maverick']}]->(TopGun), -(KellyM)-[:ACTED_IN {roles:['Charlie']}]->(TopGun), -(ValK)-[:ACTED_IN {roles:['Iceman']}]->(TopGun), -(AnthonyE)-[:ACTED_IN {roles:['Goose']}]->(TopGun), -(TomS)-[:ACTED_IN {roles:['Viper']}]->(TopGun), -(MegR)-[:ACTED_IN {roles:['Carole']}]->(TopGun), -(TonyS)-[:DIRECTED]->(TopGun), -(JimC)-[:WROTE]->(TopGun) - -CREATE (JerryMaguire:Movie {title:'Jerry Maguire', released:2000, tagline:'The rest of his life begins now.'}) -CREATE (ReneeZ:Person {name:'Renee Zellweger', born:1969}) -CREATE (KellyP:Person {name:'Kelly Preston', born:1962}) -CREATE (JerryO:Person {name:"Jerry O'Connell", born:1974}) -CREATE (JayM:Person {name:'Jay Mohr', born:1970}) -CREATE (BonnieH:Person {name:'Bonnie Hunt', born:1961}) -CREATE (ReginaK:Person {name:'Regina King', born:1971}) -CREATE (JonathanL:Person {name:'Jonathan Lipnicki', born:1996}) -CREATE (CameronC:Person {name:'Cameron Crowe', born:1957}) -CREATE -(TomC)-[:ACTED_IN {roles:['Jerry Maguire']}]->(JerryMaguire), -(CubaG)-[:ACTED_IN {roles:['Rod Tidwell']}]->(JerryMaguire), -(ReneeZ)-[:ACTED_IN {roles:['Dorothy Boyd']}]->(JerryMaguire), -(KellyP)-[:ACTED_IN {roles:['Avery Bishop']}]->(JerryMaguire), -(JerryO)-[:ACTED_IN {roles:['Frank Cushman']}]->(JerryMaguire), -(JayM)-[:ACTED_IN {roles:['Bob Sugar']}]->(JerryMaguire), -(BonnieH)-[:ACTED_IN {roles:['Laurel Boyd']}]->(JerryMaguire), -(ReginaK)-[:ACTED_IN {roles:['Marcee Tidwell']}]->(JerryMaguire), -(JonathanL)-[:ACTED_IN {roles:['Ray Boyd']}]->(JerryMaguire), -(CameronC)-[:DIRECTED]->(JerryMaguire), -(CameronC)-[:PRODUCED]->(JerryMaguire), -(CameronC)-[:WROTE]->(JerryMaguire) - -CREATE (StandByMe:Movie {title:"Stand By Me", released:1986, tagline:"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of."}) -CREATE (RiverP:Person {name:'River Phoenix', born:1970}) -CREATE (CoreyF:Person {name:'Corey Feldman', born:1971}) -CREATE (WilW:Person {name:'Wil Wheaton', born:1972}) -CREATE (JohnC:Person {name:'John Cusack', born:1966}) -CREATE (MarshallB:Person {name:'Marshall Bell', born:1942}) -CREATE -(WilW)-[:ACTED_IN {roles:['Gordie Lachance']}]->(StandByMe), -(RiverP)-[:ACTED_IN {roles:['Chris Chambers']}]->(StandByMe), -(JerryO)-[:ACTED_IN {roles:['Vern Tessio']}]->(StandByMe), -(CoreyF)-[:ACTED_IN {roles:['Teddy Duchamp']}]->(StandByMe), -(JohnC)-[:ACTED_IN {roles:['Denny Lachance']}]->(StandByMe), -(KieferS)-[:ACTED_IN {roles:['Ace Merrill']}]->(StandByMe), -(MarshallB)-[:ACTED_IN {roles:['Mr. Lachance']}]->(StandByMe), -(RobR)-[:DIRECTED]->(StandByMe) - -CREATE (AsGoodAsItGets:Movie {title:'As Good as It Gets', released:1997, tagline:'A comedy from the heart that goes for the throat.'}) -CREATE (HelenH:Person {name:'Helen Hunt', born:1963}) -CREATE (GregK:Person {name:'Greg Kinnear', born:1963}) -CREATE (JamesB:Person {name:'James L. Brooks', born:1940}) -CREATE -(JackN)-[:ACTED_IN {roles:['Melvin Udall']}]->(AsGoodAsItGets), -(HelenH)-[:ACTED_IN {roles:['Carol Connelly']}]->(AsGoodAsItGets), -(GregK)-[:ACTED_IN {roles:['Simon Bishop']}]->(AsGoodAsItGets), -(CubaG)-[:ACTED_IN {roles:['Frank Sachs']}]->(AsGoodAsItGets), -(JamesB)-[:DIRECTED]->(AsGoodAsItGets) - -CREATE (WhatDreamsMayCome:Movie {title:'What Dreams May Come', released:1998, tagline:'After life there is more. The end is just the beginning.'}) -CREATE (AnnabellaS:Person {name:'Annabella Sciorra', born:1960}) -CREATE (MaxS:Person {name:'Max von Sydow', born:1929}) -CREATE (WernerH:Person {name:'Werner Herzog', born:1942}) -CREATE (Robin:Person {name:'Robin Williams', born:1951}) -CREATE (VincentW:Person {name:'Vincent Ward', born:1956}) -CREATE -(Robin)-[:ACTED_IN {roles:['Chris Nielsen']}]->(WhatDreamsMayCome), -(CubaG)-[:ACTED_IN {roles:['Albert Lewis']}]->(WhatDreamsMayCome), -(AnnabellaS)-[:ACTED_IN {roles:['Annie Collins-Nielsen']}]->(WhatDreamsMayCome), -(MaxS)-[:ACTED_IN {roles:['The Tracker']}]->(WhatDreamsMayCome), -(WernerH)-[:ACTED_IN {roles:['The Face']}]->(WhatDreamsMayCome), -(VincentW)-[:DIRECTED]->(WhatDreamsMayCome) - -CREATE (SnowFallingonCedars:Movie {title:'Snow Falling on Cedars', released:1999, tagline:'First loves last. Forever.'}) -CREATE (EthanH:Person {name:'Ethan Hawke', born:1970}) -CREATE (RickY:Person {name:'Rick Yune', born:1971}) -CREATE (JamesC:Person {name:'James Cromwell', born:1940}) -CREATE (ScottH:Person {name:'Scott Hicks', born:1953}) -CREATE -(EthanH)-[:ACTED_IN {roles:['Ishmael Chambers']}]->(SnowFallingonCedars), -(RickY)-[:ACTED_IN {roles:['Kazuo Miyamoto']}]->(SnowFallingonCedars), -(MaxS)-[:ACTED_IN {roles:['Nels Gudmundsson']}]->(SnowFallingonCedars), -(JamesC)-[:ACTED_IN {roles:['Judge Fielding']}]->(SnowFallingonCedars), -(ScottH)-[:DIRECTED]->(SnowFallingonCedars) - -CREATE (YouveGotMail:Movie {title:"You've Got Mail", released:1998, tagline:'At odds in life... in love on-line.'}) -CREATE (ParkerP:Person {name:'Parker Posey', born:1968}) -CREATE (DaveC:Person {name:'Dave Chappelle', born:1973}) -CREATE (SteveZ:Person {name:'Steve Zahn', born:1967}) -CREATE (TomH:Person {name:'Tom Hanks', born:1956}) -CREATE (NoraE:Person {name:'Nora Ephron', born:1941}) -CREATE -(TomH)-[:ACTED_IN {roles:['Joe Fox']}]->(YouveGotMail), -(MegR)-[:ACTED_IN {roles:['Kathleen Kelly']}]->(YouveGotMail), -(GregK)-[:ACTED_IN {roles:['Frank Navasky']}]->(YouveGotMail), -(ParkerP)-[:ACTED_IN {roles:['Patricia Eden']}]->(YouveGotMail), -(DaveC)-[:ACTED_IN {roles:['Kevin Jackson']}]->(YouveGotMail), -(SteveZ)-[:ACTED_IN {roles:['George Pappas']}]->(YouveGotMail), -(NoraE)-[:DIRECTED]->(YouveGotMail) - -CREATE (SleeplessInSeattle:Movie {title:'Sleepless in Seattle', released:1993, tagline:'What if someone you never met, someone you never saw, someone you never knew was the only someone for you?'}) -CREATE (RitaW:Person {name:'Rita Wilson', born:1956}) -CREATE (BillPull:Person {name:'Bill Pullman', born:1953}) -CREATE (VictorG:Person {name:'Victor Garber', born:1949}) -CREATE (RosieO:Person {name:"Rosie O'Donnell", born:1962}) -CREATE -(TomH)-[:ACTED_IN {roles:['Sam Baldwin']}]->(SleeplessInSeattle), -(MegR)-[:ACTED_IN {roles:['Annie Reed']}]->(SleeplessInSeattle), -(RitaW)-[:ACTED_IN {roles:['Suzy']}]->(SleeplessInSeattle), -(BillPull)-[:ACTED_IN {roles:['Walter']}]->(SleeplessInSeattle), -(VictorG)-[:ACTED_IN {roles:['Greg']}]->(SleeplessInSeattle), -(RosieO)-[:ACTED_IN {roles:['Becky']}]->(SleeplessInSeattle), -(NoraE)-[:DIRECTED]->(SleeplessInSeattle) - -CREATE (JoeVersustheVolcano:Movie {title:'Joe Versus the Volcano', released:1990, tagline:'A story of love, lava and burning desire.'}) -CREATE (JohnS:Person {name:'John Patrick Stanley', born:1950}) -CREATE (Nathan:Person {name:'Nathan Lane', born:1956}) -CREATE -(TomH)-[:ACTED_IN {roles:['Joe Banks']}]->(JoeVersustheVolcano), -(MegR)-[:ACTED_IN {roles:['DeDe', 'Angelica Graynamore', 'Patricia Graynamore']}]->(JoeVersustheVolcano), -(Nathan)-[:ACTED_IN {roles:['Baw']}]->(JoeVersustheVolcano), -(JohnS)-[:DIRECTED]->(JoeVersustheVolcano) - -CREATE (WhenHarryMetSally:Movie {title:'When Harry Met Sally', released:1998, tagline:'Can two friends sleep together and still love each other in the morning?'}) -CREATE (BillyC:Person {name:'Billy Crystal', born:1948}) -CREATE (CarrieF:Person {name:'Carrie Fisher', born:1956}) -CREATE (BrunoK:Person {name:'Bruno Kirby', born:1949}) -CREATE -(BillyC)-[:ACTED_IN {roles:['Harry Burns']}]->(WhenHarryMetSally), -(MegR)-[:ACTED_IN {roles:['Sally Albright']}]->(WhenHarryMetSally), -(CarrieF)-[:ACTED_IN {roles:['Marie']}]->(WhenHarryMetSally), -(BrunoK)-[:ACTED_IN {roles:['Jess']}]->(WhenHarryMetSally), -(RobR)-[:DIRECTED]->(WhenHarryMetSally), -(RobR)-[:PRODUCED]->(WhenHarryMetSally), -(NoraE)-[:PRODUCED]->(WhenHarryMetSally), -(NoraE)-[:WROTE]->(WhenHarryMetSally) - -CREATE (ThatThingYouDo:Movie {title:'That Thing You Do', released:1996, tagline:'In every life there comes a time when that thing you dream becomes that thing you do'}) -CREATE (LivT:Person {name:'Liv Tyler', born:1977}) -CREATE -(TomH)-[:ACTED_IN {roles:['Mr. White']}]->(ThatThingYouDo), -(LivT)-[:ACTED_IN {roles:['Faye Dolan']}]->(ThatThingYouDo), -(Charlize)-[:ACTED_IN {roles:['Tina']}]->(ThatThingYouDo), -(TomH)-[:DIRECTED]->(ThatThingYouDo) - -CREATE (TheReplacements:Movie {title:'The Replacements', released:2000, tagline:'Pain heals, Chicks dig scars... Glory lasts forever'}) -CREATE (Brooke:Person {name:'Brooke Langton', born:1970}) -CREATE (Gene:Person {name:'Gene Hackman', born:1930}) -CREATE (Orlando:Person {name:'Orlando Jones', born:1968}) -CREATE (Howard:Person {name:'Howard Deutch', born:1950}) -CREATE -(Keanu)-[:ACTED_IN {roles:['Shane Falco']}]->(TheReplacements), -(Brooke)-[:ACTED_IN {roles:['Annabelle Farrell']}]->(TheReplacements), -(Gene)-[:ACTED_IN {roles:['Jimmy McGinty']}]->(TheReplacements), -(Orlando)-[:ACTED_IN {roles:['Clifford Franklin']}]->(TheReplacements), -(Howard)-[:DIRECTED]->(TheReplacements) - -CREATE (RescueDawn:Movie {title:'RescueDawn', released:2006, tagline:"Based on the extraordinary true story of one man's fight for freedom"}) -CREATE (ChristianB:Person {name:'Christian Bale', born:1974}) -CREATE (ZachG:Person {name:'Zach Grenier', born:1954}) -CREATE -(MarshallB)-[:ACTED_IN {roles:['Admiral']}]->(RescueDawn), -(ChristianB)-[:ACTED_IN {roles:['Dieter Dengler']}]->(RescueDawn), -(ZachG)-[:ACTED_IN {roles:['Squad Leader']}]->(RescueDawn), -(SteveZ)-[:ACTED_IN {roles:['Duane']}]->(RescueDawn), -(WernerH)-[:DIRECTED]->(RescueDawn) - -CREATE (TheBirdcage:Movie {title:'The Birdcage', released:1996, tagline:'Come as you are'}) -CREATE (MikeN:Person {name:'Mike Nichols', born:1931}) -CREATE -(Robin)-[:ACTED_IN {roles:['Armand Goldman']}]->(TheBirdcage), -(Nathan)-[:ACTED_IN {roles:['Albert Goldman']}]->(TheBirdcage), -(Gene)-[:ACTED_IN {roles:['Sen. Kevin Keeley']}]->(TheBirdcage), -(MikeN)-[:DIRECTED]->(TheBirdcage) - -CREATE (Unforgiven:Movie {title:'Unforgiven', released:1992, tagline:"It's a hell of a thing, killing a man"}) -CREATE (RichardH:Person {name:'Richard Harris', born:1930}) -CREATE (ClintE:Person {name:'Clint Eastwood', born:1930}) -CREATE -(RichardH)-[:ACTED_IN {roles:['English Bob']}]->(Unforgiven), -(ClintE)-[:ACTED_IN {roles:['Bill Munny']}]->(Unforgiven), -(Gene)-[:ACTED_IN {roles:['Little Bill Daggett']}]->(Unforgiven), -(ClintE)-[:DIRECTED]->(Unforgiven) - -CREATE (JohnnyMnemonic:Movie {title:'Johnny Mnemonic', released:1995, tagline:'The hottest data on earth. In the coolest head in town'}) -CREATE (Takeshi:Person {name:'Takeshi Kitano', born:1947}) -CREATE (Dina:Person {name:'Dina Meyer', born:1968}) -CREATE (IceT:Person {name:'Ice-T', born:1958}) -CREATE (RobertL:Person {name:'Robert Longo', born:1953}) -CREATE -(Keanu)-[:ACTED_IN {roles:['Johnny Mnemonic']}]->(JohnnyMnemonic), -(Takeshi)-[:ACTED_IN {roles:['Takahashi']}]->(JohnnyMnemonic), -(Dina)-[:ACTED_IN {roles:['Jane']}]->(JohnnyMnemonic), -(IceT)-[:ACTED_IN {roles:['J-Bone']}]->(JohnnyMnemonic), -(RobertL)-[:DIRECTED]->(JohnnyMnemonic) - -CREATE (CloudAtlas:Movie {title:'Cloud Atlas', released:2012, tagline:'Everything is connected'}) -CREATE (HalleB:Person {name:'Halle Berry', born:1966}) -CREATE (JimB:Person {name:'Jim Broadbent', born:1949}) -CREATE (TomT:Person {name:'Tom Tykwer', born:1965}) -CREATE (DavidMitchell:Person {name:'David Mitchell', born:1969}) -CREATE (StefanArndt:Person {name:'Stefan Arndt', born:1961}) -CREATE -(TomH)-[:ACTED_IN {roles:['Zachry', 'Dr. Henry Goose', 'Isaac Sachs', 'Dermot Hoggins']}]->(CloudAtlas), -(Hugo)-[:ACTED_IN {roles:['Bill Smoke', 'Haskell Moore', 'Tadeusz Kesselring', 'Nurse Noakes', 'Boardman Mephi', 'Old Georgie']}]->(CloudAtlas), -(HalleB)-[:ACTED_IN {roles:['Luisa Rey', 'Jocasta Ayrs', 'Ovid', 'Meronym']}]->(CloudAtlas), -(JimB)-[:ACTED_IN {roles:['Vyvyan Ayrs', 'Captain Molyneux', 'Timothy Cavendish']}]->(CloudAtlas), -(TomT)-[:DIRECTED]->(CloudAtlas), -(LillyW)-[:DIRECTED]->(CloudAtlas), -(LanaW)-[:DIRECTED]->(CloudAtlas), -(DavidMitchell)-[:WROTE]->(CloudAtlas), -(StefanArndt)-[:PRODUCED]->(CloudAtlas) - -CREATE (TheDaVinciCode:Movie {title:'The Da Vinci Code', released:2006, tagline:'Break The Codes'}) -CREATE (IanM:Person {name:'Ian McKellen', born:1939}) -CREATE (AudreyT:Person {name:'Audrey Tautou', born:1976}) -CREATE (PaulB:Person {name:'Paul Bettany', born:1971}) -CREATE (RonH:Person {name:'Ron Howard', born:1954}) -CREATE -(TomH)-[:ACTED_IN {roles:['Dr. Robert Langdon']}]->(TheDaVinciCode), -(IanM)-[:ACTED_IN {roles:['Sir Leight Teabing']}]->(TheDaVinciCode), -(AudreyT)-[:ACTED_IN {roles:['Sophie Neveu']}]->(TheDaVinciCode), -(PaulB)-[:ACTED_IN {roles:['Silas']}]->(TheDaVinciCode), -(RonH)-[:DIRECTED]->(TheDaVinciCode) - -CREATE (VforVendetta:Movie {title:'V for Vendetta', released:2006, tagline:'Freedom! Forever!'}) -CREATE (NatalieP:Person {name:'Natalie Portman', born:1981}) -CREATE (StephenR:Person {name:'Stephen Rea', born:1946}) -CREATE (JohnH:Person {name:'John Hurt', born:1940}) -CREATE (BenM:Person {name: 'Ben Miles', born:1967}) -CREATE -(Hugo)-[:ACTED_IN {roles:['V']}]->(VforVendetta), -(NatalieP)-[:ACTED_IN {roles:['Evey Hammond']}]->(VforVendetta), -(StephenR)-[:ACTED_IN {roles:['Eric Finch']}]->(VforVendetta), -(JohnH)-[:ACTED_IN {roles:['High Chancellor Adam Sutler']}]->(VforVendetta), -(BenM)-[:ACTED_IN {roles:['Dascomb']}]->(VforVendetta), -(JamesM)-[:DIRECTED]->(VforVendetta), -(LillyW)-[:PRODUCED]->(VforVendetta), -(LanaW)-[:PRODUCED]->(VforVendetta), -(JoelS)-[:PRODUCED]->(VforVendetta), -(LillyW)-[:WROTE]->(VforVendetta), -(LanaW)-[:WROTE]->(VforVendetta) - -CREATE (SpeedRacer:Movie {title:'Speed Racer', released:2008, tagline:'Speed has no limits'}) -CREATE (EmileH:Person {name:'Emile Hirsch', born:1985}) -CREATE (JohnG:Person {name:'John Goodman', born:1960}) -CREATE (SusanS:Person {name:'Susan Sarandon', born:1946}) -CREATE (MatthewF:Person {name:'Matthew Fox', born:1966}) -CREATE (ChristinaR:Person {name:'Christina Ricci', born:1980}) -CREATE (Rain:Person {name:'Rain', born:1982}) -CREATE -(EmileH)-[:ACTED_IN {roles:['Speed Racer']}]->(SpeedRacer), -(JohnG)-[:ACTED_IN {roles:['Pops']}]->(SpeedRacer), -(SusanS)-[:ACTED_IN {roles:['Mom']}]->(SpeedRacer), -(MatthewF)-[:ACTED_IN {roles:['Racer X']}]->(SpeedRacer), -(ChristinaR)-[:ACTED_IN {roles:['Trixie']}]->(SpeedRacer), -(Rain)-[:ACTED_IN {roles:['Taejo Togokahn']}]->(SpeedRacer), -(BenM)-[:ACTED_IN {roles:['Cass Jones']}]->(SpeedRacer), -(LillyW)-[:DIRECTED]->(SpeedRacer), -(LanaW)-[:DIRECTED]->(SpeedRacer), -(LillyW)-[:WROTE]->(SpeedRacer), -(LanaW)-[:WROTE]->(SpeedRacer), -(JoelS)-[:PRODUCED]->(SpeedRacer) - -CREATE (NinjaAssassin:Movie {title:'Ninja Assassin', released:2009, tagline:'Prepare to enter a secret world of assassins'}) -CREATE (NaomieH:Person {name:'Naomie Harris'}) -CREATE -(Rain)-[:ACTED_IN {roles:['Raizo']}]->(NinjaAssassin), -(NaomieH)-[:ACTED_IN {roles:['Mika Coretti']}]->(NinjaAssassin), -(RickY)-[:ACTED_IN {roles:['Takeshi']}]->(NinjaAssassin), -(BenM)-[:ACTED_IN {roles:['Ryan Maslow']}]->(NinjaAssassin), -(JamesM)-[:DIRECTED]->(NinjaAssassin), -(LillyW)-[:PRODUCED]->(NinjaAssassin), -(LanaW)-[:PRODUCED]->(NinjaAssassin), -(JoelS)-[:PRODUCED]->(NinjaAssassin) - -CREATE (TheGreenMile:Movie {title:'The Green Mile', released:1999, tagline:"Walk a mile you'll never forget."}) -CREATE (MichaelD:Person {name:'Michael Clarke Duncan', born:1957}) -CREATE (DavidM:Person {name:'David Morse', born:1953}) -CREATE (SamR:Person {name:'Sam Rockwell', born:1968}) -CREATE (GaryS:Person {name:'Gary Sinise', born:1955}) -CREATE (PatriciaC:Person {name:'Patricia Clarkson', born:1959}) -CREATE (FrankD:Person {name:'Frank Darabont', born:1959}) -CREATE -(TomH)-[:ACTED_IN {roles:['Paul Edgecomb']}]->(TheGreenMile), -(MichaelD)-[:ACTED_IN {roles:['John Coffey']}]->(TheGreenMile), -(DavidM)-[:ACTED_IN {roles:['Brutus "Brutal" Howell']}]->(TheGreenMile), -(BonnieH)-[:ACTED_IN {roles:['Jan Edgecomb']}]->(TheGreenMile), -(JamesC)-[:ACTED_IN {roles:['Warden Hal Moores']}]->(TheGreenMile), -(SamR)-[:ACTED_IN {roles:['"Wild Bill" Wharton']}]->(TheGreenMile), -(GaryS)-[:ACTED_IN {roles:['Burt Hammersmith']}]->(TheGreenMile), -(PatriciaC)-[:ACTED_IN {roles:['Melinda Moores']}]->(TheGreenMile), -(FrankD)-[:DIRECTED]->(TheGreenMile) - -CREATE (FrostNixon:Movie {title:'Frost/Nixon', released:2008, tagline:'400 million people were waiting for the truth.'}) -CREATE (FrankL:Person {name:'Frank Langella', born:1938}) -CREATE (MichaelS:Person {name:'Michael Sheen', born:1969}) -CREATE (OliverP:Person {name:'Oliver Platt', born:1960}) -CREATE -(FrankL)-[:ACTED_IN {roles:['Richard Nixon']}]->(FrostNixon), -(MichaelS)-[:ACTED_IN {roles:['David Frost']}]->(FrostNixon), -(KevinB)-[:ACTED_IN {roles:['Jack Brennan']}]->(FrostNixon), -(OliverP)-[:ACTED_IN {roles:['Bob Zelnick']}]->(FrostNixon), -(SamR)-[:ACTED_IN {roles:['James Reston, Jr.']}]->(FrostNixon), -(RonH)-[:DIRECTED]->(FrostNixon) - -CREATE (Hoffa:Movie {title:'Hoffa', released:1992, tagline:"He didn't want law. He wanted justice."}) -CREATE (DannyD:Person {name:'Danny DeVito', born:1944}) -CREATE (JohnR:Person {name:'John C. Reilly', born:1965}) -CREATE -(JackN)-[:ACTED_IN {roles:['Hoffa']}]->(Hoffa), -(DannyD)-[:ACTED_IN {roles:['Robert "Bobby" Ciaro']}]->(Hoffa), -(JTW)-[:ACTED_IN {roles:['Frank Fitzsimmons']}]->(Hoffa), -(JohnR)-[:ACTED_IN {roles:['Peter "Pete" Connelly']}]->(Hoffa), -(DannyD)-[:DIRECTED]->(Hoffa) - -CREATE (Apollo13:Movie {title:'Apollo 13', released:1995, tagline:'Houston, we have a problem.'}) -CREATE (EdH:Person {name:'Ed Harris', born:1950}) -CREATE (BillPax:Person {name:'Bill Paxton', born:1955}) -CREATE -(TomH)-[:ACTED_IN {roles:['Jim Lovell']}]->(Apollo13), -(KevinB)-[:ACTED_IN {roles:['Jack Swigert']}]->(Apollo13), -(EdH)-[:ACTED_IN {roles:['Gene Kranz']}]->(Apollo13), -(BillPax)-[:ACTED_IN {roles:['Fred Haise']}]->(Apollo13), -(GaryS)-[:ACTED_IN {roles:['Ken Mattingly']}]->(Apollo13), -(RonH)-[:DIRECTED]->(Apollo13) - -CREATE (Twister:Movie {title:'Twister', released:1996, tagline:"Don't Breathe. Don't Look Back."}) -CREATE (PhilipH:Person {name:'Philip Seymour Hoffman', born:1967}) -CREATE (JanB:Person {name:'Jan de Bont', born:1943}) -CREATE -(BillPax)-[:ACTED_IN {roles:['Bill Harding']}]->(Twister), -(HelenH)-[:ACTED_IN {roles:['Dr. Jo Harding']}]->(Twister), -(ZachG)-[:ACTED_IN {roles:['Eddie']}]->(Twister), -(PhilipH)-[:ACTED_IN {roles:['Dustin "Dusty" Davis']}]->(Twister), -(JanB)-[:DIRECTED]->(Twister) - -CREATE (CastAway:Movie {title:'Cast Away', released:2000, tagline:'At the edge of the world, his journey begins.'}) -CREATE (RobertZ:Person {name:'Robert Zemeckis', born:1951}) -CREATE -(TomH)-[:ACTED_IN {roles:['Chuck Noland']}]->(CastAway), -(HelenH)-[:ACTED_IN {roles:['Kelly Frears']}]->(CastAway), -(RobertZ)-[:DIRECTED]->(CastAway) - -CREATE (OneFlewOvertheCuckoosNest:Movie {title:"One Flew Over the Cuckoo's Nest", released:1975, tagline:"If he's crazy, what does that make you?"}) -CREATE (MilosF:Person {name:'Milos Forman', born:1932}) -CREATE -(JackN)-[:ACTED_IN {roles:['Randle McMurphy']}]->(OneFlewOvertheCuckoosNest), -(DannyD)-[:ACTED_IN {roles:['Martini']}]->(OneFlewOvertheCuckoosNest), -(MilosF)-[:DIRECTED]->(OneFlewOvertheCuckoosNest) - -CREATE (SomethingsGottaGive:Movie {title:"Something's Gotta Give", released:2003}) -CREATE (DianeK:Person {name:'Diane Keaton', born:1946}) -CREATE (NancyM:Person {name:'Nancy Meyers', born:1949}) -CREATE -(JackN)-[:ACTED_IN {roles:['Harry Sanborn']}]->(SomethingsGottaGive), -(DianeK)-[:ACTED_IN {roles:['Erica Barry']}]->(SomethingsGottaGive), -(Keanu)-[:ACTED_IN {roles:['Julian Mercer']}]->(SomethingsGottaGive), -(NancyM)-[:DIRECTED]->(SomethingsGottaGive), -(NancyM)-[:PRODUCED]->(SomethingsGottaGive), -(NancyM)-[:WROTE]->(SomethingsGottaGive) - -CREATE (BicentennialMan:Movie {title:'Bicentennial Man', released:1999, tagline:"One robot's 200 year journey to become an ordinary man."}) -CREATE (ChrisC:Person {name:'Chris Columbus', born:1958}) -CREATE -(Robin)-[:ACTED_IN {roles:['Andrew Marin']}]->(BicentennialMan), -(OliverP)-[:ACTED_IN {roles:['Rupert Burns']}]->(BicentennialMan), -(ChrisC)-[:DIRECTED]->(BicentennialMan) - -CREATE (CharlieWilsonsWar:Movie {title:"Charlie Wilson's War", released:2007, tagline:"A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire."}) -CREATE (JuliaR:Person {name:'Julia Roberts', born:1967}) -CREATE -(TomH)-[:ACTED_IN {roles:['Rep. Charlie Wilson']}]->(CharlieWilsonsWar), -(JuliaR)-[:ACTED_IN {roles:['Joanne Herring']}]->(CharlieWilsonsWar), -(PhilipH)-[:ACTED_IN {roles:['Gust Avrakotos']}]->(CharlieWilsonsWar), -(MikeN)-[:DIRECTED]->(CharlieWilsonsWar) - -CREATE (ThePolarExpress:Movie {title:'The Polar Express', released:2004, tagline:'This Holiday Season... Believe'}) -CREATE -(TomH)-[:ACTED_IN {roles:['Hero Boy', 'Father', 'Conductor', 'Hobo', 'Scrooge', 'Santa Claus']}]->(ThePolarExpress), -(RobertZ)-[:DIRECTED]->(ThePolarExpress) - -CREATE (ALeagueofTheirOwn:Movie {title:'A League of Their Own', released:1992, tagline:'Once in a lifetime you get a chance to do something different.'}) -CREATE (Madonna:Person {name:'Madonna', born:1954}) -CREATE (GeenaD:Person {name:'Geena Davis', born:1956}) -CREATE (LoriP:Person {name:'Lori Petty', born:1963}) -CREATE (PennyM:Person {name:'Penny Marshall', born:1943}) -CREATE -(TomH)-[:ACTED_IN {roles:['Jimmy Dugan']}]->(ALeagueofTheirOwn), -(GeenaD)-[:ACTED_IN {roles:['Dottie Hinson']}]->(ALeagueofTheirOwn), -(LoriP)-[:ACTED_IN {roles:['Kit Keller']}]->(ALeagueofTheirOwn), -(RosieO)-[:ACTED_IN {roles:['Doris Murphy']}]->(ALeagueofTheirOwn), -(Madonna)-[:ACTED_IN {roles:['"All the Way" Mae Mordabito']}]->(ALeagueofTheirOwn), -(BillPax)-[:ACTED_IN {roles:['Bob Hinson']}]->(ALeagueofTheirOwn), -(PennyM)-[:DIRECTED]->(ALeagueofTheirOwn) - -CREATE (PaulBlythe:Person {name:'Paul Blythe'}) -CREATE (AngelaScope:Person {name:'Angela Scope'}) -CREATE (JessicaThompson:Person {name:'Jessica Thompson'}) -CREATE (JamesThompson:Person {name:'James Thompson'}) - -CREATE -(JessicaThompson)-[:REVIEWED {summary:'An amazing journey', rating:95}]->(CloudAtlas), -(JessicaThompson)-[:REVIEWED {summary:'Silly, but fun', rating:65}]->(TheReplacements), -(JamesThompson)-[:REVIEWED {summary:'The coolest football movie ever', rating:100}]->(TheReplacements), -(AngelaScope)-[:REVIEWED {summary:'Pretty funny at times', rating:62}]->(TheReplacements), -(JessicaThompson)-[:REVIEWED {summary:'Dark, but compelling', rating:85}]->(Unforgiven), -(JessicaThompson)-[:REVIEWED {summary:"Slapstick redeemed only by the Robin Williams and Gene Hackman's stellar performances", rating:45}]->(TheBirdcage), -(JessicaThompson)-[:REVIEWED {summary:'A solid romp', rating:68}]->(TheDaVinciCode), -(JamesThompson)-[:REVIEWED {summary:'Fun, but a little far fetched', rating:65}]->(TheDaVinciCode), -(JessicaThompson)-[:REVIEWED {summary:'You had me at Jerry', rating:92}]->(JerryMaguire) ----- -//// - -== Finding nodes - -The `MATCH` clause is used to find a specific pattern in the graph, such as a specific node. -The `RETURN` clause specifies what of the found graph pattern to return. - -For example, this query will find the nodes with `Person` label and the name `Keanu Reeves`, and return the `name` and `born` properties of the found nodes: - -.Query -[source, cypher] ----- -MATCH (keanu:Person {name:'Keanu Reeves'}) -RETURN keanu.name AS name, keanu.born AS born ----- - -.Result -[role="queryresult",options="header,footer",cols="2*= 1980 AND bornInEighties.born < 1990 -RETURN bornInEighties.name as name, bornInEighties.born as born ORDER BY born DESC ----- - -.Result -[role="queryresult",options="header,footer",cols="2*(m:Movie) -Return type(r), m.title AS movies ----- - -The graph returned shows that he has 13 outgoing relationships connected to 12 different `Movie` nodes (12 have the `ACTED_IN` type and one has the `DIRECTED` type). - -image::introduction_example1.svg[width="500",role="middle"] - -.Result -[role="queryresult",options="header,footer",cols="2*(m:Movie) -Return type(r), m.title AS movies ----- - -.Result -[role="queryresult",options="header,footer",cols="2*(m:Movie)<-[:ACTED_IN]-(coActors:Person), - (coActors)-[:ACTED_IN]->(m2:Movie)<-[:ACTED_IN]-(tom:Person {name:'Tom Hanks'}) -RETURN DISTINCT coActors.name AS coActors ----- - -.Result -[role="queryresult",options="header,footer",cols="1*>. +For more information, see xref:subqueries/subqueries-in-transactions.adoc#error-behavior[Error behavior]. Canceling that outer transaction will cancel the inner ones. .+Loading CSV data in transactions+ ====== This example uses a CSV file and the `LOAD CSV` clause to import more data to the example graph. -It creates nodes in separate transactions using `+CALL { ... } IN TRANSACTIONS+`: +It creates nodes in separate transactions using `CALL { ... } IN TRANSACTIONS`: .friends.csv [source, csv, role="noheader" filename="friends.csv"] @@ -52,14 +52,13 @@ As the size of the CSV file in this example is small, only a single separate tra [NOTE] ==== -`+CALL { ... } IN TRANSACTIONS+` is only allowed in xref::introduction/cypher_neo4j.adoc#cypher-neo4j-transactions[implicit transactions]. +`CALL { ... } IN TRANSACTIONS` is only allowed in xref::introduction/cypher_neo4j.adoc#cypher-neo4j-transactions[implicit transactions]. ==== - [[delete-with-call-in-transactions]] == Deleting a large volume of nodes -Using `+CALL { ... } IN TRANSACTIONS+` is the recommended way of deleting a large volume of nodes. +Using `CALL { ... } IN TRANSACTIONS` is the recommended way of deleting a large volume of nodes. .+DETACH DELETE on all nodes+ @@ -87,7 +86,7 @@ Transactions committed: 1 [IMPORTANT] ==== -The `+CALL { ... } IN TRANSACTIONS+` subquery is handled by the database so as to ensure optimal performance. +The `CALL { ... } IN TRANSACTIONS` subquery is handled by the database to ensure optimal performance. Modifying the subquery may result in `OutOfMemory` exceptions for sufficiently large datasets. ==== @@ -97,7 +96,7 @@ Modifying the subquery may result in `OutOfMemory` exceptions for sufficiently l .+DETACH DELETE on only some nodes+ ====== -The `+CALL { ... } IN TRANSACTIONS+` subquery should not be modified. +The `CALL { ... } IN TRANSACTIONS` subquery should not be modified. Any necessary filtering can be done before the subquery. @@ -120,7 +119,7 @@ CALL { ====== - +[[batching]] == Batching The amount of work to do in each separate transaction can be specified in terms of how many input rows @@ -171,7 +170,7 @@ The query now starts and commits three separate transactions: . The last execution of the subquery (for the last input row) takes place in a third transaction. . The third transaction is committed. -You can also use `+CALL { ... } IN TRANSACTIONS OF n ROWS+` to delete all your data in batches in order to avoid a huge garbage collection or an `OutOfMemory` exception. +You can also use `CALL { ... } IN TRANSACTIONS OF n ROWS` to delete all your data in batches in order to avoid a huge garbage collection or an `OutOfMemory` exception. For example: .Query @@ -201,19 +200,20 @@ The batch size of `2 ROWS` is an example given the small data set used here. For larger data sets, you might want to use larger batch sizes, such as `10000 ROWS`. ==== -== Error behaviour [[txs_error_behaviour]] +[[error-behavior]] +== Error behavior _This feature was introduced in Neo4j 5.7._ -Users can choose one of three different option flags to control the behaviour -in case of an error occurring in any of the inner transactions of `+CALL { ... } IN TRANSACTIONS+`: +Users can choose one of three different option flags to control the behavior +in case of an error occurring in any of the inner transactions of `CALL { ... } IN TRANSACTIONS`: * `ON ERROR CONTINUE` to ignore a recoverable error and continue the execution of subsequent inner transactions. The outer transaction succeeds. It will cause the expected variables from the failed inner query to be bound as null for that specific transaction. * `ON ERROR BREAK` to ignore a recoverable error and stop the execution of subsequent inner transactions. The outer transaction succeeds. It will cause expected variables from the failed inner query to be bound as null for all onward transactions (including the failed one). -* `ON ERROR FAIL` to acknowledge a recoverable error and stop the execution of subsequent inner transactions. The outer transaction fails. This is the default behaviour if no flag is explicitly specified. +* `ON ERROR FAIL` to acknowledge a recoverable error and stop the execution of subsequent inner transactions. The outer transaction fails. This is the default behavior if no flag is explicitly specified. [IMPORTANT] ==== @@ -393,10 +393,11 @@ RETURN n.num; / by zero (Transactions committed: 1) ---- +[[status-report]] == Status report Users can also report the execution status of the inner transactions by using `REPORT STATUS AS var`. -This flag is disallowed for `ON ERROR FAIL`. For more information, see <>. +This flag is disallowed for `ON ERROR FAIL`. For more information, see xref:subqueries/subqueries-in-transactions.adoc#error-behavior[Error behavior]. After each execution of the inner query finishes (successfully or not), a status value is created that records information about the execution and the transaction that executed it: @@ -489,13 +490,14 @@ RETURN n.num, s.errorMessage; REPORT STATUS can only be used when specifying ON ERROR CONTINUE or ON ERROR BREAK ---- +[[restrictions]] == Restrictions -These are the restrictions on queries that use `+CALL { ... } IN TRANSACTIONS+`: +These are the restrictions on queries that use `CALL { ... } IN TRANSACTIONS`: -* A nested `+CALL { ... } IN TRANSACTIONS+` inside a `+CALL { ... }+` clause is not supported. -* A `+CALL { ... } IN TRANSACTIONS+` in a `UNION` is not supported. -* A `+CALL { ... } IN TRANSACTIONS+` after a write clause is not supported, unless that write clause is inside a `+CALL { ... } IN TRANSACTIONS+`. +* A nested `CALL { ... } IN TRANSACTIONS` inside a `CALL { ... }` clause is not supported. +* A `CALL { ... } IN TRANSACTIONS` in a `UNION` is not supported. +* A `CALL { ... } IN TRANSACTIONS` after a write clause is not supported, unless that write clause is inside a `CALL { ... } IN TRANSACTIONS`. Additionally, there are some restrictions that apply when using an importing `WITH` clause in a `CALL` subquery: diff --git a/modules/ROOT/pages/values-and-types/type-predicate.adoc b/modules/ROOT/pages/values-and-types/type-predicate.adoc index 2fa06c582..9e66b1691 100644 --- a/modules/ROOT/pages/values-and-types/type-predicate.adoc +++ b/modules/ROOT/pages/values-and-types/type-predicate.adoc @@ -1,5 +1,5 @@ -[[type-predicate]] -= Type predicate expression += Type predicate expressions +:description: This page describes how to use type predicate expressions with Cypher. _This feature was introduced in Neo4j 5.9._ From dfacf421c921553ce96834752eee4e4c53659b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 19 Sep 2023 11:11:30 +0200 Subject: [PATCH 14/24] post-review corrections (exlcuding subqueries) --- modules/ROOT/pages/queries/basic.adoc | 12 +++---- modules/ROOT/pages/queries/case.adoc | 36 +++++++++---------- modules/ROOT/pages/queries/concepts.adoc | 30 ++++++++-------- modules/ROOT/pages/queries/expressions.adoc | 14 ++++---- .../values-and-types/type-predicate.adoc | 6 ++-- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/modules/ROOT/pages/queries/basic.adoc b/modules/ROOT/pages/queries/basic.adoc index 2c214eda2..9890b88ce 100644 --- a/modules/ROOT/pages/queries/basic.adoc +++ b/modules/ROOT/pages/queries/basic.adoc @@ -4,7 +4,7 @@ This page contains information about how to create, query, and delete a graph database using Cypher. For more advanced queries, see the section on xref:subqueries/index.adoc[]. -The examples below uses the publicly available xref:https://github.com/neo4j-graph-examples/movies/tree/main/documentation[Neo4j movie database]. +The examples below uses the publicly available link:https://github.com/neo4j-graph-examples/movies/tree/main/documentation[Neo4j movie database]. [[data-model]] == Creating a data model @@ -675,7 +675,7 @@ MATCH (tom:Person {name:'Tom Hanks'})-[r]->(m:Movie) RETURN type(r) AS type, m.title AS movie ---- -The returbed table shows that he has 13 outgoing relationships connected to 12 different `Movie` nodes (12 have the `ACTED_IN` type and one has the `DIRECTED` type). +The result shows that he has 13 outgoing relationships connected to 12 different `Movie` nodes (12 have the `ACTED_IN` type and one has the `DIRECTED` type). image::introduction_example1.svg[width="500",role="middle"] @@ -714,7 +714,7 @@ image::introduction_example1.svg[width="500",role="middle"] |=== It is possible to further modify Cypher queries by adding label expressions to the clauses. -For example, the below query uses a `NOT` label expression (`!`) to return all relationships connected to `Tom Hanks` that do not contain the specified type, `ACTED_IN`. +For example, the below query uses a `NOT` label expression (`!`) to return all relationships connected to `Tom Hanks` that are not of type `ACTED_IN`. .Query [source, cypher] @@ -740,8 +740,8 @@ For more information about the different label expressions supported by Cypher, There are several ways in which Cypher can be used to search a graph for paths between nodes. -To search for patterns of a fixed length, specify the distance ("hops") between the nodes in the pattern by using a xref::patterns/reference.adoc#quantifiers[quantifier] (`\{n}`). -For example, the following query matches all `Person` nodes exactly 2 "hops" away from `Tom Hanks` and returns the first five rows. +To search for patterns of a fixed length, specify the distance (hops) between the nodes in the pattern by using a xref::patterns/reference.adoc#quantifiers[quantifier] (`\{n}`). +For example, the following query matches all `Person` nodes exactly 2 hops away from `Tom Hanks` and returns the first five rows. The xref:syntax/operators.adoc#syntax-using-the-distinct-operator[DISTINCT] operator ensures that the result contain no duplicate values. .Query @@ -768,7 +768,7 @@ LIMIT 5 |=== It is also possible to match a graph for patterns of a variable length. -The below query matches all `Person` nodes between `1` and `4` "hops" away from `Tom Hanks` and returns the first five rows. +The below query matches all `Person` nodes between `1` and `4` hops away from `Tom Hanks` and returns the first five rows. .Query [source, cypher, role=test-result-skip] diff --git a/modules/ROOT/pages/queries/case.adoc b/modules/ROOT/pages/queries/case.adoc index 460e226db..60e0130f5 100644 --- a/modules/ROOT/pages/queries/case.adoc +++ b/modules/ROOT/pages/queries/case.adoc @@ -3,7 +3,7 @@ :description: This page describes how to use the CASE expression with Cypher. Generic conditional expressions can be expressed in Cypher using the `CASE` construct. -Two variants of `CASE` exist within Cypher: the simple form, which allows an expression to be compared against multiple values, and the generic form, which allows multiple conditional statements to be expressed. +Two variants of `CASE` exist within Cypher: the _simple_ form, to compare a single expression against multiple values, and the _generic_ form, to express multiple conditional statements. [NOTE] ==== @@ -37,7 +37,7 @@ CREATE [[case-simple]] == Simple `CASE` -The simple `CASE` form is used to compare an expression against multiple values. +The simple `CASE` form is used to compare a single expression against multiple values. The expressions are evaluated by the `WHEN` operator until a `MATCH` is found. If no match is found, the expression in the `ELSE` operator is returned. If there is no `ELSE` case and no match is found, `null` will be returned. @@ -55,21 +55,21 @@ END ---- *Arguments:* -[options="header"] +[options="header", cols="1,2"] |=== | Name | Description | `test` -| A valid expression. +| An expression. | `value` | An expression whose result will be compared to `test`. | `result` -| This is the expression returned as output if `value` matches `test`. +| The expression returned as output if `value` matches `test` | `default` -| If no match is found, `default` is returned. +| The expression to return if no value matches the test expression. |=== [[case-simple-examples]] @@ -77,7 +77,7 @@ END [source, cypher] ---- -MATCH (n) +MATCH (n:Person) RETURN CASE n.eyes WHEN 'blue' THEN 1 @@ -86,7 +86,7 @@ CASE n.eyes END AS result, n.eyes ---- -[role="queryresult",options="header,footer",cols="1*`) indicating the direction of a relationship. +A relationship must have a start node, an end node, and exactly one type. +Relationships are represented in Cypher with arrows (e.g. `-->`) indicating the direction of a relationship. [source, cypher] ---- MATCH (:Person {name: 'Anna'})-[r:KNOWS WHERE r.since < 2020]->(friend:Person) -RETURN count(friend) As numberOfFriends +RETURN count(r) As numberOfFriends ---- Unlike nodes, information within a relationship pattern must be enclosed by square brackets. -In this case, all outgoing relationships from the start node must be of the type `KNOWS` and have the property `since` set to less than `2020`. -The xref:functions/aggregating.adoc#functions-count[count()] aggregate function is used in the `RETURN` clause to count all nodes bound by the `friend` variable in the preceding `MATCH` clause (i.e. how many friends Anna has known since before 2020). +The query example above matches for relationships of type `KNOWS` and with the property `since` set to less than `2020`. +The query also requires the relationships to go from a `Person` node named `Anna` to any other `Person` nodes, referred to as `friend`. +The xref:functions/aggregating.adoc#functions-count[count()] function is used in the `RETURN` clause to count all the relationships bound by the `r` variable in the preceding `MATCH` clause (i.e. how many friends Anna has known since before 2020). Note that while nodes can have several labels, relationships can only have one type. @@ -62,19 +64,19 @@ By applying a declarative path pattern in a query, you can find simple or comple [source, cypher] ---- MATCH (n:Person {name: 'Anna'})-[:KNOWS]-{1,5}(friend:Person WHERE n.born < friend.born) -RETURN DISTINCT friend.name AS youngerFriends +RETURN DISTINCT friend.name AS youngerConnections ---- -This example uses a xref:patterns/concepts.adoc#quantified-relationships[quantified relationship] to find all paths of a specific length (between `1` and `5` "hops") traversing only relationships of type `KNOWS` from the start node `Anna` to other `Person` nodes with a lower `born` property (as defined by the xref:clauses/where.adoc[] clause). +This example uses a xref:patterns/concepts.adoc#quantified-relationships[quantified relationship] to find all paths up to `5` hops away, traversing only relationships of type `KNOWS` from the start node `Anna` to other younger `Person` nodes (as defined by the xref:clauses/where.adoc[] clause). The xref:syntax/operators.adoc#syntax-using-the-distinct-operator[DISTINCT] operator is used to ensure that the `RETURN` clause only returns unique nodes. Patterns can also be assigned variables. -For example, the below query binds a whole path pattern, which matches the xref:patterns/concepts.adoc#shortest-path[shortest path] from `Anna` to another `Person` node in the graph with the `nationality` property set to `Canadian`. +For example, the below query binds a whole path pattern, which matches the xref:patterns/concepts.adoc#shortest-path[shortest path] from `Anna` to another `Person` node in the graph up to `10` hops away with the `nationality` property set to `Canadian`. In this case, the `RETURN` clause returns the full path pattern between the two nodes. [source, cypher] ---- -MATCH p=shortestPath((:Person {name:'Anna'})-[:KNOWS*]-(:Person {nationality: 'Canadian'})) +MATCH p=shortestPath((:Person {name: 'Anna'})-[:KNOWS*1..10]-(:Person {nationality: 'Canadian'})) RETURN p ---- diff --git a/modules/ROOT/pages/queries/expressions.adoc b/modules/ROOT/pages/queries/expressions.adoc index c126853fc..b50d94029 100644 --- a/modules/ROOT/pages/queries/expressions.adoc +++ b/modules/ROOT/pages/queries/expressions.adoc @@ -3,12 +3,6 @@ This page contains examples of allowed expressions in Cypher. -[NOTE] -==== -Most expressions in Cypher evaluate to `null` if any of their inner expressions are `null`. -Notable exceptions are the operators `IS NULL`, `IS NOT NULL`, and the xref:values-and-types/type-predicate.adoc[type predicate expressions]. -==== - [[general]] == General @@ -31,6 +25,12 @@ Using regular expressions with unsanitized user input may make your application Consider using xref:syntax/parameters.adoc[parameters] instead. ==== +[NOTE] +==== +Most expressions in Cypher evaluate to `null` if any of their inner expressions are `null`. +Notable exceptions are the operators `IS NULL`, `IS NOT NULL`, and the xref:values-and-types/type-predicate.adoc[type predicate expressions]. +==== + [[numerical]] == Numerical @@ -76,4 +76,4 @@ String literals can contain the following escape sequences: * A `BOOLEAN` literal: `true`, `false`. * A predicate expression is an expression that returns a `BOOLEAN` value, `true`, `false` or `null`: `a.prop = 'Hello'`, `length(p) > 10`, `a.name IS NOT NULL`. -* Label and relationship type expressions: `(n:A|B)`, `()-[r:R1|R2]->()`. +* Label and relationship type expressions: `(n:A|B)`, `+()-[r:R1|R2]->()+`. diff --git a/modules/ROOT/pages/values-and-types/type-predicate.adoc b/modules/ROOT/pages/values-and-types/type-predicate.adoc index 9e66b1691..b36f836b7 100644 --- a/modules/ROOT/pages/values-and-types/type-predicate.adoc +++ b/modules/ROOT/pages/values-and-types/type-predicate.adoc @@ -138,7 +138,7 @@ CREATE (:Person {name: 'Alice', age:18}), image::type_predicate_expression_graph.svg[] -The following query finds all `Person` nodes with an `age` property that is an `INTEGER` with a greater value than `18`. +The following query finds all `Person` nodes with an `age` property that is an `INTEGER` with a value greater than `18`. [source, cypher] ---- @@ -162,7 +162,7 @@ Types not storable in properties, such as `MAP`, will return `false` when checke For numerical values passed in as parameters, Cypher does not take the size of the number into account. Cypher will therefore regard any exact numerical parameter as an `INTEGER` regardless of its declared size. -For example, an `INT16` or an `INT32` passed through from a client programming language will both be treated by Cypher as an `INTEGER`. +For example, an `INT16` or an `INT32` passed through from a link:https://neo4j.com/docs/create-applications/[language library] will both be treated by Cypher as an `INTEGER`. Note that any exact numerical parameter used must fit within the range of an `INT64`. [source, cypher, role=test-skip] @@ -229,8 +229,6 @@ _This feature was introduced in Neo4j 5.11._ Closed dynamic union types allow for the testing of multiple types in the same predicate. -For example: - [source, cypher] ---- UNWIND [42, 42.0, "42"] as val From 15aee58dfe7dc3c82028903f6756dc13eec1de30 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Tue, 19 Sep 2023 14:48:45 +0200 Subject: [PATCH 15/24] Clarify null values example. --- modules/ROOT/pages/queries/case.adoc | 40 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/modules/ROOT/pages/queries/case.adoc b/modules/ROOT/pages/queries/case.adoc index 60e0130f5..48f8671e9 100644 --- a/modules/ROOT/pages/queries/case.adoc +++ b/modules/ROOT/pages/queries/case.adoc @@ -37,13 +37,13 @@ CREATE [[case-simple]] == Simple `CASE` -The simple `CASE` form is used to compare a single expression against multiple values. -The expressions are evaluated by the `WHEN` operator until a `MATCH` is found. +The simple `CASE` form is used to compare a single expression against multiple values, and is analogous to the `switch` construct of programming languages. +The expressions are evaluated by the `WHEN` operator until a match is found. If no match is found, the expression in the `ELSE` operator is returned. If there is no `ELSE` case and no match is found, `null` will be returned. [[case-simple-syntax]] -=== Syntax +=== Syntax [source, syntax] ---- @@ -100,7 +100,7 @@ END AS result, n.eyes [[case-generic]] == Generic `CASE` -The generic `CASE` expression supports multiple conditional statements. +The generic `CASE` expression supports multiple conditional statements, and is analogous to the `if-elseif-else` construct of programming languages. Each row is evaluated in order and without any pre-evaluation until a `true` value is found. If no match is found, the expression in the `ELSE` operator is returned. If there is no `ELSE` case and no match is found, `null` will be returned. @@ -159,25 +159,26 @@ END AS result, n.eyes, n.age [[expressions-case-null-differentiating]] -== `CASE` with `null` values, and differentiating simple and generic `CASE` forms - -The difference between the two `CASE` expressions boils down to what is evaluated first. -Two queries matching the `age` property of the `Daniel` node (which is `null`) are used to demonstrate the difference. +== `CASE` with `null` values -In a simple `CASE` expression, the `n.age` is evaluated on the third line in the below query. -For the node `Daniel` it results in `null`, and this will never match a value test, because `null` cannot equal any other value (including `null` itself). -The simple `CASE` expression therefore skips the `WHEN` branch, and goes straight to `ELSE`, where, because `null - 10` equals `null`, the resulting age for `Daniel` ten years ago is `null`. +When working with `null` values, you may be forced to use the generic `CASE` form. +The two examples below use the `age` property of the `Daniel` node (which has a `null` value for that property) to clarify the difference. +.Simple `CASE` [source, cypher] ---- MATCH (n:Person) RETURN n.name, -CASE n.age - WHEN null THEN -1 - ELSE n.age - 10 +CASE n.age // <1> + WHEN null THEN -1 // <2> + ELSE n.age - 10 // <3> END AS age_10_years_ago ---- +<1> `n.age` is the expression being evaluated. Note that the node `Daniel` has a `null` value as age. +<2> This branch is skipped, because `null` does not equal any other value, including `null` itself. +<3> The execution takes the `ELSE` branch, which outputs `null` because `n.age - 10` equals `null`. + [role="queryresult",options="header,footer",cols="2* + WHEN n.age IS NULL THEN -1 // <2> ELSE n.age - 10 END AS age_10_years_ago ---- -In this example, because each row is evaluated individually in the generic `CASE` form, the `WHEN n.age IS NULL` predicate expression evaluates to `true` for the `Daniel` node, and so the result from that branch is returned. -The resulting `age_10_years_ago` of `Daniel` is therefore `-1`. +<1> If no expression is provided after `CASE`, it acts in its generic form, supporting predicate expressions in each branch. +<2> This predicate expression evaluates to `true` for the node `Daniel`, so the result from this branch is returned. [role="queryresult",options="header,footer",cols="2* Date: Tue, 19 Sep 2023 15:12:37 +0200 Subject: [PATCH 16/24] update subqueries --- modules/ROOT/pages/queries/concepts.adoc | 17 +++-- .../ROOT/pages/subqueries/call-subquery.adoc | 69 ++++++++++--------- modules/ROOT/pages/subqueries/collect.adoc | 30 ++++---- modules/ROOT/pages/subqueries/count.adoc | 35 +++++----- .../ROOT/pages/subqueries/existential.adoc | 43 ++++++------ 5 files changed, 100 insertions(+), 94 deletions(-) diff --git a/modules/ROOT/pages/queries/concepts.adoc b/modules/ROOT/pages/queries/concepts.adoc index 77c6592d2..e847de101 100644 --- a/modules/ROOT/pages/queries/concepts.adoc +++ b/modules/ROOT/pages/queries/concepts.adoc @@ -2,9 +2,9 @@ = Core concepts :description: this page describes the three fundamental concepts of a Cypher query: nodes, relationships, and patterns. -Fundamentally, a Neo4j graph database consists of three core entities: nodes, relationships, and patterns. +Fundamentally, a Neo4j graph database consists of three core entities: nodes, relationships, and paths. Cypher queries are constructed to either match or create these entities in a graph. -Having a basic understanding of what nodes, relationships, and patterns are in a graph is therefore crucial in order to construct Cypher queries. +Having a basic understanding of what nodes, relationships, and paths are in a graph is therefore crucial in order to construct Cypher queries. The below examples use the xref:clauses/match.adoc[] and xref:clauses/return.adoc[] clauses to find and return the desired graph patterns. To learn more about these and many other Cypher clauses, see the section on xref:clauses/index.adoc[]. @@ -54,12 +54,11 @@ The xref:functions/aggregating.adoc#functions-count[count()] function is used in Note that while nodes can have several labels, relationships can only have one type. -[[core-concepts-patterns]] -== Patterns +[[core-concepts-paths]] +== Paths -Graph patterns consists of nodes and relationships. -Exploring these graph patterns sits at the very core of Cypher. -By applying a declarative path pattern in a query, you can find simple or complex patterns of fixed or varied lengths. +Paths in a graph consist connected of nodes and relationships. +Exploring these paths sits at the very core of Cypher. [source, cypher] ---- @@ -70,9 +69,9 @@ RETURN DISTINCT friend.name AS youngerConnections This example uses a xref:patterns/concepts.adoc#quantified-relationships[quantified relationship] to find all paths up to `5` hops away, traversing only relationships of type `KNOWS` from the start node `Anna` to other younger `Person` nodes (as defined by the xref:clauses/where.adoc[] clause). The xref:syntax/operators.adoc#syntax-using-the-distinct-operator[DISTINCT] operator is used to ensure that the `RETURN` clause only returns unique nodes. -Patterns can also be assigned variables. +Paths can also be assigned variables. For example, the below query binds a whole path pattern, which matches the xref:patterns/concepts.adoc#shortest-path[shortest path] from `Anna` to another `Person` node in the graph up to `10` hops away with the `nationality` property set to `Canadian`. -In this case, the `RETURN` clause returns the full path pattern between the two nodes. +In this case, the `RETURN` clause returns the full path between the two nodes. [source, cypher] ---- diff --git a/modules/ROOT/pages/subqueries/call-subquery.adoc b/modules/ROOT/pages/subqueries/call-subquery.adoc index f96cf3611..6e0bf4efb 100644 --- a/modules/ROOT/pages/subqueries/call-subquery.adoc +++ b/modules/ROOT/pages/subqueries/call-subquery.adoc @@ -10,33 +10,6 @@ The `CALL` clause is also used for calling procedures. For descriptions of the `CALL` clause in this context, refer to xref::clauses/call.adoc[`CALL` procedure]. ==== -[[call-syntax]] -== Syntax and rules - -[source, syntax] ----- -CALL LCURLY query RCURLY subqueryInTransactionsParameters? ----- - -There are restrictions on how `CALL` subqueries interact with enclosing queries: - -* A subquery can only refer to variables from the enclosing query if they are explicitly imported. -* A subquery cannot return variables with the same names as variables in the enclosing query. -* All variables that are returned from a subquery are afterwards available in the enclosing query. - -[[call-returning-unit]] -== Returning subqueries and unit subqueries - -Subqueries which end with a `RETURN` statement are called _returning subqueries_ while subqueries without such a return statement are called _unit subqueries_. - -A subquery is evaluated for each incoming input row. -Every output row of a _returning subquery_ is combined with the input row to build the result of the subquery. -That means that a returning subquery will influence the number of rows. -If the subquery does not return any rows, there will be no rows available after the subquery. - -_Unit subqueries_, on the other hand, are used for their ability to alter the graph with clauses such as `CREATE`, `MERGE`, `SET`, and `DELETE`. -They do not explicitly return anything, and this means that the number of rows present after the subquery is the same as was going into the subquery. - [[call-example-graph]] == Example graph @@ -63,7 +36,6 @@ CREATE (:Counter {count: 0}) A `CALL` subquery is executed once for each incoming row. -.Execute for each incoming row ====== The `CALL` subquery executes three times, one for each row that the `UNWIND` clause outputs. @@ -91,8 +63,6 @@ d|Rows:3 Each execution of a `CALL` subquery can observe changes from previous executions. - -.Observe changes from previous execution ====== .Query @@ -167,13 +137,14 @@ An importing `WITH` clause must: * Consist only of simple references to outside variables - e.g. `WITH x, y, z`. Aliasing or expressions are not supported in importing `WITH` clauses - e.g. `WITH a AS b` or `WITH a+1 AS b`. * Be the first clause of a subquery (or the second clause, if directly following a `USE` clause). -[CAUTION] -==== + +[[call-execution-order]] +== Execution order of CALL subqueries + The order in which subqueries are executed is not defined. If a query result depends on the order of execution of subqueries, an `ORDER BY` clause should precede the `CALL` clause. -==== -This query creates a linked list of all `:Person` nodes in order of ascending age. +This query creates a linked list of all `Person` nodes in order of ascending age. The `CALL` clause is relying on the incoming row ordering to ensure that a correctly ordered linked list is created, thus the incoming rows must be ordered with a preceding `ORDER BY` clause. @@ -338,6 +309,21 @@ RETURN p.name, numberOfConnections 2+d|Rows: 4 |=== +[[call-returning-unit]] +== Note on returning subqueries and unit subqueries + +The examples above have all used subqueries which end with a `RETURN` clause. +These subqueries are called _returning subqueries_. + +A subquery is evaluated for each incoming input row. +Every output row of a returning subquery is combined with the input row to build the result of the subquery. +That means that a returning subquery will influence the number of rows. +If the subquery does not return any rows, there will be no rows available after the subquery. + +Subqueries without a `RETURN` statement are called _unit subqueries_. +Unit subqueries are used for their ability to alter the graph with clauses such as `CREATE`, `MERGE`, `SET`, and `DELETE`. +They do not explicitly return anything, and this means that the number of rows present after the subquery is the same as was going into the subquery. + [[call-unit-subqueries]] == Unit subqueries @@ -369,3 +355,18 @@ Nodes created: 20 + Properties set: 20 + Labels added: 20 |=== + +[[call-rules]] +== Rules + +The following is true for `CALL` subqueries: + +* A `CALL` subquery can only refer to variables from the enclosing query if they are explicitly imported. + +* A `CALL` subquery cannot return variables with the same names as variables in the enclosing query. + +* All variables that are returned from a `CALL` subquery are afterwards available in the enclosing query. + +* A `CALL` subquery can be used to perform changes to the database. + +* A `CALL` subquery requires an importing `WITH` clause. \ No newline at end of file diff --git a/modules/ROOT/pages/subqueries/collect.adoc b/modules/ROOT/pages/subqueries/collect.adoc index 1621ae16c..05998163d 100644 --- a/modules/ROOT/pages/subqueries/collect.adoc +++ b/modules/ROOT/pages/subqueries/collect.adoc @@ -5,21 +5,9 @@ _This feature was introduced in Neo4j 5.6._ A `COLLECT` subquery expression can be used to create a list with the rows returned by a given subquery. -Any non-writing query is allowed. `COLLECT` subqueries differ from `COUNT` and `EXISTS` subqueries in that the final `RETURN` clause is mandatory. The `RETURN` clause must return exactly one column. -[[collect-syntax]] -== Syntax and rules - -[source, syntax] ----- -COLLECT LCURLY query RCURLY ----- - -A `COLLECT` subquery can appear anywhere in a query that an expression is valid. -Any variable that is defined in the outside scope can be referenced inside the subquery’s own scope. Variables introduced inside the subquery are not part of the outside scope and therefore cannot be accessed on the outside. - [[collect-example]] == Example graph @@ -318,4 +306,20 @@ RETURN COLLECT { | name | ["Pete", "Tim"] 1+d|Rows: 1 -|=== \ No newline at end of file +|=== + +[[collect-rules]] +== Rules + +The following is true for `COLLECT` subqueries: + +* Any non-writing query is allowed. + +* The final `RETURN` clause is mandatory when using a `COLLECT` subquery. +The `RETURN` clause must return exactly one column. + +* A `COLLECT` subquery can appear anywhere in a query that an expression is valid. + +* Any variable that is defined in the outside scope can be referenced inside the `COLLECT` subquery’s own scope. + +* Variables introduced inside the `COLLECT` subquery are not part of the outside scope and therefore cannot be accessed on the outside. diff --git a/modules/ROOT/pages/subqueries/count.adoc b/modules/ROOT/pages/subqueries/count.adoc index 933344b3f..bb7ee00a5 100644 --- a/modules/ROOT/pages/subqueries/count.adoc +++ b/modules/ROOT/pages/subqueries/count.adoc @@ -3,23 +3,6 @@ A `COUNT` subquery 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-syntax]] -== Syntax and rules - -[source, syntax] ----- -COUNT LCURLY (query | matchClause? patternList whereClause?) RCURLY ----- - -A `COUNT` subquery can appear anywhere in a query that an expression is valid. -Any variable that is defined in the outside scope can be referenced inside the subquery’s own scope. Variables introduced inside the subquery are not part of the outside scope and therefore cannot be accessed on the outside. - [[count-example]] == Example graph @@ -296,3 +279,21 @@ RETURN person.name AS name | "Andy" 1+d|Rows: 1 |=== + +[[count-rules]] +== Rules + +The following is true for `COUNT` subqueries: + +* Any non-writing query is allowed. + +* 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. + +* The `MATCH` keyword can be omitted in subqueries in cases where the `COUNT` consists of only a pattern and an optional `WHERE` clause. + +* A `COUNT` subquery can appear anywhere in a query that an expression is valid. + +* Any variable that is defined in the outside scope can be referenced inside the `COUNT` subquery’s own scope. + +* Variables introduced inside the `COUNT` subquery are not part of the outside scope and therefore cannot be accessed on the outside. \ No newline at end of file diff --git a/modules/ROOT/pages/subqueries/existential.adoc b/modules/ROOT/pages/subqueries/existential.adoc index da111eed8..62defcf5c 100644 --- a/modules/ROOT/pages/subqueries/existential.adoc +++ b/modules/ROOT/pages/subqueries/existential.adoc @@ -3,26 +3,6 @@ An `EXISTS` subquery can be used to find out if a specified pattern exists at least once in the graph. It serves the same purpose as a xref::clauses/where.adoc#filter-on-patterns[path pattern] but it 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 `EXISTS` 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. - -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. - -[[exists-syntax]] -== Syntax and rules - -[source, syntax] ----- -EXISTS LCURLY (query | matchClause? patternList whereClause?) RCURLY ----- - -An `EXISTS` subquery can appear anywhere in a query that an expression is valid. -Any variable that is defined in the outside scope can be referenced inside the subquery’s own scope. Variables introduced inside the subquery are not part of the outside scope and therefore cannot be accessed on the outside. [[exists-example]] == Example graph @@ -254,4 +234,25 @@ RETURN person.name AS name | "Andy" | "Peter" 1+d|Rows: 2 -|=== \ No newline at end of file +|=== + +[[existential-summary]] +== Rules + +The following is true for `EXISTS` subqueries: + +* Any non-writing query is allowed. + +* If the `EXISTS` 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. + +* `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. + +* The `MATCH` keyword can be omitted in subqueries in cases where the `EXISTS` consists of only a pattern and an optional `WHERE` clause. + +* An `EXISTS` subquery can appear anywhere in a query that an expression is valid. + +* Any variable that is defined in the outside scope can be referenced inside the subquery’s own scope. + +* Variables introduced inside the subquery are not part of the outside scope and therefore cannot be accessed on the outside. \ No newline at end of file From 8fd2e04332be8abd6861aa76e4cd0efcf3eb5527 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Tue, 19 Sep 2023 15:56:16 +0200 Subject: [PATCH 17/24] Add `null` to examples, simplify examples. --- .../values-and-types/type-predicate.adoc | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/modules/ROOT/pages/values-and-types/type-predicate.adoc b/modules/ROOT/pages/values-and-types/type-predicate.adoc index b36f836b7..f44c4f8a3 100644 --- a/modules/ROOT/pages/values-and-types/type-predicate.adoc +++ b/modules/ROOT/pages/values-and-types/type-predicate.adoc @@ -21,7 +21,7 @@ For all available Cypher types, see the section on xref::values-and-types/proper [source, cypher] ---- -UNWIND [42, true, 'abc'] AS val +UNWIND [42, true, 'abc', null] AS val RETURN val, val IS :: INTEGER AS isInteger ---- @@ -31,7 +31,8 @@ RETURN val, val IS :: INTEGER AS isInteger | 42 | true | true | false | 'abc' | false -2+d|Rows: 3 +| null | true +2+d|Rows: 4 |=== [[type-predicate-not]] @@ -41,7 +42,7 @@ It is also possible to verify that a Cypher expression is not of a certain type, [source, cypher] ---- -UNWIND [42, true, 'abc'] AS val +UNWIND [42, true, 'abc', null] AS val RETURN val, val IS NOT :: STRING AS notString ---- @@ -51,20 +52,22 @@ RETURN val, val IS NOT :: STRING AS notString | 42 | true | true | true | 'abc' | false -2+d|Rows: 3 +| null | false +2+d|Rows: 4 |=== [[type-predicate-null]] == Type predicate expressions for null -Cypher types includes the `null` value. +All Cypher types includes the `null` value. Since Neo4j 5.10, type predicate expressions can be appended with `NOT NULL`. This means that `IS ::` returns `true` for all expressions evaluating to `null`, unless `NOT NULL` is appended. [source, cypher] ---- -WITH toFloat(null) AS nullValue -RETURN nullValue IS :: BOOLEAN AS isBoolean, nullValue IS :: BOOLEAN NOT NULL AS isNotNullBoolean +RETURN + null IS :: BOOLEAN AS isBoolean, + null IS :: BOOLEAN NOT NULL AS isNotNullBoolean ---- [role="queryresult",options="header,footer",cols="2* Date: Wed, 20 Sep 2023 09:17:12 +0200 Subject: [PATCH 18/24] Improve CALL IN TRANSACTIONS page --- .../subqueries-in-transactions.adoc | 95 +++++++++---------- 1 file changed, 45 insertions(+), 50 deletions(-) diff --git a/modules/ROOT/pages/subqueries/subqueries-in-transactions.adoc b/modules/ROOT/pages/subqueries/subqueries-in-transactions.adoc index 393a90f22..8d0e3e58b 100644 --- a/modules/ROOT/pages/subqueries/subqueries-in-transactions.adoc +++ b/modules/ROOT/pages/subqueries/subqueries-in-transactions.adoc @@ -3,17 +3,23 @@ `CALL` subqueries can be made to execute in separate, inner transactions, producing intermediate commits. This can be useful when doing large write operations, like batch updates, imports, and deletes. -To execute a `CALL` subquery in separate transactions, you add the modifier `IN TRANSACTIONS` after the subquery. + +To execute a `CALL` subquery in separate transactions, add the modifier `IN TRANSACTIONS` after the subquery. An outer transaction is opened to report back the accumulated statistics for the inner transactions (created and deleted nodes, relationships, etc.) and it will succeed or fail depending on the results of those inner transactions. -For more information, see xref:subqueries/subqueries-in-transactions.adoc#error-behavior[Error behavior]. -Canceling that outer transaction will cancel the inner ones. +By default, inner transactions group together batches of 1000 rows. +Cancelling the outer transaction will cancel the inner ones as well. +[NOTE] +==== +`CALL { ... } IN TRANSACTIONS` is only allowed in xref::introduction/cypher_neo4j.adoc#cypher-neo4j-transactions[implicit transactions]. +==== -.+Loading CSV data in transactions+ -====== -This example uses a CSV file and the `LOAD CSV` clause to import more data to the example graph. + +== Loading CSV data + +This example uses a CSV file and the `LOAD CSV` clause to import data into the database. It creates nodes in separate transactions using `CALL { ... } IN TRANSACTIONS`: .friends.csv @@ -48,17 +54,12 @@ Transactions committed: 1 |=== As the size of the CSV file in this example is small, only a single separate transaction is started and committed. -====== -[NOTE] -==== -`CALL { ... } IN TRANSACTIONS` is only allowed in xref::introduction/cypher_neo4j.adoc#cypher-neo4j-transactions[implicit transactions]. -==== [[delete-with-call-in-transactions]] -== Deleting a large volume of nodes +== Deleting a large volume of data -Using `CALL { ... } IN TRANSACTIONS` is the recommended way of deleting a large volume of nodes. +Using `CALL { ... } IN TRANSACTIONS` is the recommended way of deleting a large volume of data. .+DETACH DELETE on all nodes+ @@ -84,12 +85,6 @@ Relationships deleted: 2 + Transactions committed: 1 |=== -[IMPORTANT] -==== -The `CALL { ... } IN TRANSACTIONS` subquery is handled by the database to ensure optimal performance. -Modifying the subquery may result in `OutOfMemory` exceptions for sufficiently large datasets. -==== - ====== @@ -201,17 +196,17 @@ For larger data sets, you might want to use larger batch sizes, such as `10000 R ==== [[error-behavior]] -== Error behavior +== Error behavior _This feature was introduced in Neo4j 5.7._ Users can choose one of three different option flags to control the behavior in case of an error occurring in any of the inner transactions of `CALL { ... } IN TRANSACTIONS`: -* `ON ERROR CONTINUE` to ignore a recoverable error and continue the execution of subsequent inner transactions. +* `ON ERROR CONTINUE` to ignore a recoverable error and continue the execution of subsequent inner transactions. The outer transaction succeeds. It will cause the expected variables from the failed inner query to be bound as null for that specific transaction. -* `ON ERROR BREAK` to ignore a recoverable error and stop the execution of subsequent inner transactions. The outer transaction succeeds. +* `ON ERROR BREAK` to ignore a recoverable error and stop the execution of subsequent inner transactions. The outer transaction succeeds. It will cause expected variables from the failed inner query to be bound as null for all onward transactions (including the failed one). * `ON ERROR FAIL` to acknowledge a recoverable error and stop the execution of subsequent inner transactions. The outer transaction fails. This is the default behavior if no flag is explicitly specified. @@ -277,11 +272,11 @@ RETURN n.num; .Result [role="queryresult",options="header,footer",cols="1* Date: Wed, 20 Sep 2023 09:44:05 +0200 Subject: [PATCH 19/24] Cypher injection note --- modules/ROOT/pages/queries/expressions.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ROOT/pages/queries/expressions.adoc b/modules/ROOT/pages/queries/expressions.adoc index b50d94029..811abae90 100644 --- a/modules/ROOT/pages/queries/expressions.adoc +++ b/modules/ROOT/pages/queries/expressions.adoc @@ -21,8 +21,9 @@ This page contains examples of allowed expressions in Cypher. [NOTE] ==== -Using regular expressions with unsanitized user input may make your application vulnerable to Cypher injection. +Expressions containing unsanitized user input may make your application vulnerable to Cypher injection. Consider using xref:syntax/parameters.adoc[parameters] instead. +Learn more in link:https://neo4j.com/developer/kb/protecting-against-cypher-injection/[Protecting against Cypher Injection]. ==== [NOTE] From a65c36567fa5c5afe9881994c14526891826d89a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 20 Sep 2023 10:39:29 +0200 Subject: [PATCH 20/24] more subquery fixes and some typo corrections --- modules/ROOT/images/call_subquery_graph.svg | 10 ++- modules/ROOT/pages/queries/concepts.adoc | 4 +- .../ROOT/pages/subqueries/call-subquery.adoc | 85 +++++++++---------- modules/ROOT/pages/subqueries/collect.adoc | 2 +- modules/ROOT/pages/subqueries/count.adoc | 2 +- .../ROOT/pages/subqueries/existential.adoc | 4 +- 6 files changed, 56 insertions(+), 51 deletions(-) diff --git a/modules/ROOT/images/call_subquery_graph.svg b/modules/ROOT/images/call_subquery_graph.svg index d5f2f8437..10e7f2ae6 100644 --- a/modules/ROOT/images/call_subquery_graph.svg +++ b/modules/ROOT/images/call_subquery_graph.svg @@ -1 +1,9 @@ -CHILD_OFFRIEND_OFPersonChildname:'Alice'age:20PersonParentname:'Charlie'age:65Personname:'Bob'age:25Personname:'Dora'age:30Countercount:0 \ No newline at end of file + + + + + + + + + diff --git a/modules/ROOT/pages/queries/concepts.adoc b/modules/ROOT/pages/queries/concepts.adoc index e847de101..7502c64f3 100644 --- a/modules/ROOT/pages/queries/concepts.adoc +++ b/modules/ROOT/pages/queries/concepts.adoc @@ -4,7 +4,7 @@ Fundamentally, a Neo4j graph database consists of three core entities: nodes, relationships, and paths. Cypher queries are constructed to either match or create these entities in a graph. -Having a basic understanding of what nodes, relationships, and paths are in a graph is therefore crucial in order to construct Cypher queries. +Having a basic understanding of what nodes, relationships, and paths are in a graph database is therefore crucial in order to construct Cypher queries. The below examples use the xref:clauses/match.adoc[] and xref:clauses/return.adoc[] clauses to find and return the desired graph patterns. To learn more about these and many other Cypher clauses, see the section on xref:clauses/index.adoc[]. @@ -57,7 +57,7 @@ Note that while nodes can have several labels, relationships can only have one t [[core-concepts-paths]] == Paths -Paths in a graph consist connected of nodes and relationships. +Paths in a graph consist of connected nodes and relationships. Exploring these paths sits at the very core of Cypher. [source, cypher] diff --git a/modules/ROOT/pages/subqueries/call-subquery.adoc b/modules/ROOT/pages/subqueries/call-subquery.adoc index 6e0bf4efb..b0ad8aa29 100644 --- a/modules/ROOT/pages/subqueries/call-subquery.adoc +++ b/modules/ROOT/pages/subqueries/call-subquery.adoc @@ -22,12 +22,17 @@ To recreate the graph, run the following query in an empty Neo4j database: [source, cypher, role=test-setup] ---- CREATE - (a:Person:Child {name: 'Alice', age: 20}), - (b:Person {name: 'Bob', age: 27}), - (c:Person:Parent {name: 'Charlie', age: 65}), - (d:Person {name: 'Dora', age: 30}) - CREATE (a)-[:FRIEND_OF]->(b) - CREATE (a)-[:CHILD_OF]->(c) + (a:Person:Child {name: 'Alice', age: 20}), + (b:Person {name: 'Bob', age: 27}), + (c:Person:Parent {name: 'Charlie', age: 65}), + (d:Person {name: 'Dora', age: 30}) + CREATE (a)-[:FRIEND_OF]->(b) + CREATE (a)-[:CHILD_OF]->(c) + CREATE (a)-[:OWES {dollars: 20}]->(c) + CREATE (a)-[:OWES {dollars: 25}]->(b) + CREATE (b)-[:OWES {dollars: 35}]->(d) + CREATE (d)-[:OWES {dollars: 15}]->(b) + CREATE (d)-[:OWES {dollars: 30}]->(b) CREATE (:Counter {count: 0}) ---- @@ -36,9 +41,7 @@ CREATE (:Counter {count: 0}) A `CALL` subquery is executed once for each incoming row. -====== - -The `CALL` subquery executes three times, one for each row that the `UNWIND` clause outputs. +In the below example, the `CALL` subquery executes three times, one for each row that the `UNWIND` clause outputs. .Query [source, cypher] @@ -59,12 +62,9 @@ RETURN innerReturn | 'hello' d|Rows:3 |=== -====== Each execution of a `CALL` subquery can observe changes from previous executions. -====== - .Query [source, cypher] ---- @@ -96,8 +96,6 @@ RETURN 2+d|Rows:3 |=== -====== - [[call-importing-variables]] == Importing variables into subqueries @@ -187,7 +185,7 @@ RETURN [[call-post-union]] == Post-union processing -Call subqueries can be used to process the results of a `UNION` query further. +Call subqueries can be used to further process the results of a xref:clauses/union.adoc[] query. This example query finds the youngest and the oldest person in the database and orders them by name. .Query @@ -211,40 +209,40 @@ ORDER BY p.name .Result [role="queryresult",options="header,footer",cols="2*(other:Person) - RETURN other -UNION - WITH p - OPTIONAL MATCH (p)-[:CHILD_OF]->(other:Parent) - RETURN other + WITH p + OPTIONAL MATCH (p)-[o:OWES]->(other:Person) + RETURN o.dollars * -1 AS moneyOwed +UNION ALL + WITH p + OPTIONAL MATCH (other:Person)-[o:OWES]->(p) + RETURN o.dollars AS moneyOwed } -RETURN DISTINCT p.name, count(other) +RETURN p.name, sum(moneyOwed) AS amountOwing ---- .Result [role="queryresult",options="header,footer",cols="2*(c) + RETURN sum(o.dollars) AS owedAmount, c.name AS owedName } -RETURN p.name, numberOfConnections +RETURN p.name, owedAmount, owedName ---- .Result [role="queryresult",options="header,footer",cols="2* Date: Wed, 20 Sep 2023 10:55:09 +0200 Subject: [PATCH 21/24] final comments --- modules/ROOT/pages/queries/case.adoc | 3 +-- modules/ROOT/pages/queries/concepts.adoc | 2 +- modules/ROOT/pages/queries/expressions.adoc | 6 +++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/ROOT/pages/queries/case.adoc b/modules/ROOT/pages/queries/case.adoc index 48f8671e9..ed1e4305a 100644 --- a/modules/ROOT/pages/queries/case.adoc +++ b/modules/ROOT/pages/queries/case.adoc @@ -101,7 +101,7 @@ END AS result, n.eyes == Generic `CASE` The generic `CASE` expression supports multiple conditional statements, and is analogous to the `if-elseif-else` construct of programming languages. -Each row is evaluated in order and without any pre-evaluation until a `true` value is found. +Each row is evaluated in order until a `true` value is found. If no match is found, the expression in the `ELSE` operator is returned. If there is no `ELSE` case and no match is found, `null` will be returned. @@ -221,7 +221,6 @@ For more information about `null`, see xref:values-and-types/working-with-null.a == `CASE` expressions and succeeding clauses The results of a `CASE` expression can be used to set properties on a node or relationship. -For example, instead of specifying a node directly, properties can be set for a node selected by a `CASE` expression. [source, cypher] ---- diff --git a/modules/ROOT/pages/queries/concepts.adoc b/modules/ROOT/pages/queries/concepts.adoc index 7502c64f3..77da23c7d 100644 --- a/modules/ROOT/pages/queries/concepts.adoc +++ b/modules/ROOT/pages/queries/concepts.adoc @@ -39,7 +39,7 @@ The variable `n` is then passed along to the subsequent `RETURN` clause, which r Nodes in a graph can be connected with relationships. A relationship must have a start node, an end node, and exactly one type. -Relationships are represented in Cypher with arrows (e.g. `-->`) indicating the direction of a relationship. +Relationships are represented in Cypher with arrows (e.g. `+-->+`) indicating the direction of a relationship. [source, cypher] ---- diff --git a/modules/ROOT/pages/queries/expressions.adoc b/modules/ROOT/pages/queries/expressions.adoc index 811abae90..7f73fb3cb 100644 --- a/modules/ROOT/pages/queries/expressions.adoc +++ b/modules/ROOT/pages/queries/expressions.adoc @@ -18,6 +18,7 @@ This page contains examples of allowed expressions in Cypher. * A subquery expression: `COUNT {}`, `COLLECT {}`, `EXISTS {}`, `CALL {}`. * A regular expression: `a.name =~ 'Tim.*'`. * A `CASE` expression. +* `null`. [NOTE] ==== @@ -40,6 +41,7 @@ Notable exceptions are the operators `IS NULL`, `IS NOT NULL`, and the xref:valu * A hexadecimal `INTEGER` literal (starting with `0x`): `0x13af`, `0xFC3A9`, `-0x66eff`. * An octal `INTEGER` literal (starting with `0o`): `0o1372`, `-0o5671`. * A `FLOAT` literal: `Inf`, `Infinity`, `NaN`. +* `null`. [NOTE] ==== @@ -52,6 +54,7 @@ There may be an underscore between the `0x` or `0o` and the digits for hexadecim * A `STRING` literal: `'Hello'`, `"World"`. * A case-sensitive `STRING` matching expression: `a.surname STARTS WITH 'Sven'`, `a.surname ENDS WITH 'son'` or `a.surname CONTAINS 'son'`. +* `null`. [[expressions-string-literals]] === String literal escape sequences @@ -76,5 +79,6 @@ String literals can contain the following escape sequences: == Boolean * A `BOOLEAN` literal: `true`, `false`. -* A predicate expression is an expression that returns a `BOOLEAN` value, `true`, `false` or `null`: `a.prop = 'Hello'`, `length(p) > 10`, `a.name IS NOT NULL`. +* A predicate expression (i.e. an expression returning a `BOOLEAN` value): `a.prop = 'Hello'`, `length(p) > 10`, `a.name IS NOT NULL`. * Label and relationship type expressions: `(n:A|B)`, `+()-[r:R1|R2]->()+`. +* `null`. From 9259e2d2cb914bd8cbba8d5c62ba4c73497fb0b3 Mon Sep 17 00:00:00 2001 From: Stefano Ottolenghi Date: Wed, 20 Sep 2023 10:56:39 +0200 Subject: [PATCH 22/24] Table formatting --- modules/ROOT/pages/subqueries/call-subquery.adoc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/ROOT/pages/subqueries/call-subquery.adoc b/modules/ROOT/pages/subqueries/call-subquery.adoc index b0ad8aa29..916db0444 100644 --- a/modules/ROOT/pages/subqueries/call-subquery.adoc +++ b/modules/ROOT/pages/subqueries/call-subquery.adoc @@ -56,8 +56,8 @@ RETURN innerReturn .Result [role="queryresult",options="header,footer",cols="m"] |=== -| innerReturn -| 'hello' +| innerReturn +| 'hello' | 'hello' | 'hello' d|Rows:3 @@ -209,9 +209,9 @@ ORDER BY p.name .Result [role="queryresult",options="header,footer",cols="2* Date: Wed, 20 Sep 2023 11:45:15 +0200 Subject: [PATCH 23/24] final touches --- modules/ROOT/pages/subqueries/call-subquery.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/subqueries/call-subquery.adoc b/modules/ROOT/pages/subqueries/call-subquery.adoc index b0ad8aa29..7cca28dbb 100644 --- a/modules/ROOT/pages/subqueries/call-subquery.adoc +++ b/modules/ROOT/pages/subqueries/call-subquery.adoc @@ -216,7 +216,7 @@ ORDER BY p.name |=== If different parts of a result should be matched differently, with some aggregation over the whole result, subqueries need to be used. -This example query uses a `CALL` subquery in combination with `UNION ALL` to determine how much each `Person` node in the graph owes or is owed. +The example below query uses a `CALL` subquery in combination with `UNION ALL` to determine how much each `Person` node in the graph owes or is owed. .Query [source, cypher] @@ -296,7 +296,7 @@ RETURN p.name, owedAmount, owedName ---- .Result -[role="queryresult",options="header,footer",cols="2* Date: Wed, 20 Sep 2023 11:57:31 +0200 Subject: [PATCH 24/24] fix queries intro page --- modules/ROOT/pages/queries/index.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ROOT/pages/queries/index.adoc b/modules/ROOT/pages/queries/index.adoc index 8ba1b3444..b5f941fee 100644 --- a/modules/ROOT/pages/queries/index.adoc +++ b/modules/ROOT/pages/queries/index.adoc @@ -1,8 +1,8 @@ = Queries :description: This page is an overview of the queries section in the Cypher Manual. -This section provides a brief overview of the core concepts of a Cypher query (nodes, relationships, and patterns), and examples of how to query a Neo4j graph. -It also contains information about how expressions are used in a Cypher query. +This section provides a brief overview of the core concepts of a Cypher query (nodes, relationships, and paths), and examples of how to query a Neo4j graph database. +It also contains information about Cypher expressions. * xref:queries/concepts.adoc[] * xref:queries/basic.adoc[]