Skip to content

Commit bab0540

Browse files
committed
further massaging of text
1 parent abb28db commit bab0540

File tree

1 file changed

+116
-69
lines changed

1 file changed

+116
-69
lines changed

modules/ROOT/pages/queries/composed-queries/sequential-queries.adoc

Lines changed: 116 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99

1010
* `NEXT` can improve the modularity and readability of complex queries.
1111
* `NEXT` can be used instead of xref:clauses/with.adoc[] clause to construct complex queries.
12-
* `NEXT` can improve the usability of xref:queries/composed-queries/conditional-queries.adoc[conditional `WHEN`].
13-
* `NEXT` allows for passing the full table of intermediate results into the arms of xref:queries/composed-queries/combined-queries.adoc[combined `UNION`] queries.
12+
* `NEXT` can improve the usability of xref:queries/composed-queries/conditional-queries.adoc[].
13+
* `NEXT` allows for passing the full table of intermediate results into the arms of xref:queries/composed-queries/combined-queries.adoc[].
1414
1515
[[example-graph]]
1616
== Example graph
@@ -82,9 +82,7 @@ NEXT
8282
[[passing-values]]
8383
== Passing values to subsequent queries
8484

85-
`NEXT` passes the full whole table of intermediate results to the subsequent query.
86-
This is referred to by-table semantics, which is different from `CALL` subqueries which passes values using by-row semantics e.g., one row at the time.
87-
By-table semantics are particularly useful when aggregating values.
85+
`NEXT` passes the result table of a query to the subsequent query.
8886
In the following example, `NEXT` passes the variable `customer` to the second query:
8987

9088
.Passing a variable to another query via `NEXT`
@@ -112,7 +110,6 @@ RETURN customer.firstName AS chocolateCustomer
112110
|===
113111

114112
.Passing multiple variables to another query via `NEXT`
115-
// tag::sequential_queries_basic_example[]
116113
[source,cypher]
117114
----
118115
MATCH (c:Customer)-[:BUYS]->(p:Product {name: 'Chocolate'})
@@ -123,7 +120,6 @@ NEXT
123120
RETURN customer.firstName AS chocolateCustomer,
124121
product.price * (1 - customer.discount) AS chocolatePrice
125122
----
126-
// end::sequential_queries_basic_example[]
127123

128124
.Result
129125
[role="queryresult",options="header,footer",cols="2*<m"]
@@ -144,66 +140,66 @@ Literals or unaliased expressions are not allowed.
144140
For example, `RETURN 1` and `RETURN 1 + 1` cannot precede `NEXT`, but `RETURN 1 AS one` and `RETURN 1 + 1 AS two` can.
145141
====
146142

147-
Variables which are local to a query and which are not explicitly returned are not accessible by subsequent queries in the context of `NEXT`.
143+
Variables which are local to the query preceding `NEXT` and which are not explicitly returned as part of the result of that query are not accessible by subsequent queries in the context of `NEXT`.
148144
This allows you to control variable scope similarly to what you can do with `WITH`, see xref:clauses/with.adoc#variable-scope[Control variables in scope].
149145

150-
[[next-and-union]]
151-
== Interactions with `UNION` queries
146+
[[aggregation-after-next]]
147+
== Aggregation after `NEXT`
148+
149+
`NEXT` passes the result table as a whole to the subsequent query.
150+
This particularly useful when aggregating values.
151+
152+
In the following example, `NEXT` passes the variable `customer` to the second query:
152153

153-
.`NEXT` in a query using `UNION`
154+
.Aggregation after `NEXT`
154155
[source,cypher]
155156
----
156-
MATCH (c:Customer)-[:BUYS]->(:Product{name: "Laptop"})
157-
RETURN c.firstName AS customer
158-
UNION ALL
159-
MATCH (c:Customer)-[:BUYS]-> (:Product{name: "Coffee"})
160-
RETURN c.firstName AS customer
157+
MATCH (c:Customer)-[:BUYS]->(p:Product)
158+
RETURN c AS customer, p AS product
161159
162160
NEXT
163161
164-
RETURN customer AS customer, count(customer) as numberOfProducts
162+
RETURN product.name AS product,
163+
COUNT(customer) AS numberOfCustomers
165164
----
166165

167166
.Result
168167
[role="queryresult",options="header,footer",cols="2*<m"]
169168
|===
170-
| customer | numberOfProducts
171-
172-
| "Amir" | 1
173-
| "Mateo" | 2
174-
| "Leila" | 1
175-
| "Yusuf" | 1
176-
| "Hannah" | 1
177-
| "Niko" | 1
169+
| product | numberOfCustomers
178170

171+
| "Laptop" | 4
172+
| "Chocolate" | 3
173+
| "Headphones"| 3
174+
| "Coffee" | 3
175+
| "Phone" | 1
179176

180-
2+d|Rows: 6
177+
2+d|Rows: 5
181178
|===
182179

