From d3aafdc58ee2f3367d11e6b1f09d848c13d66afc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Wed, 26 Mar 2025 09:21:55 +0100 Subject: [PATCH 01/10] initial structure --- modules/ROOT/content-nav.adoc | 1 + modules/ROOT/pages/expressions/index.adoc | 1 + .../pages/expressions/list-expressions.adoc | 20 +++++++++++++++++++ 3 files changed, 22 insertions(+) create mode 100644 modules/ROOT/pages/expressions/list-expressions.adoc diff --git a/modules/ROOT/content-nav.adoc b/modules/ROOT/content-nav.adoc index 5bc92b6e8..b1bf4a547 100644 --- a/modules/ROOT/content-nav.adoc +++ b/modules/ROOT/content-nav.adoc @@ -76,6 +76,7 @@ ** xref:expressions/mathematical-operators.adoc[] ** xref:expressions/string-operators.adoc[] ** xref:expressions/temporal-operators.adoc[] +** xref:expressions/list-expressions.adoc[] ** xref:expressions/conditional-expressions.adoc[] * xref:functions/index.adoc[] diff --git a/modules/ROOT/pages/expressions/index.adoc b/modules/ROOT/pages/expressions/index.adoc index 3dbcd364e..a02658a8f 100644 --- a/modules/ROOT/pages/expressions/index.adoc +++ b/modules/ROOT/pages/expressions/index.adoc @@ -14,5 +14,6 @@ For details and examples of specific expressions, see the following sections: * xref:expressions/mathematical-operators.adoc[]: `+`, `-`, `*`, `/`, `%`, `^`. * xref:expressions/string-operators.adoc[]: `+`, `||` * xref:expressions/temporal-operators.adoc[]: `+`, `-`, `*`, `/` +* xref:expressions/list-expressions.adoc[]: information about list concatenation operators, list element access, list slicing, and list as well as pattern comprehensions. * xref:expressions/conditional-expressions.adoc[] diff --git a/modules/ROOT/pages/expressions/list-expressions.adoc b/modules/ROOT/pages/expressions/list-expressions.adoc new file mode 100644 index 000000000..20018a39b --- /dev/null +++ b/modules/ROOT/pages/expressions/list-expressions.adoc @@ -0,0 +1,20 @@ += List expressions +:description: Information about Cypher's list expressions. +:table-caption!: + +List expressions are a way to manipulate and query lists in Cypher. +They allow for operations like concatenation, element access, slicing, and transformations using comprehensions. + +For more expressions that evaluate to a xref:values-and-types/lists.adoc[`LIST`] value, see xref:functions/list.adoc[List functions]. + +== List operators + +== List element access + +== List slicing + +== List comprehension + +== Pattern comprehension + + From 88271da8a3c17e8a33b21967b6386981fcc1f57b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Fri, 28 Mar 2025 09:17:23 +0100 Subject: [PATCH 02/10] list element access --- modules/ROOT/pages/expressions/index.adoc | 2 +- .../pages/expressions/list-expressions.adoc | 211 +++++++++++++++++- modules/ROOT/pages/syntax/operators.adoc | 210 ----------------- 3 files changed, 206 insertions(+), 217 deletions(-) diff --git a/modules/ROOT/pages/expressions/index.adoc b/modules/ROOT/pages/expressions/index.adoc index a02658a8f..f2b0fc0ec 100644 --- a/modules/ROOT/pages/expressions/index.adoc +++ b/modules/ROOT/pages/expressions/index.adoc @@ -14,6 +14,6 @@ For details and examples of specific expressions, see the following sections: * xref:expressions/mathematical-operators.adoc[]: `+`, `-`, `*`, `/`, `%`, `^`. * xref:expressions/string-operators.adoc[]: `+`, `||` * xref:expressions/temporal-operators.adoc[]: `+`, `-`, `*`, `/` -* xref:expressions/list-expressions.adoc[]: information about list concatenation operators, list element access, list slicing, and list as well as pattern comprehensions. +* xref:expressions/list-expressions.adoc[]: information about list concatenation operators (`||`, `+`), list element access, list slicing, and list as well as pattern comprehensions. * xref:expressions/conditional-expressions.adoc[] diff --git a/modules/ROOT/pages/expressions/list-expressions.adoc b/modules/ROOT/pages/expressions/list-expressions.adoc index 20018a39b..b9d7494c3 100644 --- a/modules/ROOT/pages/expressions/list-expressions.adoc +++ b/modules/ROOT/pages/expressions/list-expressions.adoc @@ -2,19 +2,218 @@ :description: Information about Cypher's list expressions. :table-caption!: -List expressions are a way to manipulate and query lists in Cypher. +List expressions are a way to manipulate and query xref:values-and-types/lists.adoc[`LIST`] values in Cypher. They allow for operations like concatenation, element access, slicing, and transformations using comprehensions. -For more expressions that evaluate to a xref:values-and-types/lists.adoc[`LIST`] value, see xref:functions/list.adoc[List functions]. +For more expressions that evaluate to a `LIST` value, see xref:functions/list.adoc[List functions]. + +For information about how to check membership in a `LIST` using the `IN` operator, see xref:expressions/predicates/list-operators.adoc[Predicates -> List operators]. -== List operators +== Example graph + +[source, cypher] +---- +CREATE (alice:Person {name:'Alice', age: 65, role: 'Project manager', skills: ['Java', 'Python']}), + (cecil:Person {name: 'Cecil', age: 25, role: 'Software developer', skills: ['Java', 'Python']}), + (cecilia:Person {name: 'Cecilia', age: 31, role: 'Software developer', skills: ['JavaScript', 'TypeScript']}), + (charlie:Person {name: 'Charlie', age: 61, role: 'Security engineer', skills: ['C++', 'Python']}), + (daniel:Person {name: 'Daniel', age: 39, role: 'Director', skills: ['Ruby', 'Go']}), + (eskil:Person {name: 'Eskil', age: 39, role: 'CEO'}), + + (alice)-[:WORKS_FOR]->(daniel), + (cecil)-[:WORKS_FOR]->(alice), + (cecilia)-[:WORKS_FOR]->(alice), + (charlie)-[:WORKS_FOR]->(daniel), + (daniel)-[:WORKS_FOR]->(eskil) +---- + +== List concatenation + +Cypher contains two list concatenation operators: `||` and `+`. +`||` is xref:appendix/gql-conformance/index.adoc[GQL conformant], while `+` is not. + +.List concatenation using `||` and `+` +[source, cypher] +---- +RETURN [1,2] || [3,4] AS list1, + [1,2] + [3,4] AS list2 +---- + +.Result +[role="queryresult",options="header,footer",cols="2* 0 -RETURN count(n) ----- - -As long as `labels(n)` returns either `Person` or `Employee` (or both), the query will return a value greater than zero. - - -[[syntax-accessing-elements-in-a-list]] -=== Accessing elements in a list using the `[]` operator - -.Query -[source, cypher] ----- -WITH ['Anne', 'John', 'Bill', 'Diane', 'Eve'] AS names -RETURN names[1..3] AS result ----- - -The square brackets will extract the elements from the start index `1`, and up to (but excluding) the end index `3`. - -.Result -[role="queryresult",options="header,footer",cols="1* Date: Mon, 31 Mar 2025 11:07:06 +0200 Subject: [PATCH 03/10] add graph, fix list values page --- .../ROOT/images/list_expressions_graph.svg | 1 + .../pages/expressions/list-expressions.adoc | 364 ++++++++++++++++-- .../ROOT/pages/values-and-types/lists.adoc | 166 +------- 3 files changed, 360 insertions(+), 171 deletions(-) create mode 100644 modules/ROOT/images/list_expressions_graph.svg diff --git a/modules/ROOT/images/list_expressions_graph.svg b/modules/ROOT/images/list_expressions_graph.svg new file mode 100644 index 000000000..3fdbc2779 --- /dev/null +++ b/modules/ROOT/images/list_expressions_graph.svg @@ -0,0 +1 @@ +WORKS_FORWORKS_FORWORKS_FORWORKS_FORWORKS_FORPersonname:'Alice'age:65role:'Project manager'skills:['Java', 'Python']Personname:'Cecil'age:25role:'Software developer'skills:['Java', 'Python']Personname:'Cecilia'age:31role:'Software developer'skills:['JavaScript', 'TypeScript']Personname:'Charlie'age:61role:'Security engineer'skills:['C++', 'Python']Personname:'Daniel'age:39role:'Director'skills:['Ruby', 'Go']Personname:'Eskil'age:39role:'CEO'skills:['Java', 'C++', 'Python'] \ No newline at end of file diff --git a/modules/ROOT/pages/expressions/list-expressions.adoc b/modules/ROOT/pages/expressions/list-expressions.adoc index b9d7494c3..4f81d848d 100644 --- a/modules/ROOT/pages/expressions/list-expressions.adoc +++ b/modules/ROOT/pages/expressions/list-expressions.adoc @@ -3,13 +3,19 @@ :table-caption!: List expressions are a way to manipulate and query xref:values-and-types/lists.adoc[`LIST`] values in Cypher. -They allow for operations like concatenation, element access, slicing, and transformations using comprehensions. For more expressions that evaluate to a `LIST` value, see xref:functions/list.adoc[List functions]. + For information about how to check membership in a `LIST` using the `IN` operator, see xref:expressions/predicates/list-operators.adoc[Predicates -> List operators]. +[[example-graph]] == Example graph +The following graph is used for the examples below: + +image::list_expressions_graph.svg[width="700",role="middle"] + +To recreate the graph, run the following query against an empty Neo4j database: + [source, cypher] ---- CREATE (alice:Person {name:'Alice', age: 65, role: 'Project manager', skills: ['Java', 'Python']}), @@ -17,15 +23,16 @@ CREATE (alice:Person {name:'Alice', age: 65, role: 'Project manager', skills: [' (cecilia:Person {name: 'Cecilia', age: 31, role: 'Software developer', skills: ['JavaScript', 'TypeScript']}), (charlie:Person {name: 'Charlie', age: 61, role: 'Security engineer', skills: ['C++', 'Python']}), (daniel:Person {name: 'Daniel', age: 39, role: 'Director', skills: ['Ruby', 'Go']}), - (eskil:Person {name: 'Eskil', age: 39, role: 'CEO'}), + (eskil:Person {name: 'Eskil', age: 39, role: 'CEO', skills: ['Java', 'C++', 'Python']}), - (alice)-[:WORKS_FOR]->(daniel), (cecil)-[:WORKS_FOR]->(alice), (cecilia)-[:WORKS_FOR]->(alice), (charlie)-[:WORKS_FOR]->(daniel), + (alice)-[:WORKS_FOR]->(daniel), (daniel)-[:WORKS_FOR]->(eskil) ---- +[[list-concatenation]] == List concatenation Cypher contains two list concatenation operators: `||` and `+`. @@ -65,10 +72,29 @@ RETURN cecil.skills || cecilia.skills AS combinedSkills 1+d|Rows: 1 |=== -The `+` operator can add elements to the beginning of a `LIST` value. +The `+` operator can add elements to the beginning or end of a `LIST` value. This is not possible using the `||` operator. -.Adding elements to the beginning of a `LIST` with `+` +.Adding elements to the beginning of a `LIST` +[source, cypher] +---- +WITH [1, 2, 3, 4] AS list +RETURN 0 + list AS newBeginning, + list + 5 AS newEnd +---- + + +.Result +[role="queryresult",options="header,footer",cols="2* 2 | n] AS filteredList +---- + +.Result +[role="queryresult",options="header,footer",cols="1* 30 | employee.name || ', ' || toString(employee.age)] AS employeesAbove30 +---- + +.Result +[role="queryresult",options="header,footer",cols="1*+(superior:Person) | superior.skills] AS superiorsSkills +---- + +Pattern comprehension only supports only the xref:patterns/reference.adoc#variable-length-relationships[variable-length relationships] syntax. +The belowquery uses a pattern comprehension to collect the skills of all superiors in the chain above `Cecil`. +The xref:functions/list.adoc#functions-reduce[`reduce()`] function concatenates these skills into a single `LIST`, and xref:clauses/unwind.adoc[`UNWIND`] is used to flatten this `LIST` before returning the distinct skills in a new `LIST`. + +.Allowed - pattern comprehension to match patterns of a variable length using variable-length relationship syntax +[source, cypher] +---- +MATCH (cecil:Person {name: 'Cecil'}) +WITH [(cecil)-[:WORKS_FOR*]->(superior:Person) | superior.skills] AS allSuperiorsSkills +WITH reduce(accumulatedSkills = [], superiorSkills IN allSuperiorsSkills | accumulatedSkills || superiorSkills) AS allSkills +UNWIND allSkills AS superiorsSkills +RETURN collect(DISTINCT superiorsSkills) AS distinctSuperiorsSkills +---- + +.Result +[role="queryresult",options="header,footer",cols="1* Predicates -> List operators]. +For information list concatenation (`+` and `||`), list element access and slicing (`[]`), as well as list and pattern comprehensions, see xref:expressions/list-expressions.adoc[List expressions]. [[cypher-lists-general]] == Lists in general @@ -70,7 +67,7 @@ RETURN list[2] 1+d|Rows: 1 |=== -=== List range and size +== List range and size The below examples use the xref::functions/list.adoc#functions-range[`range`] function to create lists. This function returns a list containing all numbers between given start and end numbers. @@ -212,169 +209,40 @@ RETURN size(range(0, 10)[0..3]) AS list |=== -[[cypher-pattern-comprehension]] -== Pattern comprehension - -Pattern comprehension is a syntactic construct available in Cypher for creating a list based on matchings of a pattern. -A pattern comprehension matches the specified pattern like a normal `MATCH` clause, with predicates like a normal `WHERE` clause, but yields a custom projection as specified. - -=== Example graph - -The following graph is used for examples below: - -image::values_and_types_lists_graph.svg[] - -To recreate the graph, run the following query against an empty Neo4j database: - -[source, cypher, role=test-setup] ----- -CREATE - (keanu:Person {name: 'Keanu Reeves'}), - (johnnyMnemonic:Movie {title: 'Johnny Mnemonic', released: 1995}), - (theMatrixRevolutions:Movie {title: 'The Matrix Revolutions', released: 2003}), - (theMatrixReloaded:Movie {title: 'The Matrix Reloaded', released: 2003}), - (theReplacements:Movie {title: 'The Replacements', released: 2000}), - (theMatrix:Movie {title: 'The Matrix', released: 1999}), - (theDevilsAdvocate:Movie {title: 'The Devils Advocate', released: 1997}), - (theMatrixResurrections:Movie {title: 'The Matrix Resurrections', released: 2021}), - (keanu)-[:ACTED_IN]->(johnnyMnemonic), - (keanu)-[:ACTED_IN]->(theMatrixRevolutions), - (keanu)-[:ACTED_IN]->(theMatrixReloaded), - (keanu)-[:ACTED_IN]->(theReplacements), - (keanu)-[:ACTED_IN]->(theMatrix), - (keanu)-[:ACTED_IN]->(theDevilsAdvocate), - (keanu)-[:ACTED_IN]->(theMatrixResurrections) ----- - -=== Examples +== Storing lists as properties -This example returns a list that contains the year when the movies were released. -The pattern matching in the pattern comprehension looks for `Matrix` in the movie title and that the node `keanu` (`Person` node with the name `Keanu Reeves`) has a relationship with the movie. +It is possible to store homogenous lists of simple values as properties. -.Query +.Allowed - store homogenous list as a property [source, cypher] ---- -MATCH (keanu:Person {name: 'Keanu Reeves'}) -RETURN [(keanu)-->(b:Movie) WHERE b.title CONTAINS 'Matrix' | b.released] AS years +CREATE (n:Label) +SET n.listProperty = [1, 2, 3] +RETURN n.listProperty AS homogenousListProperty ---- .Result [role="queryresult",options="header,footer",cols="1*(b:Movie) | b.title] AS movieTitles -SET keanu.resume = movieTitles -RETURN keanu.resume ----- - -.Result -[role="queryresult",options="header,footer",cols="1*(b:Movie) | b.title] + [(keanu)-->(b:Movie) | b.released] AS movieTitles -SET keanu.resume = movieTitles -RETURN keanu.resume +CREATE (n:Label) +SET n.listProperty = [1, "hello", .45, date()] +RETURN n.listProperty AS heterogenousListProperty ---- +.Error [source,error] ---- Neo4j only supports a subset of Cypher types for storage as singleton or array properties. Please refer to section cypher/syntax/values of the manual for more details. ---- - - -[[cypher-list-comprehension]] -== List comprehension - -List comprehension is a syntactic construct available in Cypher for creating a list based on existing lists. - -For example, the following query returns a new list from the previously created `resume` property (a list of strings) of `Keanu Reeves`: - -.Query -[source, cypher] ----- -MATCH (keanu:Person {name:'Keanu Reeves'}) -RETURN [x IN keanu.resume WHERE x contains 'The Matrix'] AS matrixList ----- - -.Result -[role="queryresult",options="header,footer",cols="1* Date: Mon, 31 Mar 2025 11:08:53 +0200 Subject: [PATCH 04/10] cleanup --- modules/ROOT/pages/syntax/operators.adoc | 1 - modules/ROOT/pages/values-and-types/lists.adoc | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/ROOT/pages/syntax/operators.adoc b/modules/ROOT/pages/syntax/operators.adoc index 3a65403d5..3c4195f8e 100644 --- a/modules/ROOT/pages/syntax/operators.adoc +++ b/modules/ROOT/pages/syntax/operators.adoc @@ -12,7 +12,6 @@ This page contains an overview of the available Cypher operators. | xref::syntax/operators.adoc#query-operators-aggregation[Aggregation operators] | `DISTINCT` | xref::syntax/operators.adoc#query-operators-property[Property operators] | `.` for static property access, `[]` for dynamic property access, `=` for replacing all properties, `+=` for mutating specific properties | xref::syntax/operators.adoc#query-operators-map[Map operators] | `.` for static value access by key, `[]` for dynamic value access by key -| xref::syntax/operators.adoc#query-operators-list[List operators] | `+` and `\|\|` (list concatenation), `IN` to check existence of an element in a list, `[]` for accessing element(s) dynamically |=== diff --git a/modules/ROOT/pages/values-and-types/lists.adoc b/modules/ROOT/pages/values-and-types/lists.adoc index 449ccce30..f57fc7a84 100644 --- a/modules/ROOT/pages/values-and-types/lists.adoc +++ b/modules/ROOT/pages/values-and-types/lists.adoc @@ -234,7 +234,7 @@ RETURN n.listProperty AS homogenousListProperty It is not, however, possible to store heterogeneous lists as properties. .Not allowed - store heterogenous list as a property -[source, cypher] +[source, cypher, role=test-fail] ---- CREATE (n:Label) SET n.listProperty = [1, "hello", .45, date()] From 35e14596010ee6681f102657b6eac5946064d103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 31 Mar 2025 12:26:08 +0200 Subject: [PATCH 05/10] more cleanup --- .../pages/appendix/gql-conformance/additional-cypher.adoc | 2 +- modules/ROOT/pages/expressions/list-expressions.adoc | 7 +++---- modules/ROOT/pages/functions/list.adoc | 6 +++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/ROOT/pages/appendix/gql-conformance/additional-cypher.adoc b/modules/ROOT/pages/appendix/gql-conformance/additional-cypher.adoc index ae7fe27a0..1dabfe915 100644 --- a/modules/ROOT/pages/appendix/gql-conformance/additional-cypher.adoc +++ b/modules/ROOT/pages/appendix/gql-conformance/additional-cypher.adoc @@ -623,7 +623,7 @@ GQL supports `GRAPH TYPES` as a way of constraining a graph schema, but does not | xref:syntax/operators.adoc#query-operator-comparison-string-specific[`STARTS WITH`, `CONTAINS`, `ENDS WITH`, and regular expressions]. | `STRING` comparison operators. -| xref:syntax/operators.adoc#query-operators-list[`IN`] +| xref:expressions/predicates/list-operators.adoc[`IN`] | `IN` predicate for `LIST` values. |=== diff --git a/modules/ROOT/pages/expressions/list-expressions.adoc b/modules/ROOT/pages/expressions/list-expressions.adoc index 4f81d848d..cd32d824f 100644 --- a/modules/ROOT/pages/expressions/list-expressions.adoc +++ b/modules/ROOT/pages/expressions/list-expressions.adoc @@ -466,11 +466,11 @@ Pattern comprehension is used to create new `LIST` values by matching graph patt The below query retrieves a list of names of people who work for `Alice` by using pattern comprehension extract the names of `employees` into a `LIST`. -.Pattern comprehension on a fixed-length relationship +.Pattern comprehension on a fixed-length pattern [source, cypher] ---- MATCH (alice:Person {name: 'Alice'}) -RETURN [(alice)<-[:WORKS_FOR]-(employee) | employee.name] AS employees +RETURN [(employee:Person)-[:WORKS_FOR]->(alice) | employee.name] AS employees ---- .Result @@ -485,12 +485,11 @@ RETURN [(alice)<-[:WORKS_FOR]-(employee) | employee.name] AS employees Pattern comprehensions can include `WHERE` predicates. - .Pattern comprehension including a `WHERE` predicate [source, cypher] ---- MATCH (alice:Person {name: 'Alice'}) -RETURN [(alice)<-[:WORKS_FOR]-(employee:Person) WHERE employee.age > 30 | employee.name || ', ' || toString(employee.age)] AS employeesAbove30 +RETURN [(employee:Person)-[:WORKS_FOR]->(alice) WHERE employee.age > 30 | employee.name || ', ' || toString(employee.age)] AS employeesAbove30 ---- .Result diff --git a/modules/ROOT/pages/functions/list.adoc b/modules/ROOT/pages/functions/list.adoc index 86ca9a4a0..e908f844c 100644 --- a/modules/ROOT/pages/functions/list.adoc +++ b/modules/ROOT/pages/functions/list.adoc @@ -6,7 +6,11 @@ List functions return lists of different data entities. -Further details and examples of lists may be found in xref::values-and-types/lists.adoc[Lists] and xref::syntax/operators.adoc#query-operators-list[List operators]. +For more information about working with `LIST` values, see: + +* xref:values-and-types/lists.adoc[Values and types -> Lists] +* xref:expressions/predicates/list-operators.adoc[Predicates -> List operators] +* xref:expressions/list-expressions.adoc[List expressions] [[example-graph]] == Example graph From 0d384f369d63e535589ddbe615551ea8d5a0e12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 31 Mar 2025 12:35:11 +0200 Subject: [PATCH 06/10] a bit more cleanup --- .../pages/expressions/list-expressions.adoc | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/modules/ROOT/pages/expressions/list-expressions.adoc b/modules/ROOT/pages/expressions/list-expressions.adoc index cd32d824f..a35abbfab 100644 --- a/modules/ROOT/pages/expressions/list-expressions.adoc +++ b/modules/ROOT/pages/expressions/list-expressions.adoc @@ -55,7 +55,7 @@ RETURN [1,2] || [3,4] AS list1, 2+d|Rows: 1 |=== -.Concatenating two `LIST` properties +.Concatenate two `LIST` properties [source, cypher] ---- MATCH (cecil:Person {name: 'Cecil'}), (cecilia:Person {name: 'Cecilia'}) @@ -75,7 +75,7 @@ RETURN cecil.skills || cecilia.skills AS combinedSkills The `+` operator can add elements to the beginning or end of a `LIST` value. This is not possible using the `||` operator. -.Adding elements to the beginning of a `LIST` +.Add elements to the beginning and end of a `LIST` [source, cypher] ---- WITH [1, 2, 3, 4] AS list @@ -94,7 +94,7 @@ RETURN 0 + list AS newBeginning, 2+d|Rows: 1 |=== -.Adding elements to the beginning of a `LIST` property `+` +.Add elements to the beginning of a `LIST` property [source, cypher] ---- MATCH (cecil:Person {name: 'Cecil'}) @@ -114,7 +114,7 @@ RETURN cecil.skills AS skillsList If `NULL` is part of a concatenated `LIST`, `NULL` will be a part of the new `LIST`. -.Concatenating `LIST` including `NULL` +.Concatenate `LIST` including `NULL` [source, cypher] ---- RETURN [1, 2] || [3, null] AS listWithNull @@ -139,7 +139,7 @@ The subscript operator, `[]`, can be used to access specific elements in a `LIST `[0]` refers to the first element in a `LIST`, `[1]` to the second, and so on. `[-1]` refers to the last element in a `LIST`, `[-2]` to the penultimate element, and so on. -.Accessing individual elements in a `LIST` +.Access individual elements in a `LIST` [source, cypher] ---- WITH [1, 2, 3, 4] AS list @@ -168,7 +168,7 @@ The index of the element in the `LIST` can be parameterized. } ---- -.Accessing `LIST` elements with a parameter +.Access `LIST` elements with a parameter [source, cypher] ---- WITH [1, 2, 3, 4] AS list @@ -185,7 +185,7 @@ RETURN list[$myIndex] AS secondElement 1+d|Rows: 1 |=== -.Accessing a `LIST` within a nested `LIST` +.Access a `LIST` within a nested `LIST` [source, cypher] ---- WITH [[1, 2], [3, 4], [5, 6]] AS nestedList @@ -204,7 +204,7 @@ RETURN nestedList[1] AS secondList The xref:expressions/predicates/list-operators.adoc[`IN`] operator, which checks for `LIST` membership, can be used together with `[]` to test whether an element exists in a nested `LIST`. -.Checking for membership in nested `LIST` +.Check for membership in a nested `LIST` [source, cypher] ---- WITH [[1, 2, 3], [4, 5, 6]] AS nestedList @@ -223,7 +223,7 @@ RETURN 3 IN nestedList[0] AS elementPresent Attempting to reference an element outside the bounds of the `LIST` will return `NULL`, as will attempting to access elements from an empty `LIST`. -.Out-of-bounds and empty list access +.Out-of-bounds and empty `LIST` access [source, cypher] ---- WITH [1, 2, 3, 4] AS list, [] AS emptyList @@ -308,7 +308,7 @@ RETURN nestedList[1][0..2] AS slicedInnerList Accessing specific elements or a range of elements can also be used in combination with the `+` operator to create a new `LIST` with values inserted into specific sections of an existing `LIST` value. -.Inserting elements into specific positions of a `LIST` +.Insert elements into specific positions of a `LIST` [source, cypher] ---- WITH [1, 3, 4] AS list @@ -390,7 +390,7 @@ RETURN [n IN list WHERE n > 2 | n] AS filteredList This below query iterates over the `skills` property of each Person node and creates a new `LIST` by xref:expressions/string-operators.adoc[concatenating the `STRING`] `" expert"` to each element in `skills`. -.Modify `LIST` properties +.Modify `LIST` properties using list comprehension [source, cypher] ---- MATCH (p:Person) WHERE p.skills IS NOT NULL @@ -434,10 +434,9 @@ RETURN [person IN collect(p) WHERE 'Python' IN person.skills | person.name] AS p 1+d|Rows: 1 |=== - List comprehension can be used to remove any unknown `NULL` values when concatenating `LIST` values. -.Use a list comprehension to remove `NULL` values during list concatenation +.List comprehension to remove `NULL` values during list concatenation [source, cypher] ---- RETURN [x IN ([1, null, 3] || [null, 5, null]) WHERE x IS NOT NULL] AS listWithoutNull @@ -513,7 +512,7 @@ RETURN [(cecil)-[:WORKS_FOR]->+(superior:Person) | superior.skills] AS superiors ---- Pattern comprehension only supports only the xref:patterns/reference.adoc#variable-length-relationships[variable-length relationships] syntax. -The belowquery uses a pattern comprehension to collect the skills of all superiors in the chain above `Cecil`. +The below query uses a pattern comprehension to collect the skills of all superiors in the chain above `Cecil`. The xref:functions/list.adoc#functions-reduce[`reduce()`] function concatenates these skills into a single `LIST`, and xref:clauses/unwind.adoc[`UNWIND`] is used to flatten this `LIST` before returning the distinct skills in a new `LIST`. .Allowed - pattern comprehension to match patterns of a variable length using variable-length relationship syntax From 0b1d7f1a60e56af8ee96335bfef1f19695a2b438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Mon, 31 Mar 2025 12:44:42 +0200 Subject: [PATCH 07/10] rephrase --- modules/ROOT/pages/expressions/list-expressions.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ROOT/pages/expressions/list-expressions.adoc b/modules/ROOT/pages/expressions/list-expressions.adoc index a35abbfab..c2c33686a 100644 --- a/modules/ROOT/pages/expressions/list-expressions.adoc +++ b/modules/ROOT/pages/expressions/list-expressions.adoc @@ -2,7 +2,7 @@ :description: Information about Cypher's list expressions. :table-caption!: -List expressions are a way to manipulate and query xref:values-and-types/lists.adoc[`LIST`] values in Cypher. +List expressions allow you to manipulate and query xref:values-and-types/lists.adoc[`LIST`] values in Cypher. For more expressions that evaluate to a `LIST` value, see xref:functions/list.adoc[List functions]. + For information about how to check membership in a `LIST` using the `IN` operator, see xref:expressions/predicates/list-operators.adoc[Predicates -> List operators]. From eea7358c4b70afc6c411607d44a324970b957425 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:46:35 +0200 Subject: [PATCH 08/10] post review corrections 1 --- .../pages/expressions/list-expressions.adoc | 291 ++++++++++++------ 1 file changed, 190 insertions(+), 101 deletions(-) diff --git a/modules/ROOT/pages/expressions/list-expressions.adoc b/modules/ROOT/pages/expressions/list-expressions.adoc index c2c33686a..70abbc7ae 100644 --- a/modules/ROOT/pages/expressions/list-expressions.adoc +++ b/modules/ROOT/pages/expressions/list-expressions.adoc @@ -32,105 +32,6 @@ CREATE (alice:Person {name:'Alice', age: 65, role: 'Project manager', skills: [' (daniel)-[:WORKS_FOR]->(eskil) ---- -[[list-concatenation]] -== List concatenation - -Cypher contains two list concatenation operators: `||` and `+`. -`||` is xref:appendix/gql-conformance/index.adoc[GQL conformant], while `+` is not. - -.List concatenation using `||` and `+` -[source, cypher] ----- -RETURN [1,2] || [3,4] AS list1, - [1,2] + [3,4] AS list2 ----- - -.Result -[role="queryresult",options="header,footer",cols="2* 2 | n] AS filteredList 1+d|Rows: 1 |=== -This below query iterates over the `skills` property of each Person node and creates a new `LIST` by xref:expressions/string-operators.adoc[concatenating the `STRING`] `" expert"` to each element in `skills`. +The next example shows how to map a `LIST` using its indexes with a list comprehension. +The xref:functions/list.adoc#functions-range[`range()`] function is used to generate the indexes from `0` to the last valid index of the `LIST`, and then each index is combined with its corresponding `LIST` value into a `STRING` value. +The result is a `LIST` of `STRING` values formatted as `'index: value'`. + +.Map list elements using indexes +[source, cypher] +---- +WITH [1,2,3,4] AS list +RETURN [listIndex IN range(0, size(list)-1) | toString(listIndex) || ': ' || toString(list[listIndex])] AS mappedListElements +---- + +.Result +[role="queryresult",options="header,footer",cols="1* Date: Tue, 1 Apr 2025 10:50:44 +0200 Subject: [PATCH 09/10] colons not hyphen --- modules/ROOT/pages/expressions/list-expressions.adoc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/ROOT/pages/expressions/list-expressions.adoc b/modules/ROOT/pages/expressions/list-expressions.adoc index 70abbc7ae..9fb38692d 100644 --- a/modules/ROOT/pages/expressions/list-expressions.adoc +++ b/modules/ROOT/pages/expressions/list-expressions.adoc @@ -388,9 +388,6 @@ RETURN cecil.skills AS skillsList 1+d|Rows: 1 |=== - - - [[list-comprehension]] == List comprehension @@ -592,7 +589,7 @@ RETURN [(employee:Person)-[:WORKS_FOR]->(alice) WHERE employee.age > 30 | employ Pattern comprehension can also match for xref:patterns/variable-length-patterns.adoc[variable-length patterns]. However, pattern comprehension does not support the xref:patterns/reference.adoc#quantifiers[GQL conformant quantifier syntax]. -.Not allowed - pattern comprehension to match patterns of a variable length using GQL quantifier syntax +.Not allowed: variable-length pattern comprehension using GQL quantifier syntax [source, cypher, role=test-fail] ---- MATCH (cecil:Person {name: 'Cecil'}) @@ -603,7 +600,7 @@ Pattern comprehension only supports only the xref:patterns/reference.adoc#variab The below query uses a pattern comprehension to collect the skills of all superiors in the chain above `Cecil`. The xref:functions/list.adoc#functions-reduce[`reduce()`] function concatenates these skills into a single `LIST`, and xref:clauses/unwind.adoc[`UNWIND`] is used to flatten this `LIST` before returning the distinct skills in a new `LIST`. -.Allowed - pattern comprehension to match patterns of a variable length using variable-length relationship syntax +.Allowed: variable-length pattern comprehension using variable-length relationship syntax [source, cypher] ---- MATCH (cecil:Person {name: 'Cecil'}) From 73b2e804da32af3f03589fa33674701148902a29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jens=20Pryce-=C3=85klundh?= <112686610+JPryce-Aklundh@users.noreply.github.com> Date: Tue, 1 Apr 2025 13:13:48 +0200 Subject: [PATCH 10/10] negative indexing for list slicing --- .../pages/expressions/list-expressions.adoc | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/modules/ROOT/pages/expressions/list-expressions.adoc b/modules/ROOT/pages/expressions/list-expressions.adoc index 9fb38692d..494fa1c9c 100644 --- a/modules/ROOT/pages/expressions/list-expressions.adoc +++ b/modules/ROOT/pages/expressions/list-expressions.adoc @@ -207,6 +207,27 @@ RETURN list[2..4] AS middleElements, 3+d|Rows: 1 |=== +Negative indexing in list slicing references elements from the end of the `LIST`; `..-1` excludes the last element, `..-2` excludes the last two elements, and so on. + +.Negative indexing and list slicing +[source, cypher] +---- +WITH [1, 2, 3, 4, 5, 6] AS list +RETURN list[..-1] AS finalElementRemoved, + list[..-2] AS finalTwoElementsRemoved, + list[-3..-1] AS removedFirstThreeAndLast +---- + +.Result +[role="queryresult",options="header,footer",cols="3*