You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Clear separation between additive and replacing semantics
- Additive semantics for nesting with {}
- Replacing semantics for flat composition
- Use THEN for discard cardinality
- Use WITH|RETURN|YIELD NOTHING for discard fields
Copy file name to clipboardexpand all lines: cip/CIP2016-06-22-nested-updating-and-chained-subqueries.adoc
+111-89
Original file line number
Diff line number
Diff line change
@@ -1,4 +1,4 @@
1
-
= CIP2016-06-22 - Nested subqueries
1
+
= CIP2016-06-22 - Nested, updating, and chained subqueries
2
2
:numbered:
3
3
:toc:
4
4
:toc-placement: macro
@@ -9,7 +9,7 @@
9
9
[abstract]
10
10
.Abstract
11
11
--
12
-
This CIP proposes the incorporation of nested subqueries into Cypher.
12
+
This CIP proposes the incorporation of nested, updating, and chained subqueries into Cypher.
13
13
--
14
14
15
15
toc::[]
@@ -21,162 +21,165 @@ Subqueries - i.e. queries within queries - are a powerful and expressive feature
21
21
22
22
* Increased query expressivity
23
23
* Better query construction and readability
24
-
* Easier query composition and reuse
24
+
* Easier composition of simple query pipelines
25
25
* Post-processing results from multiple queries as a single unit
26
26
* Performing a sequence of multiple write commands for each record
27
27
28
28
== Background
29
29
30
-
This CIP may be viewed in light of the EXISTS CIP, the Scalar Subqueries and List Subqueries CIP, and the Map Projection CIP, all of which propose variants of subqueries.
31
-
In contrast, this CIP focusses on subqueries operating at a clause level while the EXISTS CIP and Map Projection CIP propose subqueries operating at an expression level.
30
+
This CIP may be viewed in light of CIPs for query combinators and set operations, `EXISTS`, scalar subqueries, and list subqueries.
32
31
33
32
== Proposal
34
33
35
-
Nested subqueries are self-contained Cypher queries that are usually run within the scope of an outer Cypher query.
34
+
Subqueries are self-contained Cypher queries that are usually run within the scope of an outer, containing Cypher query.
36
35
37
-
This proposal suggests the introduction of new nested subquery constructs to Cypher.
36
+
This proposal suggests the introduction of new subquery constructs to Cypher.
38
37
39
-
* Read-only nested simple subqueries of the form `{ ... RETURN ... }`
40
-
* Read-only nested chained subqueries of the form `THEN { ... RETURN ... }`
41
-
* Read-only nested optional subqueries of the form `OPTIONAL { ... RETURN ... }`
42
-
* Read-only nested mandatory subqueries of the form `MANDATORY { ... RETURN ... }`
43
-
* Read/Write nested simple updating subqueries of the form `DO { ... }` (inner query not ending with `RETURN`)
44
-
* Read/Write nested conditionally-updating subqueries of the form `DO [WHEN cond THEN { ... }]+ [ELSE { ... }] END` (inner queries not ending with `RETURN`)
38
+
* Read-only nested subqueries
39
+
** Read-only nested regular subqueries of the form `MATCH { <reading-query> }`
40
+
** Read-only nested optional subqueries of the form `OPTIONAL MATCH { <reading-query> }`
41
+
** Read-only nested mandatory subqueries of the form `MANDATORY MATCH { <reading-query> }`
42
+
* Read/Write updating subqueries
43
+
** Read/Write simple updating subqueries of the form `DO { <updating-query> }` (inner query not ending with `RETURN`)
44
+
** Read/Write conditionally-updating subqueries of the form `DO [WHEN <predicate> THEN { <updating-query> }]+ [ELSE { <updating-query> }] END` (inner queries not ending with `RETURN`)
45
+
* Chained subqueries
46
+
** Chained data-dependent subqueries by extending the `WITH` projection clause that have the form `<query> <with-clause> <query>`. Additionally, this CIP proposes new shorthand syntax for starting a query with `WITH` to compose a query with external inputs.
47
+
** Chained data-independent subqueries by introducing the new `THEN` clause for discarding all variables in scope as well as the cardinality of all input records. Additionally, this CIP proposes new shorthand syntax for discarding all variables in scope without discarding the cardinality of input records using `WITH|RETURN|YIELD NOTHING`.
45
48
46
-
A nested simple subquery consists of an inner query in curly braces.
49
+
We additionally propose removing the `FOREACH` clause from the current language (it is rendered obsolete by the introduction of `DO`).
47
50
48
-
All other nested subquery constructs are introduced with a keyword in conjunction with an inner query in curly braces.
51
+
Subquery constructs are always introduced with a keyword(s) in conjunction with an inner query in curly braces.
49
52
50
-
Nested subqueries may be correlated - i.e. the inner query may use variables from the outer query - or uncorrelated.
53
+
Subqueries may be correlated - i.e. the inner query may use variables from the outer query - or uncorrelated.
51
54
52
-
Nested subqueries can be contained within other nested subqueries at an arbitrary (but finite) depth.
55
+
Subqueries can be contained within other subqueries at an arbitrary (but finite) depth.
53
56
54
-
Read/Write nested subqueries cannot be contained within other read-only nested subqueries.
57
+
Read/Write subqueries cannot be contained within other read-only subqueries.
55
58
56
-
Finally, this CIP proposes new shorthand syntax for starting a query with `WHERE`, along with the ability to specify that no fields are to be returned through the introduction of `WITH -`, `RETURN -`, and `YIELD -`.
57
59
60
+
=== Read-only nested subqueries
58
61
59
-
**1. Read-only nested simple subqueries**
62
+
Conceptually, a nested subquery is evaluated for each incoming input record and may produce an arbitrary number of output records.
60
63
61
-
We propose the addition of read-only nested simple subqueries as a new form of read-only Cypher query.
64
+
==== Read-only nested regular subqueries
62
65
63
-
A nested read-only simple subquery is denoted using the following syntax: `{ <inner-query> }`.
66
+
We propose the addition of read-only nested regular subqueries as a new form of read-only Cypher query.
64
67
65
-
The inner query can be any complete read-only Cypher query.
68
+
A nested read-only simple subquery is denoted using the following syntax: `MATCH { <inner-query> }`.
66
69
67
-
A nested read-only simple subquery may only be used as a primary clause, i.e. as a
70
+
The inner query can be any complete read-only Cypher query.
68
71
69
-
* top-level Cypher query,
70
-
* inner query of another nested subquery,
71
-
* inner query of another expression-level subquery (such as a pattern comprehension, or an `EXISTS` subquery),
72
-
* argument query to `UNION` and similar clause-level binary operators
72
+
==== Read-only nested optional subqueries
73
73
74
-
A nested read-only simple subquery may not be used as a secondary clause after a preceding primary clause.
75
-
(However, a nested read-only chained subquery may be used in this case.)
74
+
We propose extending the `OPTIONAL MATCH` clause to express read-only nested optional subqueries.
76
75
76
+
A read-only nested optional subquery is denoted by the following syntax: `OPTIONAL MATCH { <inner-query> }`.
77
77
78
-
**2. Read-only nested chained subqueries**
78
+
==== Read-only nested mandatory subqueries
79
79
80
-
We propose the addition of read-only nested chained subqueries for using nested subqueries in a similar position as a secondary clause.
81
-
This is called _subquery chaining_.
80
+
We propose extending the `MANDATORY MATCH` clause to express read-only nested mandatory subqueries.
82
81
83
-
After a chain of clauses that together form a query, a new nested chained subquery may be introduced as a secondary clause using the `THEN` keyword followed by an inner query in curly braces, i.e. it is denoted using the following syntax: `... THEN { <inner-query> }`.
84
-
`THEN` is a query combinator and more details may be found in the Query Combinator CIP.
82
+
A read-only nested mandatory subquery is denoted by the following syntax: `MANDATORY MATCH { <inner-query> }`.
85
83
84
+
==== Semantics
86
85
87
-
**3. Read-only nested optional subqueries**
86
+
The nested subquery will be provided with all variables visible in the outer query as subquery input.
88
87
89
-
We propose the addition of a new `OPTIONAL` clause for expressing read-only nested optional subqueries.
88
+
All records returned by the final `RETURN` clause of the subquery will be augmented with the variable bindings of the initial input record from the outer query to form the output records of the subquery.
89
+
No other variable bindings will be added to the output records.
90
+
If an incoming variable is either discarded or shadows within the subquery, an error will be raised if the subquery returns that variable to the outer query.
90
91
91
-
A read-only nested optional subquery is denoted by the following syntax: `OPTIONAL { <inner-query> }`.
92
+
Finally, the result records of the different forms of nested subqueries are formed as follows:
92
93
94
+
* The result records of a read-only regular subquery are just the output records.
95
+
* The result records of a read-only optional subquery are all the output records (if there is at least one output record), or a single record with the same fields as the output records where all newly introduced variable bindings are set to `NULL`.
96
+
* The result records of a read-only mandatory subquery are just the output records. However, if the set of output records is empty, an error is raised in the same way as regular `MANDATORY MATCH`.
93
97
94
-
**4. Read-only nested mandatory subqueries**
98
+
Nested subqueries interact with write clauses in the same way as `MATCH` does.
95
99
96
-
We propose the addition of a new `MANDATORY` clause for expressing read-only nested mandatory subqueries.
97
100
98
-
A read-only nested mandatory subquery is denoted by the following syntax: `MANDATORY { <inner-query> }`.
101
+
=== Read/Write updating subqueries
99
102
103
+
Updating subqueries never change the cardinality; i.e. the inner update query is run for each incoming input record.
We propose the addition of a second form of the `DO` clause for expressing read/write nested conditionally-updating subqueries that _do not return any data_.
117
+
We propose the addition of a new conditional `DO` clause for expressing read/write conditionally-updating subqueries that _do not return any data_ from the inner query.
115
118
116
-
A read/write nested conditionally-updating subquery is denoted by the following syntax:
119
+
A read/write conditionally-updating subquery is denoted by the following syntax:
117
120
118
121
```
119
122
DO
120
-
[WHEN <cond> THEN <inner-update-query>]+
123
+
[WHEN <predicate> THEN <inner-update-query>]+
121
124
[ELSE <inner-update-query>]
122
125
END
123
126
```
124
127
125
-
126
128
Evaluation proceeds as follows:
127
129
128
-
* Semantically, the `WHEN` conditions are tested in the order given, and the inner updating query is executed for only the first condition that evaluates to `true`.
129
-
* If no given `WHEN` condition evaluates to `true` and an `ELSE` branch is provided, the inner updating query of the `ELSE` branch is executed.
130
-
* If no given `WHEN` condition evaluates to `true` and no `ELSE` branch is provided, no updates will be executed.
130
+
* Semantically, the `WHEN` predicates are tested in the order given, and the inner updating query is executed for only the first predicate that evaluates to `true`.
131
+
* If no given `WHEN` predicates evaluates to `true` and an `ELSE` branch is provided, the inner updating query of the `ELSE` branch is executed.
132
+
* If no given `WHEN` predicates evaluates to `true` and no `ELSE` branch is provided, no updates will be executed.
131
133
134
+
A query may end with a conditional `DO` subquery in the same way that a query can currently end with any update clause.
132
135
133
-
**6. Shorthand syntax**
134
136
135
-
We propose the addition of a new clause `WHERE <cond> <subclauses>` as a shorthand syntax for `WITH * WHERE <cond> THEN { <subclauses> }`.
136
-
The idea is for this to be used exclusively as a primary clause; for example, as the first clause of a nested subquery.
137
+
=== Chained subqueries
137
138
138
-
We propose the addition of a new projection clauses of the form `WITH -` and `RETURN -`, which will retain the input cardinality but project no result fields.
139
-
This allows for *only* checking the cardinality in a read-only nested mandatory subquery.
139
+
==== Chained data-dependent subqueries
140
140
141
-
We propose the addition of a new subclause to `CALL` of the form `YIELD -`, which will retain the output cardinality of a call but project no result fields.
142
-
This allows for *only* checking the cardinality in an `EXISTS` subquery.
141
+
We propose extending the `WITH` projection clause to sequentially compose arbitrary queries to form a chained data-dependent subquery without resorting to nesting and indentation (e.g. as a short-hand syntax for post-UNION processing).
143
142
143
+
Chained data-dependent subqueries have the following general form `<Q1> WITH ... <Q2>`.
144
144
145
-
=== Semantic clarification
145
+
Both `<Q1`> and `<Q2>` are arbitrary, complete Cypher queries.
146
146
147
-
**1. Read-only nested subqueries**
147
+
Conceptually, the query `<Q2>` is evaluated for each incoming input record from the query `<Q1>` and may produce an arbitrary number of result records.
148
+
In other words, the query `<Q2>` will be provided with all variables returned by the query `<Q1>` as input variable bindings.
148
149
149
-
Conceptually, a nested subquery is evaluated for each incoming record and may produce an arbitrary number of result records.
150
+
Furthermore, this CIP proposes allowing a leading `WITH` to project variables from expressions that refer to unbound variables from the preceding scope (or query).
151
+
This set of referenced, unbound variables of such a leading `WITH` is understood to implicitly declare the input variables required for the query to execute.
150
152
151
-
The rules regarding variable scoping are detailed as follows:
153
+
Note:: This mechanism allows composing a Cypher query with inputs that have been constructed programmatically.
152
154
153
-
* All incoming variables remain in scope throughout the whole subquery.
154
-
* When evaluating the subquery, any new variable bindings introduced by the final `RETURN` clause will augment the variable bindings of the initial record.
155
-
* It is valid (though redundant) if incoming variables from the outer scope are passed on explicitly by any projection clause of the subquery (including the final `RETURN`).
156
-
* Nested subqueries therefore cannot shadow variables present in the outer scope, and thus behave in the same way as `UNWIND` and `CALL` with regard to the introduction of new variable bindings.
157
-
* Any other variable bindings that are introduced temporarily in the subquery will not be visible to the outer scope.
155
+
==== Chained data-independent subqueries
158
156
159
-
Subqueries interact with write clauses in the same way as `MATCH` does.
157
+
We propose introducing the `THEN` projection clause to sequentially compose two arbitrary subqueries to form a chained data-independent subquery without resorting to nesting and indentation.
160
158
159
+
Chained data-independent subqueries have the following general form `<Q1> THEN <Q2>`.
161
160
162
-
**2. Read/Write subqueries**
161
+
Both `<Q1`> and `<Q2>` are arbitrary, complete Cypher queries.
162
+
No variables and no input records are passed from `<Q1>` to `<Q2>`.
163
+
Instead `<Q2>` is executed in a standalone fashion after the execution of `<Q1>` has finished.
163
164
164
-
Execution of a `DO` subquery does not change the cardinality; i.e. the inner update query is run for each incoming record.
165
+
Furthermore, this CIP proposes allowing queries to start with a leading `THEN` for discarding all variables in scope as well as the cardinality of all input records provided by the surrounding execution environment.
165
166
166
-
Any input record is always passed on to the clause succeeding the `DO` subquery, irrespective of whether it was eligible for processing by any inner update query.
167
+
Note:: This mechanism allows guaranteed execution of `<Q2>` irrespective of the number of records produced by `<Q1>`.
167
168
168
-
A `DO` clause that uses `WHEN` sub-clause is called a _conditional DO_.
169
+
Note:: In general, `<Q1>` is expected to be an updating query and it is recommended that implementations generate a warning if this is not the case (to inform the user that `<Q1>` is essentially superfluous).
169
170
170
-
A query may end with a `DO` subquery in the same way that a query can currently end with any update clause.
171
+
==== Discarding variables in scope
172
+
173
+
Finally, this CIP proposes new shorthand syntax for discarding all variables in scope without discarding the cardinality of input records using `WITH|RETURN|YIELD NOTHING`.
171
174
172
175
=== Examples
173
176
174
-
**1. Read-only nested simple and chained subqueries**
177
+
==== Read-only nested regular subqueries
175
178
176
179
Post-UNION processing:
177
180
[source, cypher]
178
181
----
179
-
{
182
+
MATCH {
180
183
// authored tweets
181
184
MATCH (me:User {name: 'Alice'})-[:FOLLOWS]->(user:User),
182
185
(user)<-[:AUTHORED]-(tweet:Tweet)
@@ -197,7 +200,7 @@ Uncorrelated nested subquery:
197
200
[source, cypher]
198
201
----
199
202
MATCH (f:Farm {id: $farmId})
200
-
THEN {
203
+
MATCH {
201
204
MATCH (u:User {id: $userId})-[:LIKES]->(b:Brand),
202
205
(b)-[:PRODUCES]->(p:Lawnmower)
203
206
RETURN b.name AS name, p.code AS code
@@ -214,7 +217,7 @@ Correlated nested subquery:
214
217
[source, cypher]
215
218
----
216
219
MATCH (f:Farm {id: $farmId})-[:IS_IN]->(country:Country)
217
-
THEN {
220
+
MATCH {
218
221
MATCH (u:User {id: $userId})-[:LIKES]->(b:Brand),
219
222
(b)-[:PRODUCES]->(p:Lawnmower)
220
223
RETURN b.name AS name, p.code AS code
@@ -233,7 +236,7 @@ Filtered and correlated nested subquery:
233
236
----
234
237
MATCH (f:Farm)-[:IS_IN]->(country:Country)
235
238
WHERE country.name IN $countryNames
236
-
THEN {
239
+
MATCH {
237
240
MATCH (u:User {id: $userId})-[:LIKES]->(b:Brand),
238
241
(b)-[:PRODUCES]->(p:Lawnmower)
239
242
RETURN b AS brand, p.code AS code
@@ -253,9 +256,9 @@ Doubly-nested subquery:
253
256
[source, cypher]
254
257
----
255
258
MATCH (f:Farm {id: $farmId})
256
-
THEN {
259
+
MATCH {
257
260
MATCH (c:Customer)-[:BUYS_FOOD_AT]->(f)
258
-
THEN {
261
+
MATCH {
259
262
MATCH (c)-[:RETWEETS]->(t:Tweet)<-[:TWEETED_BY]-(f)
260
263
RETURN c, count(*) AS count
261
264
UNION
@@ -271,23 +274,23 @@ THEN {
271
274
RETURN f.name AS name, type, sum(endorsement) AS endorsement
272
275
----
273
276
274
-
**2. Read-only nested optional match and mandatory subqueries**
277
+
===== Read-only nested optional and mandatory subqueries
275
278
276
279
This proposal also provides nested subquery forms of `OPTIONAL MATCH` and `MANDATORY MATCH`:
277
280
278
281
[source, cypher]
279
282
----
280
283
MANDATORY MATCH (p:Person {name: 'Petra'})
281
284
MANDATORY MATCH (conf:Conference {name: $conf})
282
-
MANDATORY {
283
-
WHERE conf.impact > 5
285
+
MANDATORY MATCH {
286
+
WITH * WHERE conf.impact > 5
284
287
MATCH (p)-[:ATTENDS]->(conf)
285
288
RETURN conf
286
289
UNION
287
290
MATCH (p)-[:LIVES_IN]->(:City)<-[:IN]-(conf)
288
291
RETURN conf
289
292
}
290
-
OPTIONAL {
293
+
OPTIONAL MATCH {
291
294
MATCH (p)-[:KNOWS]->(a:Attendee)-[:PUBLISHED_AT]->(conf)
292
295
RETURN a.name AS name
293
296
UNION
@@ -298,7 +301,7 @@ RETURN name
298
301
----
299
302
300
303
301
-
**3. Read/Write nested simple and conditionally-updating subqueries**
304
+
==== Read/Write simple updating and conditionally-updating subqueries
302
305
303
306
We illustrate these by means of an 'old' version of the query, in which `FOREACH` is used, followed by the 'new' version, using `DO`.
304
307
@@ -376,12 +379,31 @@ DO WHEN x % 2 = 1 THEN {
376
379
END
377
380
----
378
381
382
+
==== Chained subqueries
383
+
384
+
Combining nested and chained subqueries
385
+
[source, cypher]
386
+
----
387
+
MATCH (x)-[:IN]->(:Category {name: "A"})
388
+
WITH x LIMIT 5
389
+
MATCH (x)-[:FROM]-(c :City)
390
+
RETURN x, c
391
+
UNION
392
+
MATCH (x)-[:IN]->(:Category {name: "A"})
393
+
WITH x LIMIT 10
394
+
MATCH (x)-[:FROM]-(c :City)
395
+
// This finished the right arm of the UNION
396
+
RETURN x, c
397
+
// This applies to the whole UNION
398
+
WITH x.name AS name ORDER BY x.age
399
+
RETURN x LIMIT 10
400
+
----
379
401
380
402
=== Interaction with existing features
381
403
382
404
Apart from the suggested deprecation of the `FOREACH` clause, nested read-only, write-only and read-write subqueries do not interact directly with any existing features.
383
405
384
-
=== Alternatives
406
+
== Alternatives
385
407
386
408
Alternative syntax has been considered during the production of this document:
0 commit comments