183-
In this example, the list of customer names from the first segment has a duplicate entry for "Mateo" who bought both a laptop and coffee.
184-
The use of `UNION ALL` added him to the list twice.
185-
The second segment can access the list, because both parts of the `UNION` return a part of the list, aliased as `customer`.
186-
By using xref:functions/aggregating.adoc#functions-count[`count()`], the list aggregates the duplicate in the `RETURN` part of the query.
180+
[[next-and-union]]
181+
== Interactions with `UNION` queries
187182

188-
=== Leveraging by-table semantics using `UNION` after `NEXT`
183+
[[union-after-next]]
184+
=== Using `UNION` after `NEXT`
189185

190186
When a `UNION` query follows a `NEXT` the full table of intermediate results are passed into all arms of the `UNION` query.
191187

192-
.By-table semantics using `NEXT`
188+
.`UNION` after `NEXT`
193189
[source,cypher]
194190
----
195-
MATCH (c:Customer)-[:BUYS]->(p:Product)
196-
RETURN c, p
191+
MATCH (c:Customer)-[:BUYS]->(p:Product)
192+
RETURN c, p
197193
198-
NEXT
194+
NEXT
199195
200-
RETURN c.firstName AS name, COLLECT(p.price * c.discount) AS purchases, "discounted price" AS type
201-
UNION
202-
RETURN c.firstName AS name, COLLECT(p.price) AS purchases, "real price" AS type
196+
RETURN c.firstName AS name, COLLECT(p.price * c.discount) AS purchases, "discounted price" AS type
197+
UNION
198+
RETURN c.firstName AS name, COLLECT(p.price) AS purchases, "real price" AS type
203199
204-
NEXT
200+
NEXT
205201
206-
RETURN * ORDER BY name, type
202+
RETURN * ORDER BY name, type
207203
----
208204

209205
.Result
@@ -229,6 +225,47 @@ When a `UNION` query follows a `NEXT` the full table of intermediate results are
229225
3+d|Rows: 14
230226
|===
231227

228+
[[union-before-next]]
229+
=== Using `UNION` before `NEXT`
230+
231+
When a `UNION` query precedes a `NEXT` the full result of the `UNION` is passed into the subsequent query.
232+
233+
.`UNION` before `NEXT`
234+
[source,cypher]
235+
----
236+
MATCH (c:Customer)-[:BUYS]->(:Product{name: "Laptop"})
237+
RETURN c.firstName AS customer
238+
UNION ALL
239+
MATCH (c:Customer)-[:BUYS]-> (:Product{name: "Coffee"})
240+
RETURN c.firstName AS customer
241+
242+
NEXT
243+
244+
RETURN customer AS customer, count(customer) as numberOfProducts
245+
----
246+
247+
.Result
248+
[role="queryresult",options="header,footer",cols="2*<m"]
249+
|===
250+
| customer | numberOfProducts
251+
252+
| "Amir" | 1
253+
| "Mateo" | 2
254+
| "Leila" | 1
255+
| "Yusuf" | 1
256+
| "Hannah" | 1
257+
| "Niko" | 1
258+
259+
260+
2+d|Rows: 6
261+
|===
262+
263+
In this example, the list of customer names from the first segment has a duplicate entry for "Mateo" who bought both a laptop and coffee.
264+
The use of `UNION ALL` added him to the list twice.
265+
The second segment can access the list, because both parts of the `UNION` return a part of the list, aliased as `customer`.
266+
By using xref:functions/aggregating.adoc#functions-count[`count()`], the list aggregates the duplicate in the `RETURN` part of the query.
267+
268+
[[next-inside-union]]
232269
=== `NEXT` inside a `UNION` using `{}`
233270

234271
If a `UNION` query has a `NEXT` in any of its blocks, it is necessary to wrap that block with `{}`.
@@ -275,57 +312,69 @@ RETURN customer.firstName AS plantCustomer
275312
[[next-and-call]]
276313
== Interactions with `CALL` subqueries
277314

278-
When `NEXT` is wrapped within a `CALL` subquery it is possible to go from by-row semantics to by-table semantics.
315+
`CALL` subqueries pass the table of intermediate rules to the subquery row-by-row, while `NEXT` passes the table as a whole.
316+
When `NEXT` is wrapped within a `CALL` subquery, the first query gets passed only a single row at a time.
317+
This can be used to compute more complex aggregates in groups.
279318

