diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index 2d08c7ecc..0386481a3 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -37,10 +37,6 @@ ** xref:clauses/where.adoc[] ** xref:clauses/with.adoc[] -* xref:expressions/index.adoc[] -** xref:expressions/expressions-overview.adoc[] -** xref:expressions/conditional-expressions.adoc[] - * xref:subqueries/index.adoc[] ** xref:subqueries/call-subquery.adoc[] ** xref:subqueries/subqueries-in-transactions.adoc[] @@ -56,7 +52,6 @@ ** xref:patterns/non-linear-patterns.adoc[] ** xref:patterns/reference.adoc[] - * xref:values-and-types/index.adoc[] ** xref:values-and-types/property-structural-constructed.adoc[] ** xref:values-and-types/temporal.adoc[] @@ -65,7 +60,18 @@ ** xref:values-and-types/lists.adoc[] ** xref:values-and-types/maps.adoc[] ** xref:values-and-types/casting-data.adoc[] -** xref:values-and-types/type-predicate.adoc[] +** xref:values-and-types/ordering-equality-comparison.adoc[] + +* xref:expressions/index.adoc[] +** xref:expressions/expressions-overview.adoc[] +** xref:expressions/predicates/index.adoc[] +*** xref:expressions/predicates/boolean-operators.adoc[] +*** xref:expressions/predicates/comparison-operators.adoc[] +*** xref:expressions/predicates/list-operators.adoc[] +*** xref:expressions/predicates/string-operators.adoc[] +*** xref:expressions/predicates/path-pattern-expressions.adoc[] +*** xref:expressions/predicates/type-predicate-expressions.adoc[] +** xref:expressions/conditional-expressions.adoc[] * xref:functions/index.adoc[] ** xref:functions/aggregating.adoc[] @@ -85,7 +91,6 @@ ** xref:functions/user-defined.adoc[] ** xref:functions/vector.adoc[] - * xref:genai-integrations.adoc[] * xref:indexes/index.adoc[] ** xref:indexes/search-performance-indexes/overview.adoc[] diff --git a/modules/ROOT/images/graph_where_clause.svg b/modules/ROOT/images/graph_where_clause.svg index b307c8355..f15e25ef6 100644 --- a/modules/ROOT/images/graph_where_clause.svg +++ b/modules/ROOT/images/graph_where_clause.svg @@ -1 +1 @@ -KNOWSsince:2012KNOWSsince:1999PersonSwedishname:'Andy'age:36belt:'white'Personname:'Timothy'age:25Personname:'Peter'age:35email:'peter_n@example.com' \ No newline at end of file +KNOWSsince:2012KNOWSsince:1999KNOWSsince:2005KNOWSsince:2010KNOWSsince:2021SwedishPersonname:'Andy'age:36Personname:'Timothy'age:38Personname:'Peter'age:35Personname:'Lisa'age:48Personname:'John'age:40Personname:'Susan'age:32 \ No newline at end of file diff --git a/modules/ROOT/images/path_pattern_expressions.svg b/modules/ROOT/images/path_pattern_expressions.svg new file mode 100644 index 000000000..e236e0637 --- /dev/null +++ b/modules/ROOT/images/path_pattern_expressions.svg @@ -0,0 +1 @@ +WORKS_FORsince:2023WORKS_FORsince:2015Personname:'Alice'age:65role:'Project manager'Personname:'Cecil'age:25role:'Software developer'Personname:'Cecilia'age:31role:'Software developer' \ No newline at end of file diff --git a/modules/ROOT/images/predicate_operators.svg b/modules/ROOT/images/predicate_operators.svg new file mode 100644 index 000000000..d1cc6b308 --- /dev/null +++ b/modules/ROOT/images/predicate_operators.svg @@ -0,0 +1 @@ +Personname:'Alice'age:65role:'Project manager'email:'alice@company.com'Personname:'Cecil'age:25role:'Software developer'email:'cecil@private.se'Personname:'Cecilia'age:31role:'Software developer'Personname:'Daniel'age:39role:'Director'email:'daniel@company.com'Personname:'Eskil'age:39role:'CEO'email:'eskil@company.com'Personname:'Charlie'age:61role:'Security engineer' \ No newline at end of file diff --git a/modules/ROOT/pages/appendix/gql-conformance/supported-mandatory.adoc b/modules/ROOT/pages/appendix/gql-conformance/supported-mandatory.adoc index 3504f299e..7ecb42f80 100644 --- a/modules/ROOT/pages/appendix/gql-conformance/supported-mandatory.adoc +++ b/modules/ROOT/pages/appendix/gql-conformance/supported-mandatory.adoc @@ -143,12 +143,12 @@ This is currently not available in Cypher. | 19.5 | -| xref:values-and-types/type-predicate.adoc#type-predicate-null[Type predicate expressions for `NULL`] +| xref:expressions/predicates/type-predicate-expressions.adoc#type-predicate-null[Type predicate expressions for `NULL`] | | 19.6 | -| xref:values-and-types/type-predicate.adoc#[] +| xref:expressions/predicates/type-predicate-expressions.adoc#[] | | 19.7 diff --git a/modules/ROOT/pages/appendix/gql-conformance/supported-optional.adoc b/modules/ROOT/pages/appendix/gql-conformance/supported-optional.adoc index 64d09b71c..b64511105 100644 --- a/modules/ROOT/pages/appendix/gql-conformance/supported-optional.adoc +++ b/modules/ROOT/pages/appendix/gql-conformance/supported-optional.adoc @@ -85,7 +85,7 @@ These codes order the features in the table below. | GA06 | Value type predicates -| xref:values-and-types/type-predicate.adoc[Type predicate expressions] +| xref:expressions/predicates/type-predicate-expressions.adoc[Type predicate expressions] | | GA07 @@ -211,12 +211,12 @@ GQL also defines a parameterless version of the function not in Cypher: `CURRENT | GV66 | Open dynamic unions -| xref:values-and-types/type-predicate.adoc#type-predicate-any-and-nothing[Type predicate expressions -> `ANY` and `NOTHING`] +| xref:expressions/predicates/type-predicate-expressions.adoc#type-predicate-any-and-nothing[Type predicate expressions -> `ANY` and `NOTHING`] | | GV67 | Closed dynamic unions -| xref:values-and-types/type-predicate.adoc#type-predicate-closed-dynamic-unions[Closed dynamic unions] +| xref:expressions/predicates/type-predicate-expressions.adoc#type-predicate-closed-dynamic-unions[Closed dynamic unions] | | GV70 @@ -226,7 +226,7 @@ GQL also defines a parameterless version of the function not in Cypher: `CURRENT | GV71 | Immaterial value types: empty type support (`NOTHING`)] -| xref:values-and-types/type-predicate.adoc#type-predicate-any-and-nothing[Type predicate expressions -> `ANY` and `NOTHING`] +| xref:expressions/predicates/type-predicate-expressions.adoc#type-predicate-any-and-nothing[Type predicate expressions -> `ANY` and `NOTHING`] | |=== diff --git a/modules/ROOT/pages/clauses/where.adoc b/modules/ROOT/pages/clauses/where.adoc index 87abb66bb..cf976e4b7 100644 --- a/modules/ROOT/pages/clauses/where.adoc +++ b/modules/ROOT/pages/clauses/where.adoc @@ -1,189 +1,106 @@ :description: `WHERE` adds constraints to the patterns in a `MATCH` or `OPTIONAL MATCH` clause or filters the results of a `WITH` clause. - -[[query-where]] +:table-caption!: = WHERE -[[where-introduction]] -== Introduction - -The `WHERE` clause is not a clause in its own right -- rather, it is part of the `MATCH`, `OPTIONAL MATCH`, and `WITH` clauses. +The `WHERE` clause is not a clause in its own right -- rather, it is subclause used with xref:clauses/match.adoc[`MATCH`], xref:clauses/optional-match.adoc[`OPTIONAL MATCH`], and xref:clauses/with.adoc[`WITH`] clauses. When used with `MATCH` and `OPTIONAL MATCH`, `WHERE` adds constraints to the patterns described. _It should not be seen as a filter after the matching is finished._ -In the case of `WITH`, however, `WHERE` simply filters the results. - In the case of multiple `MATCH` / `OPTIONAL MATCH` clauses, the predicate in `WHERE` is always a part of the patterns in the directly preceding `MATCH` / `OPTIONAL MATCH`. Both results and performance may be impacted if `WHERE` is put inside the wrong `MATCH` clause. -xref:indexes/search-performance-indexes/managing-indexes.adoc[Indexes] may be used to optimize queries using `WHERE` in a variety of cases. +When used after `WITH`, `WHERE` simply filters the results. + +[TIP] +For more uses of `WHERE`, see xref:expressions/predicates/index.adoc[]. [[where-example-graph]] == Example graph The following graph is used for the examples below: -image::graph_where_clause.svg[width="600",role="middle"] +image::graph_where_clause.svg[width="700",role="middle"] To recreate the graph, run the following query in an empty Neo4j database: [source, cypher, role=test-setup] ---- -CREATE -(andy:Swedish:Person {name: 'Andy', age: 36, belt: 'white'}), -(timothy:Person {name: 'Timothy', age: 25}), -(peter:Person {name: 'Peter', age: 35, email: 'peter_n@example.com'}), -(andy)-[:KNOWS {since: 2012}]->(timothy), -(andy)-[:KNOWS {since: 1999}]->(peter) +CREATE (andy:Swedish:Person {name: 'Andy', age: 36}), + (timothy:Person {name: 'Timothy', age: 38}), + (peter:Person {name: 'Peter', age: 35}), + (lisa:Person {name: 'Lisa', age: 48}), + (john:Person {name: 'John', age: 40}), + (susan:Person {name: 'Susan', age: 32}), + (andy)-[:KNOWS {since: 2012}]->(timothy), + (andy)-[:KNOWS {since: 1999}]->(peter), + (peter)-[:KNOWS {since: 2005}]->(lisa), + (lisa)-[:KNOWS {since: 2010}]->(john), + (john)-[:KNOWS {since: 2021}]->(susan) ---- -[[query-where-basic]] -== Basic usage - -[[node-pattern-predicates]] -=== Node pattern predicates - -`WHERE` can appear inside a node pattern in a `MATCH` clause or a pattern comprehension: +[[basic-filtering]] +== Basic filtering -.Query +.Filter on a node label [source, cypher] ---- -WITH 30 AS minAge -MATCH (a:Person WHERE a.name = 'Andy')-[:KNOWS]->(b:Person WHERE b.age > minAge) -RETURN b.name +MATCH (n) +WHERE n:Swedish +RETURN n.name AS name ---- .Result [role="queryresult",options="header,footer",cols="1*(b WHERE b:Person) | b.name] AS friends ----- +| "Andy" -.Result -[role="queryresult",options="header,footer",cols="1*(f) +WHERE k.since < 2000 +RETURN f.name AS oldFriend ---- -The `name` and `age` values for `Timothy` are returned because he is less than 30 years of age: - .Result -[role="queryresult",options="header,footer",cols="2*(f) -WHERE k.since < 2000 -RETURN f.name, f.age, f.email ----- - -The `name`, `age` and `email` values for `Peter` are returned because `Andy` has known him since before 2000: - -.Result -[role="queryresult",options="header,footer",cols="3* 40 +RETURN n.name AS name, n.age AS age ---- -The `name` and `age` values for `Timothy` are returned because he is less than 30 years of age: - .Result [role="queryresult",options="header,footer",cols="2*(b:Person WHERE b.age > minAge) +RETURN b.name AS name ---- -The given `STRING` values contain only normalized Unicode characters, therefore all the matched `name` properties are returned. -For more information, see the section about the xref:syntax/operators.adoc#match-string-is-normalized[normalization operator]. - .Result [role="queryresult",options="header,footer",cols="1*(b WHERE b:Person) | b.name] AS friends ---- -The `name` and `age` values for `Timothy` are returned because his name starts with "Tim". - .Result -[role="queryresult",options="header,footer",cols="2*(b:Person) +RETURN a.name AS person, b.name AS friend, r.since AS knowsSince ---- -The `name`, `age`, and `email` values for `Peter` are returned because his email ends with ".com": - .Result [role="queryresult",options="header,footer",cols="3*(timothy) -RETURN other.name, other.age ----- - -The `name` and `age` values for nodes that have an outgoing relationship to `Timothy` are returned: - -.Result -[role="queryresult",options="header,footer",cols="2*(peter) -RETURN other.name, other.age ----- - -The `name` and `age` values for nodes that do not have an outgoing relationship to `Peter` are returned: - -.Result -[role="queryresult",options="header,footer",cols="2*(b:Person) | r.since] AS years ---- -Only the `name`, `age`, and `belt` values of nodes with white belts are returned: - .Result -[role="queryresult",options="header,footer",cols="3* -| "Timothy" | 25 | -3+|Rows: 3 +[role="queryresult",options="header,footer",cols="1* -3+|Rows: 1 +1+d|Rows: 1 |=== +[[variable-length-patterns]] +=== Variable-length patterns -[[query-where-ranges]] -== Using ranges - -[[simple-range]] -=== Simple range +If matching for variable length patterns, `WHERE` can only be used together with the xref:patterns/variable-length-patterns.adoc#quantified-path-patterns[quantified path pattern] or xref:patterns/variable-length-patterns.adoc#quantified-relationships[quantified relationships] syntax. -To check whether an element exists within a specific range, use the inequality operators `<`, `<=`, `>=`, `>`: - -.Query +.Allowed - `WHERE` predicate inside a quantified relationship [source, cypher] ---- -MATCH (a:Person) -WHERE a.name >= 'Peter' -RETURN a.name, a.age +MATCH p = (a:Person {name: "Andy"})-[r:KNOWS WHERE r.since < 2011]->{1,4}(:Person) +RETURN [n IN nodes(p) | n.name] AS paths ---- -The `name` and `age` values of nodes having a `name` property lexicographically (i.e. using the dictionary order) greater than or equal to `Peter` are returned: +Note that any path´s including `Timothy` and `Susan` are excluded by the `WHERE` predicate, since their incoming `KNOWS` relationships both have a `since` value that is higher than `2011.` .Result -[role="queryresult",options="header,footer",cols="2* 'Andy' AND a.name < 'Timothy' -RETURN a.name, a.age ----- - -The `name` and `age` values of nodes having a `name` property lexicographically between `Andy` and `Timothy` are returned: - -.Result -[role="queryresult",options="header,footer",cols="2*(b:Person) -RETURN r.since ----- - -.Result -[role="queryresult",options="header,footer",cols="1* b.yearOfBirth]->(b:Person) -RETURN r.since ----- - -.Error message -[source, output] ----- -Relationship pattern predicates are not supported for variable-length relationships. ----- - - -Putting predicates inside a relationship pattern can help with readability. -Note that it is strictly equivalent to using a standalone `WHERE` sub-clause. - -.Query -[source, cypher] ----- -WITH 2000 AS minYear -MATCH (a:Person)-[r:KNOWS]->(b:Person) -WHERE r.since < minYear -RETURN r.since ----- - -.Result -[role="queryresult",options="header,footer",cols="1*(b:Person) | r.since] AS years +MATCH p = (a:Person {name: 'Andy'})-[r:KNOWS*1..4 WHERE r.since < 2011]->(b:Person) +RETURN [n IN nodes(p) | n.name] AS path ---- - -.Result -[role="queryresult",options="header,footer",cols="1* ---- a| -Extended xref:values-and-types/type-predicate.adoc[type predicate expressions]. +Extended xref:expressions/predicates/type-predicate-expressions.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"] @@ -1769,7 +1769,7 @@ IS [NOT] :: ---- a| -Extended xref:values-and-types/type-predicate.adoc[type predicate expressions]. +Extended xref:expressions/predicates/type-predicate-expressions.adoc[type predicate expressions]. The newly supported types are: * `NOTHING` @@ -1943,7 +1943,7 @@ IS [NOT] :: ---- a| -Added xref:values-and-types/type-predicate.adoc[type predicate expressions]. +Added xref:expressions/predicates/type-predicate-expressions.adoc[type predicate expressions]. The available types are: * `BOOLEAN` diff --git a/modules/ROOT/pages/expressions/conditional-expressions.adoc b/modules/ROOT/pages/expressions/conditional-expressions.adoc index 9c6d93c21..8a775bcb6 100644 --- a/modules/ROOT/pages/expressions/conditional-expressions.adoc +++ b/modules/ROOT/pages/expressions/conditional-expressions.adoc @@ -108,7 +108,7 @@ The supported comparators are: * xref::syntax/operators.adoc#query-operators-comparison[Regular Comparison Operators]: `+=+`, `+<>+`, `+<+`, `+>+`, `+<=+`, `+>=+` * xref:values-and-types/working-with-null.adoc#is-null-is-not-null[`IS NULL` Operator]: `IS [NOT] NULL` -* xref:values-and-types/type-predicate.adoc[Type Predicate Expression]: `IS [NOT] TYPED ` (Note that the form `IS [NOT] :: ` is not accepted) +* xref:expressions/predicates/type-predicate-expressions.adoc[Type Predicate Expression]: `IS [NOT] TYPED ` (Note that the form `IS [NOT] :: ` is not accepted) * xref::syntax/operators.adoc#match-string-is-normalized[Normalization Predicate Expression]: `IS [NOT] NORMALIZED` * xref::syntax/operators.adoc#query-operator-comparison-string-specific[String Comparison Operators]: `STARTS WITH`, `ENDS WITH`, `=~` (regex matching) diff --git a/modules/ROOT/pages/expressions/expressions-overview.adoc b/modules/ROOT/pages/expressions/expressions-overview.adoc index e45863e9f..94251910c 100644 --- a/modules/ROOT/pages/expressions/expressions-overview.adoc +++ b/modules/ROOT/pages/expressions/expressions-overview.adoc @@ -1,4 +1,4 @@ -= Overview += Expressions overview :description: Overview of the expressions allowed in Cypher. This page contains an overview of the allowed expressions in Cypher. @@ -6,19 +6,21 @@ This page contains an overview of the allowed expressions in Cypher. [[general]] == General -* 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 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: `COUNT {}`, `COLLECT {}`, `EXISTS {}`, `CALL {}`. -* A regular expression: `a.name =~ 'Tim.*'`. -* A `CASE` expression. -* `null`. +* A xref:syntax/variables.adoc[variable]: `n`, `x`, `rel`, `myFancyVariable`, `++`A name with special characters in it[]!`++`. +* A xref:queries/concepts.adoc[property]: `n.prop`, `x.prop`, `rel.thisProperty`, `++myFancyVariable.`(special property name)`++`. +For more information, see xref:values-and-types/property-structural-constructed.adoc#property-types[Values and types -> property types]. +* A xref:clauses/set.adoc#dynamic-set-property[dynamic property]: `n["prop"]`, `rel[n.city + n.zip]`, `map[coll[0]]`. +For more information, see xref:clauses/where.adoc#filter-on-dynamic-property[`WHERE` -> filter on dynamically computed properties]. +* A xref:syntax/parameters.adoc[parameter]: `$param`, `$0`. +* A xref:values-and-types/lists.adoc[list of expressions]: `['a', 'b']`, `[1, 2, 3]`, `['a', 2, n.property, $param]`, `[]`. +* A xref:functions/index.adoc[function] call: `length(p)`, `nodes(p)`. +* An xref:functions/aggregating.adoc[aggregating function] call: `avg(x.prop)`, `+count(*)+`. +* A xref:patterns/fixed-length-patterns.adoc#path-patterns[path-pattern]: `+(a)-[r]->(b)+`, `+(a)-[r]-(b)+`, `+(a)--(b)+`, `+(a)-->()<--(b)+`. +* An xref:syntax/operators.adoc[operator application]: `1 + 2`, `3 < 4`. +* A xref:subqueries/index.adoc[subquery expression]: `COUNT {}`, `COLLECT {}`, `EXISTS {}`, `CALL {}`. +* A xref:expressions/predicates/string-operators#regular-expressions[regular expression]: `a.name =~ 'Tim.*'`. +* A xref:expressions/conditional-expressions.adoc[`CASE` expression]. +* xref:values-and-types/working-with-null[`null`]. [NOTE] ==== @@ -30,7 +32,7 @@ Learn more in link:https://neo4j.com/developer/kb/protecting-against-cypher-inje [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]. +Notable exceptions are the operators `IS NULL`, `IS NOT NULL`, and the xref:expressions/predicates/type-predicate-expressions.adoc[type predicate expressions]. ==== [[numerical]] diff --git a/modules/ROOT/pages/expressions/index.adoc b/modules/ROOT/pages/expressions/index.adoc index 144420147..1b1812a3a 100644 --- a/modules/ROOT/pages/expressions/index.adoc +++ b/modules/ROOT/pages/expressions/index.adoc @@ -1,9 +1,14 @@ = Expressions -In Cypher, an expression is any combination of components that evaluates to a result. -Expressions are used to perform operations like calculations, comparisons, and data transformations within a query. - -This section includes: +A Cypher expression is any part of a query that evaluates to a value. +For details and examples of specific expressions, see the following sections: * xref:expressions/expressions-overview.adoc[] +* xref:expressions/predicates/index.adoc[] +** xref:expressions/predicates/boolean-operators.adoc[]: `AND`, `OR`, `XOR`, `NOT` +** xref:expressions/predicates/comparison-operators.adoc[]: `=`, `<>`, `<`, `>`, `\<=`, `>=`, `IS NULL`, `IS NOT NULL` +** xref:expressions/predicates/list-operators.adoc[]: `IN` +** xref:expressions/predicates/string-operators.adoc[]: `STARTS WITH`, `ENDS WITH`, `CONTAINS`, `IS NORMALIZED`, `IS NOT NORMALIZED`, `=~` +** xref:expressions/predicates/path-pattern-expressions.adoc[]: information about filtering queries with path pattern expressions. +** xref:expressions/predicates/type-predicate-expressions.adoc[]: information about how to verify the value type of a Cypher expression. * xref:expressions/conditional-expressions.adoc[] diff --git a/modules/ROOT/pages/expressions/predicates/boolean-operators.adoc b/modules/ROOT/pages/expressions/predicates/boolean-operators.adoc new file mode 100644 index 000000000..14b614313 --- /dev/null +++ b/modules/ROOT/pages/expressions/predicates/boolean-operators.adoc @@ -0,0 +1,154 @@ += Boolean operators +:description: Information about Cypher's boolean operators. +:table-caption!: + +Boolean operators are used to combine or evaluate logical conditions. +Cypher contains the following boolean operators: + +* Conjunction: `AND` +* Disjunction: `OR` +* Exclusive disjunction: `XOR` +* Negation: `NOT` + +.Truth table for boolean operators +[options="header", cols="^,^,^,^,^,^", width="85%"] +|=== +|a | b | a `AND` b | a `OR` b | a `XOR` b | `NOT` a +|`FALSE` | `FALSE` | `FALSE` | `FALSE` | `FALSE` | `TRUE` +|`FALSE` | `NULL` | `FALSE` | `NULL` | `NULL` | `TRUE` +|`FALSE` | `TRUE` | `FALSE` | `TRUE` | `TRUE` | `TRUE` +|`TRUE` | `FALSE` | `FALSE` | `TRUE` | `TRUE` | `FALSE` +|`TRUE` | `NULL` | `NULL` | `TRUE` | `NULL` | `FALSE` +|`TRUE` | `TRUE` | `TRUE` | `TRUE` | `FALSE` | `FALSE` +|`NULL` | `FALSE` | `FALSE` | `NULL` | `NULL` | `NULL` +|`NULL` | `NULL` | `NULL` | `NULL` | `NULL` | `NULL` +|`NULL` | `TRUE` | `NULL` | `TRUE` | `NULL` | `NULL` +|=== + +[[example-graph]] +== Example graph + +The following graph is used for the examples below: + +image::predicate_operators.svg[width="500",role="middle"] + +To recreate the graph, run the following query in an empty Neo4j database: + +[source, cypher, role=test-setup] +---- +CREATE (alice:Person {name:'Alice', age: 65, role: 'Project manager', email: 'alice@company.com'}), + (cecil:Person {name: 'Cecil', age: 25, role: 'Software developer', email: 'cecil@private.se'}), + (cecilia:Person {name: 'Cecilia', age: 31, role: 'Software developer'}), + (charlie:Person {name: 'Charlie', age: 61, role: 'Security engineer'}), + (daniel:Person {name: 'Daniel', age: 39, role: 'Director', email: 'daniel@company.com'}), + (eskil:Person {name: 'Eskil', age: 39, role: 'CEO', email: 'eskil@company.com'}) +---- + +== Examples + +.Boolean operators +===== + +.`AND` operator +[source, cypher] +---- +MATCH (n:Person) +WHERE n.age > 30 AND n.role = 'Software developer' +RETURN n.name AS name, n.age AS age, n.role AS role +---- + +.Result +[role="queryresult",options="header,footer",cols="3* 30 XOR n.role = 'Software developer' +RETURN n.name AS name, n.age AS age, n.role AS role +---- + +.Result +[role="queryresult",options="header,footer",cols="5* 60 AND n.role = 'Security engineer') OR NOT (n.role = 'Director' OR n.name = 'Eskil') +RETURN n.name AS name, n.age AS age, n.role AS role +---- + +.Result +[role="queryresult",options="header,footer",cols="3*` +* Less than: `<` +* Greater than: `>` +* Less than or equal to: `\<=` +* Greater than or equal to: `>=` +* `IS NULL` +* `IS NOT NULL` + +[TIP] +For more information about how Cypher orders and compares different value types, see xref:values-and-types/ordering-equality-comparison.adoc[Values and types -> Equality, ordering, and comparison of value types] + + +[[example-graph]] +== Example graph + +The following graph is used for the examples below: + +image::predicate_operators.svg[width="500",role="middle"] + +To recreate the graph, run the following query in an empty Neo4j database: + +[source, cypher, role=test-setup] +---- +CREATE (alice:Person {name:'Alice', age: 65, role: 'Project manager', email: 'alice@company.com'}), + (cecil:Person {name: 'Cecil', age: 25, role: 'Software developer', email: 'cecil@private.se'}), + (cecilia:Person {name: 'Cecilia', age: 31, role: 'Software developer'}), + (charlie:Person {name: 'Charlie', age: 61, role: 'Security engineer'}), + (daniel:Person {name: 'Daniel', age: 39, role: 'Director', email: 'daniel@company.com'}), + (eskil:Person {name: 'Eskil', age: 39, role: 'CEO', email: 'eskil@company.com'}) +---- + +[[examples]] +== Examples + +.Comparison operators +===== + +.Equality operator (`=`) +[source, cypher] +---- +MATCH (n:Person) +WHERE n.role = 'Software developer' +RETURN n.name AS name, n.role AS role +---- + +.Result +[role="queryresult",options="header,footer",cols="2*`) +[source, cypher] +---- +MATCH (n:Person) +WHERE n.role <> 'Software developer' +RETURN n.name AS name, n.role AS role +---- + +.Result +[role="queryresult",options="header,footer",cols="2*`) +[source, cypher] +---- +MATCH (n:Person) +WHERE n.age > 39 +RETURN n.name AS name, n.age AS age +---- + +.Result +[role="queryresult",options="header,footer",cols="2*`) +[source, cypher] +---- +MATCH (n:Person) +WHERE n.age => 39 +RETURN n.name AS name, n.age AS age +---- + +.Result +[role="queryresult",options="header,footer",cols="2* z`, `x` and `z` are not compared. + +[[chaining-equality-operators]] +=== Chaining equality operators + +Chains of `=` and `<>` are treated in a special way in Cypher. +Specifically, `1=1=true` is equivalent to `1=1 AND 1=true` and not to `(1=1)=true` or `1=(1=true)`. +For example, the following expressions are equivalent. + +.Equivalent expressions +[source, syntax, role=noplay] +---- +a < b = c <= d <> e; +a < b AND b = c AND c <= d AND d <> e +---- diff --git a/modules/ROOT/pages/expressions/predicates/index.adoc b/modules/ROOT/pages/expressions/predicates/index.adoc new file mode 100644 index 000000000..7ca49610d --- /dev/null +++ b/modules/ROOT/pages/expressions/predicates/index.adoc @@ -0,0 +1,20 @@ += Predicates +:Description: Overview of the predicate expressions in Cypher. + +Predicates evaluate to a `BOOLEAN` value (`TRUE`, `FALSE`, or `NULL`), and are frequently used for filtering in xref:clauses/where.adoc[`WHERE`] subclauses. + +This chapter is divided into the following sections: + +* xref:expressions/predicates/boolean-operators.adoc[]: `AND`, `OR`, `XOR`, `NOT` +* xref:expressions/predicates/comparison-operators.adoc[]: `=`, `<>`, `<`, `>`, `\<=`, `>=`, `IS NULL`, `IS NOT NULL` +* xref:expressions/predicates/list-operators.adoc[]: `IN` +* xref:expressions/predicates/string-operators.adoc[]: `STARTS WITH`, `ENDS WITH`, `CONTAINS`, `IS NORMALIZED`, `IS NOT NORMALIZED`, `=~` +* xref:expressions/predicates/path-pattern-expressions.adoc[]: information about filtering queries with path pattern expressions. +* xref:expressions/predicates/type-predicate-expressions.adoc[]: information about how to verify the value type of a Cypher expression. + +The following can also serve as a predicates if they result in a `BOOLEAN` value (but are documented elsewhere): + +* xref:syntax/variables.adoc[] +* xref:queries/concepts.adoc[Properties] (See also xref:values-and-types/property-structural-constructed.adoc#property-types[Values and types -> property types]) +* xref:functions/predicate.adoc[Predicate functions] +* xref:expressions/conditional-expressions.adoc[] diff --git a/modules/ROOT/pages/expressions/predicates/list-operators.adoc b/modules/ROOT/pages/expressions/predicates/list-operators.adoc new file mode 100644 index 000000000..0f1d9365d --- /dev/null +++ b/modules/ROOT/pages/expressions/predicates/list-operators.adoc @@ -0,0 +1,273 @@ += List operators +:description: Information about Cypher's list operators. +:table-caption!: + +List operators are used to perform operations on xref:values-and-types/lists.adoc[`LIST`] values. +Cypher contains the following list operator: + +* Membership: `IN` + +For additional list predicates, see: + +* The following predicate functions: xref:functions/predicate.adoc#functions-all[`all()`], xref:functions/predicate.adoc#functions-any[`any()`], xref:functions/predicate.adoc#functions-none[`none()`], and xref:functions/predicate.adoc#functions-single[`single()`]. +* The xref:functions/predicate.adoc#functions-isempty[`isEmpty()`] function. + +[[example-graph]] +== Example graph + +The following graph is used for the examples below: + +image::predicate_operators.svg[width="500",role="middle"] + +To recreate the graph, run the following query in an empty Neo4j database: + +[source, cypher, role=test-setup] +---- +CREATE (alice:Person {name:'Alice', age: 65, role: 'Project manager', email: 'alice@company.com'}), + (cecil:Person {name: 'Cecil', age: 25, role: 'Software developer', email: 'cecil@private.se'}), + (cecilia:Person {name: 'Cecilia', age: 31, role: 'Software developer'}), + (charlie:Person {name: 'Charlie', age: 61, role: 'Security engineer'}), + (daniel:Person {name: 'Daniel', age: 39, role: 'Director', email: 'daniel@company.com'}), + (eskil:Person {name: 'Eskil', age: 39, role: 'CEO', email: 'eskil@company.com'}) +---- + +== Examples + +.Basic `LIST` membership checks +===== + +.`IN` operator +[source, cypher] +---- +MATCH (n:Person) +WHERE n.role IN ['Software developer', 'Project manager'] +RETURN n.name AS name, n.role AS role +---- + +.Result +[role="queryresult",options="header,footer",cols="2*(alice), + (cecilia)-[:WORKS_FOR {since: 2015}]->(alice) +---- + +[[filter-on-patterns]] +== Examples + +.Simple path pattern expression +[source, cypher] +---- +MATCH (employee:Person) +WHERE (employee)-[:WORKS_FOR]->(:Person {name: 'Alice'}) +RETURN employee.name AS employee +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(:Person {name: 'Alice'}) +RETURN employee.name AS employee +---- + + +.Result +[role="queryresult",options="header,footer",cols="1* Boolean operators]. + +Patterns can be placed inside expressions. + +.Pattern inside an expression +[source, cypher] +---- +RETURN NOT (:Person {name: "Alice"})<-[:WORKS_FOR {since: 2023}]-(:Person) AS patternCheck +---- + +.Result +[role="queryresult",options="header,footer",cols="1*(:Person {name: "Alice"})) AS patternCheck +---- + +.Result +[role="queryresult",options="header,footer",cols="1* AS isIntList ---- [role="queryresult",options="header,footer",cols="2* as isMixedList [role="queryresult",options="header,footer",cols="1*+`, `+<+`, `+>+`, `+<=+`, `+>=+`, `IS NULL`, `IS NOT NULL` -| xref::syntax/operators.adoc#query-operators-comparison[`STRING`-specific comparison operators] | `STARTS WITH`, `ENDS WITH`, `CONTAINS`, `=~` (regex matching) -| xref::syntax/operators.adoc#query-operators-boolean[Boolean operators] | `AND`, `OR`, `XOR`, `NOT` | xref::syntax/operators.adoc#query-operators-string[String operators] | `+` and `\|\|` (string concatenation), `IS NORMALIZED` | xref::syntax/operators.adoc#query-operators-temporal[Temporal operators] | `+` and `-` for operations between durations and temporal instants/durations, `*` and `/` for operations between durations and numbers | xref::syntax/operators.adoc#query-operators-map[Map operators] | `.` for static value access by key, `[]` for dynamic value access by key @@ -131,7 +128,7 @@ RETURN DISTINCT restaurant.name 1+d|Rows: 1 |=== -See xref::clauses/where.adoc#query-where-basic[Basic usage] for more details on dynamic property access. +See also xref::clauses/where.adoc#filter-on-dynamic-properties[`WHERE` -> Filter on dynamic properties]. [NOTE] ==== @@ -245,290 +242,6 @@ RETURN b - a AS result |=== -[[query-operators-comparison]] -== Comparison operators - -The comparison operators comprise: - -* equality: `+=+` -* inequality: `+<>+` -* less than: `+<+` -* greater than: `+>+` -* less than or equal to: `+<=+` -* greater than or equal to: `+>=+` -* `IS NULL` -* `IS NOT NULL` - - -[[query-operator-comparison-string-specific]] -=== `STRING`-specific comparison operators comprise: - -* `STARTS WITH`: perform case-sensitive prefix searching on `STRING` values. -* `ENDS WITH`: perform case-sensitive suffix searching on `STRING` values. -* `CONTAINS`: perform case-sensitive inclusion searching in `STRING` values. -* `=~`: regular expression for matching a pattern. - -[[syntax-comparing-two-numbers]] -=== Comparing two numbers - -.Query -[source, cypher] ----- -WITH 4 AS one, 3 AS two -RETURN one > two AS result ----- - -.Result -[role="queryresult",options="header,footer",cols="1*` operators. - -Values of the same type are only equal if they are the same identical value (e.g. `3 = 3` and `"x" <> "xy"`). - -Maps are only equal if they map exactly the same keys to equal values and lists are only equal if they contain the same sequence of equal values (e.g. `[3, 4] = [1+2, 8/2]`). - -Values of different types are considered as equal according to the following rules: - -* Paths are treated as lists of alternating nodes and relationships and are equal to all lists that contain that very same sequence of nodes and relationships. -* Testing any value against `null` with both the `=` and the `<>` operators always evaluates to `null`. -This includes `null = null` and `null <> null`. -The only way to reliably test if a value `v` is `null` is by using the special `v IS NULL`, or `v IS NOT NULL`, equality operators. -`v IS NOT NULL` is equivalent to `NOT(v IS NULL)`. - -All other combinations of types of values cannot be compared with each other. -Especially, nodes, relationships, and literal maps are incomparable with each other. - -It is an error to compare values that cannot be compared. - - -[[cypher-ordering]] -=== Ordering and comparison of values - -The comparison operators `+<=+`, `+<+` (for ascending) and `+>=+`, `+>+` (for descending) are used to compare values for ordering. -The following points give some details on how the comparison is performed. - -* Numerical values are compared for ordering using numerical order (e.g. `3 < 4` is true). -* All comparability tests (`+<+`, `+<=+`, `+>+`, `+>=+`) with `java.lang.Double.NaN` evaluate as false. -For example, `1 > b` and `1 < b` are both false when b is NaN. -* String values are compared for ordering using lexicographic order (e.g. `"x" < "xy"`). -* Boolean values are compared for ordering such that `false < true`. -* Spatial values cannot be compared using the operators `<`, `+<=+`, `>`, or `>=`. -To compare spatial values within a specific range, use either the xref:functions/spatial.adoc#functions-withinBBox[`point.withinBBox()`] or the xref:functions/spatial.adoc#functions-point-wgs84-2d[`point()`] function. - -* *Ordering* of spatial values: - ** `ORDER BY` requires all values to be orderable. - ** Points are ordered after arrays and before temporal types. - ** Points of different CRS are ordered by the CRS code (the value of SRID field). For the currently supported set of xref::values-and-types/spatial.adoc#cypher-spatial-crs[Coordinate Reference Systems] this means the order: 4326, 4979, 7302, 9157 - ** Points of the same CRS are ordered by each coordinate value in turn, `x` first, then `y` and finally `z`. - ** Note that this order is different to the order returned by the spatial index, which will be the order of the space filling curve. -* *Comparison* of temporal values: - ** xref::values-and-types/temporal.adoc#cypher-temporal-instants[Temporal instant values] are comparable within the same type. - An instant is considered less than another instant if it occurs before that instant in time, and it is considered greater than if it occurs after. - ** Instant values that occur at the same point in time -- but that have a different time zone -- are not considered equal, and must therefore be ordered in some predictable way. - Cypher prescribes that, after the primary order of point in time, instant values be ordered by effective time zone offset, from west (negative offset from UTC) to east (positive offset from UTC). - This has the effect that times that represent the same point in time will be ordered with the time with the earliest local time first. - If two instant values represent the same point in time, and have the same time zone offset, but a different named time zone (this is possible for _DateTime_ only, since _Time_ only has an offset), these values are not considered equal, and ordered by the time zone identifier, alphabetically, as its third ordering component. - If the type, point in time, offset, and time zone name are all equal, then the values are equal, and any difference in order is impossible to observe. - ** xref::values-and-types/temporal.adoc#cypher-temporal-durations[_Duration_] values cannot be compared, since the length of a _day_, _month_ or _year_ is not known without knowing which _day_, _month_ or _year_ it is. - Since _Duration_ values are not comparable, the result of applying a comparison operator between two _Duration_ values is `null`. -* *Ordering* of temporal values: - ** `ORDER BY` requires all values to be orderable. - ** Temporal instances are ordered after spatial instances and before strings. - ** Comparable values should be ordered in the same order as implied by their comparison order. - ** Temporal instant values are first ordered by type, and then by comparison order within the type. - ** Since no complete comparison order can be defined for _Duration_ values, we define an order for `ORDER BY` specifically for _Duration_: - *** _Duration_ values are ordered by normalising all components as if all years were `365.2425` days long (`PT8765H49M12S`), all months were `30.436875` (`1/12` year) days long (`PT730H29M06S`), and all days were `24` hours long footnote:[The `365.2425` days per year comes from the frequency of leap years. - A leap year occurs on a year with an ordinal number divisible by `4`, that is not divisible by `100`, unless it divisible by `400`. - This means that over `400` years there are `((365 * 4 + 1) * 25 - 1) * 4 + 1 = 146097` days, which means an average of `365.2425` days per year.]. -* Comparing for ordering when one argument is `null` (e.g. `null < 3` is `null`). -* *Ordering* of values with *different* types: - ** The ordering is, in ascending order, defined according to the following list: - *** xref::values-and-types/maps.adoc#cypher-literal-maps[`MAP`] - *** xref::values-and-types/property-structural-constructed.adoc#structural-types[`NODE`] - *** xref::values-and-types/property-structural-constructed.adoc#structural-types[`RELATIONSHIP`] - *** xref::values-and-types/lists.adoc[`LIST`] - *** xref::patterns/fixed-length-patterns.adoc#path-patterns[`PATH`] - *** xref::values-and-types/temporal.adoc[`ZONED DATETIME`] - *** xref::values-and-types/temporal.adoc[`LOCAL DATETIME`] - *** xref::values-and-types/temporal.adoc[`DATE`] - *** 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/expressions-overview.adoc#string[`STRING`] - *** xref::expressions/expressions-overview.adoc#boolean[`BOOLEAN`] - *** Numbers: xref::expressions/expressions-overview.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. -For example, `[1, 'foo', 3]` is ordered before `[1, 2, 'bar']` since `'foo'` is ordered before `2`. - - -[[cypher-operations-chaining]] -=== Chaining comparison operations - -Comparisons can be chained arbitrarily, e.g., `+x < y <= z+` is equivalent to `+x < y AND y <= z+`. - -Formally, if `+a, b, c, ..., y, z+` are expressions and `+op1, op2, ..., opN+` are comparison operators, then `+a op1 b op2 c ... y opN z+` is equivalent to `+a op1 b and b op2 c and ... y opN z+`. - -Note that `a op1 b op2 c` does not imply any kind of comparison between `a` and `c`, so that, e.g., `x < y > z` is perfectly legal (although perhaps not elegant). - -The example: - -[source, cypher] ----- -MATCH (n) WHERE 21 < n.age <= 30 RETURN n ----- - -is equivalent to - -[source, cypher] ----- -MATCH (n) WHERE 21 < n.age AND n.age <= 30 RETURN n ----- - -Thus, it matches all nodes where the age is between 21 and 30. - -This syntax extends to all equality `=` and inequality `<>` comparisons, as well as to chains longer than three. - -[NOTE] -==== -Chains of `=` and `<>` are treated in a special way in Cypher. - -This means that `1=1=true` is equivalent to `1=1 AND 1=true` and not to `(1=1)=true` or `1=(1=true)`. -==== - -For example: - -[source, syntax, role=noplay] ----- -a < b = c <= d <> e ----- - -Is equivalent to: - -[source, syntax, role=noplay] ----- -a < b AND b = c AND c <= d AND d <> e ----- - - -[[syntax-using-a-regular-expression-to-filter-words]] -=== Using a regular expression with `=~` to filter words - -.Query -[source, cypher] ----- -WITH ['mouse', 'chair', 'door', 'house'] AS wordlist -UNWIND wordlist AS word -WITH word -WHERE word =~ '.*ous.*' -RETURN word ----- - -.Result -[role="queryresult",options="header,footer",cols="1* 6 AND number < 10) -RETURN number ----- - -.Result -[role="queryresult",options="header,footer",cols="1*`) operators] allows for comparing the equality of different values. + +Values of the same type are only equal if they are the same identical value (e.g. `3 = 3` and `"x" <> "xy"`). + +Maps are only equal if they map exactly the same keys to equal values and lists are only equal if they contain the same sequence of equal values (e.g. `[3, 4] = [1+2, 8/2]`). + +Values of different types are considered as equal according to the following rules: + +* `PATH` values are treated as lists of alternating nodes and relationships and are equal to all lists that contain an identical sequence of nodes and relationships. +* Testing any value against `NULL` with either the `=` or `<>` operator always evaluates to `NULL`. +This includes `NULL = NULL` and `NULL <> NULL`. +To reliably test if a value is `NULL` use `v IS NULL` or `v IS NOT NULL` (the latter is equivalent to `NOT(v IS NULL)`). + +Other combinations of value types cannot be compared with each other. +For example, nodes, relationships, and literal maps cannot be compared to one another. +Comparing incomparable values will throw an error. + +[[ordering-and-comparison]] +== Ordering and comparison of values + +xref:clauses/order-by.adoc[`ORDER BY`] requires that all values are orderable. +The following points explain how comparisons are made when using the `\<=`, `<`,`>=`, `>` operators. + +* Numerical values are compared for ordering using numerical order (e.g. `3 < 4` is `TRUE`). +* All comparability tests with `java.lang.Double.NaN` evaluate as `FALSE`. +For example, `1 > b` and `1 < b` are both `FALSE` when `b` is NaN. +* String values are compared for ordering using lexicographic order (e.g. `"x" < "xy"`). +* Boolean values are compared for ordering such that `FALSE < TRUE`. +* When comparing values for ordering, if one of the arguments is `NULL`, the result is always `NULL`. +* xref:values-and-types/spatial.adoc[Spatial values] cannot be compared using the operators `\<=`, `<`,`>=`, `>`. +To compare spatial values within a specific range, use either the xref:functions/spatial.adoc#functions-withinBBox[`point.withinBBox()`] or the xref:functions/spatial.adoc#functions-point-wgs84-2d[`point()`] function. + +[[value-hierarchy]] +=== Hierarchy of values + +Values of different types are ordered based on a predefined hierarchy, from least to greatest, as outlined in the following list: + +* xref::values-and-types/maps.adoc#cypher-literal-maps[`MAP`] +* xref::values-and-types/property-structural-constructed.adoc#structural-types[`NODE`] +* xref::values-and-types/property-structural-constructed.adoc#structural-types[`RELATIONSHIP`] +* xref::values-and-types/lists.adoc[`LIST`] +* xref::patterns/fixed-length-patterns.adoc#path-patterns[`PATH`] +* xref::values-and-types/temporal.adoc[`ZONED DATETIME`] +* xref::values-and-types/temporal.adoc[`LOCAL DATETIME`] +* xref::values-and-types/temporal.adoc[`DATE`] +* xref::values-and-types/temporal.adoc[`ZONED TIME`] +* xref::values-and-types/temporal.adoc[`LOCAL TIME`] +* xref::values-and-types/temporal.adoc[`DURATION`] +* xref::values-and-types/spatial.adoc[`POINT`] +* xref::expressions/expressions-overview.adoc#string[`STRING`] +* xref::expressions/expressions-overview.adoc#boolean[`BOOLEAN`] +* Numbers: xref::expressions/expressions-overview.adoc#numerical[`INTEGER`, `FLOAT`] + +[NOTE] +`NULL` is ordered after all other values. + +.Sorting rules for mixed Cypher types +[source, cypher] +---- +WITH [42, "hello", NULL, true, {name: "Alice"}, [1, 2, 3], date("2024-02-10")] AS v +UNWIND v AS values +RETURN values +ORDER BY values +---- + +.Result +[role="queryresult",options="header,footer",cols="1* Constructed types]. + +[[ordering-spatial-temporal]] +=== Ordering spatial and temporal values + +The following applies to the ordering of xref:values-and-types/spatial.adoc[spatial types]: + +* `POINT` values are ordered after lists and before temporal types. +* `POINT` values of different coordinate reference systems (CRS) are ordered by the CRS code (the value of SRID field). +For the currently supported set of xref::values-and-types/spatial.adoc#cypher-spatial-crs[CRS], the following ascending order applies: `4326`, `4979`, `7302`, `9157`. +* `POINT` values with the same CRS are ordered by each coordinate value in turn; first `x`, then `y`, and finally `z`. +* Note that this ordering is different to the order returned by the spatial index, which follows the space filling curve. + +The following applies to the ordering of xref:values-and-types/temporal.adoc[temporal types]: + +* Temporal types are ordered after spatial types but before strings. +* Temporal values follow a chronological order. +For example, `2023-01-01` comes before `2024-01-01`. +* Temporal values are first sorted by type, then by value. +For example, `DATETIME` is considered "greater" than a `DATE` and `2023-02-10T12:00:00` comes before `2023-02-10T15:00:00` because it is chronologically earlier. +* Since there is no perfect way to compare duration values (because months and years have varying lengths), Cypher defines a specific rule for sorting them in `ORDER BY`: +** 1 year is treated as 365.2425 days (to account for leap years). +** 1 month is treated as 30.436875 days (which is 1/12 of a year). +** 1 day is always 24 hours. + +The following applies to the comparison of temporal types: + +* xref::values-and-types/temporal.adoc#cypher-temporal-instants[Temporal instant values] (like `DATETIME` and `DATE`) can be compared if they are of the same type. +An earlier instant is considered smaller (less than) compared to a later instant. +* Instants at the same point in time but with different time zones are not considered equal. +To ensure consistent ordering, Cypher sorts them first by their actual point in time. +If two instants have the same time but different time zones, they are ordered by their UTC offset (west to east, meaning negative offsets come first). +If they have the same time and offset but different named time zones, they are sorted alphabetically by the time zone name. +* Duration values cannot be directly compared. +Since the length of a day, month, or year varies, Cypher does not define a strict ordering for durations. +As a result, comparing two durations `(e.g, duration1 < duration2)` will always return `NULL`. 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 168419952..98f6c6240 100644 --- a/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc +++ b/modules/ROOT/pages/values-and-types/property-structural-constructed.adoc @@ -74,7 +74,7 @@ For more details, see xref::values-and-types/working-with-null.adoc[working with The table below shows the types and their syntactic synonyms. -These types (and their synonyms) can be used in xref::values-and-types/type-predicate.adoc[type predicate expressions] and in xref::constraints/managing-constraints.adoc#create-property-type-constraints[property type constraints]. +These types (and their synonyms) can be used in xref::expressions/predicates/type-predicate-expressions.adoc[type predicate expressions] and in xref::constraints/managing-constraints.adoc#create-property-type-constraints[property type constraints]. They are also returned as a `STRING` value when using the xref::functions/scalar.adoc#functions-valueType[valueType()] function. However, not all types can be used in all places. @@ -121,7 +121,7 @@ The type `PROPERTY VALUE` is expanded to a closed dynamic union of all valid pro For example, given the closed dynamic type `BOOL | LIST | BOOLEAN | LIST`, the normalized type would be: `BOOLEAN | LIST`. -This normalization is run on types used in xref::values-and-types/type-predicate.adoc[type predicate expressions], and in xref::constraints/managing-constraints.adoc#create-property-type-constraints[property type constraints]. +This normalization is run on types used in xref::expressions/predicates/type-predicate-expressions.adoc[type predicate expressions], and in xref::constraints/managing-constraints.adoc#create-property-type-constraints[property type constraints]. Type normalization is also used to ensure the consistency of the output for the xref::functions/scalar.adoc#functions-valueType[valueType()] function. [[ordering-of-types]] 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 7c96a40fa..e980d0494 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::values-and-types/type-predicate.adoc#type-predicate-null[type predicate expressions] always return `true` for `null` values. +This means that xref::expressions/predicates/type-predicate-expressions.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`.