280-
[.tabbed-example]
281-
[.include-with-CALL-subquery]
319+
.`NEXT` inside `CALL`
282320
[source,cypher]
283321
----
284-
MATCH (p:Product)
322+
MATCH (p:Product) WHERE p.name <> "Coffee"
285323
CALL (p) {
286-
MATCH (c:Customer)-[:BUYS]->(p)
324+
MATCH (p)<-[:BUYS]-(c:Customer)-[:BUYS]->(otherProduct)
325+
RETURN c, otherProduct
287326
288327
NEXT
289328
290-
RETURN collect(c.firstName) AS customers
329+
RETURN count(DISTINCT c) AS customers, 0 AS customersAlsoBuyingCoffee
291330
UNION
292-
RETURN collect(c.lastName) AS customers
331+
FILTER otherProduct.name = "Coffee"
332+
RETURN 0 as customers, count(DISTINCT c) AS customersAlsoBuyingCoffee
333+
334+
NEXT
335+
336+
RETURN max(customers) AS customers, max(customersAlsoBuyingCoffee) AS customersAlsoBuyingCoffee
293337
}
294-
RETURN p.name as product, customers
338+
RETURN p.name AS product,
339+
round(toFloat(customersAlsoBuyingCoffee) * 100 / customers, 1) AS percentageOfCustomersAlsoBuyingCoffee
340+
ORDER BY product
295341
----
296342

297-
[.include-with-NEXT]
298-
299343
.Result
300344
[role="queryresult",options="header,footer",cols="2*<m"]
301345
|===
302-
| product | customers
303-
304-
| "Laptop" | ["Amir","Mateo","Leila","Yusuf"]
305-
| "Phone" | ["Niko"]
306-
| "Headphones" | ["Keisha", "Hannah", "Niko"]
307-
| "Chocolate" | ["Amir", "Mateo", "Yusuf"]
308-
| "Coffee" | ["Mateo", "Hannah", "Niko"]
309-
| "Laptop" | ["Rahman","Ortega","Haddad","Abdi"]
310-
| "Phone" | ["Petrov"]
311-
| "Headphones" | ["Nguyen","Connor","Petrov"]
312-
| "Chocolate" | ["Rahman","Ortega","Abdi"]
313-
| "Coffee" | ["Ortega","Connor","Petrov"]
346+
| product | percentageOfCustomersAlsoBuyingCoffee
347+
348+
| "Chocolate" | 33.3
349+
| "Headphones" | 100.0
350+
| "Laptop" | 33.3
351+
| "Phone" | 100.0
314352
2+d|Rows: 5
315353
|===
316354

355+
In this example, we compute for each non-coffee product the percentage of customers that also bought coffee.
356+
So for each product `p`, the subquery find all pairs of a customer `c` of product `p` and another product `otherProduct` that customer has also bought.
357+
The first `NEXT` passes these pairs as a whole into a `UNION`, so that the query can
358+
(1) count all customers in the first arm of the union and
359+
(2) count the customers who also bought coffee in the second arm of the union.
360+
The `UNION` produce two rows -- one from each arm.
361+
The second `NEXT` passes these two rows as a whole into a query the aggregates them into a single row, which is the result of the `CALL` subquery.
362+
317363
[NOTE]
318364
====
319365
`NEXT` cannot be used inside a `CALL` subquery that uses the (deprecated) xref:subqueries/call-subquery.adoc#importing-with[importing `WITH`] syntax.
320366
====
321367

322368
[[next-and-conditional-queries]]
323369
== Interactions with conditional queries
324-
Conditional queries act similar to `CALL` by processing incoming rows using by-row semantics.
370+
371+
[[conditional-queries-inside-next]]
372+
=== Using conditional query before or after `NEXT`
373+
374+
Conditional queries act similar to `CALL` by processing the incoming table of intermediate result row-by-row.
325375
A conditional query following a `NEXT` acts equivalent to a conditional query wrapped in a `CALL` subquery.
326376

327-
.Conditional queries in `NEXT`
328-
// tag::sequential_queries_chaining_conditional_queries[]
377+
.Conditional query inside `NEXT`
329378
[source,cypher]
330379
----
331380
MATCH (c:Customer)-[:BUYS]->(:Product)<-[:SUPPLIES]-(s:Supplier)
@@ -372,13 +421,12 @@ The second segment is a conditional query that returns different base personalit
372421
The third segment aggregates the personality types.
373422
Finally, the fourth segment is another conditional query which subsumes multiple base personality types, if present, to a new personality.
374423

375-
[[next-conditional-queries-top-level-braces]]
424+
[[next-inside-conditional-queries]]
376425
=== `NEXT` inside a conditional query using `{}`
377426

378427
If a conditional query has a `NEXT` in any of its `THEN` or `ELSE` blocks, it is necessary to wrap the part after `THEN` or `ELSE` with `{}`.
379428

380-
.`NEXT` inside a conditional query
381-
// tag::sequential_queries_in_conditional_queries[]
429+
.`NEXT` inside conditional query
382430
[source,cypher]
383431
----
384432
MATCH (c:Customer)-[:BUYS]->(p:Product)
@@ -397,7 +445,6 @@ ELSE {
397445
RETURN customer.firstName AS customer, "club below 1000" AS customerType, finalSum AS sum
398446
}
399447
----
400-
// end::sequential_queries_in_conditional_queries[]
401448

402449
.Result
403450
[role="queryresult",options="header,footer",cols="3*<m"]

0 commit comments

Comments
 (0)