Skip to content

Commit 1bd66bb

Browse files
JPryce-Aklundhgem-neo4j
authored andcommitted
Clarify CALL subquery ambiguities (neo4j#1060)
1 parent 01eb3cd commit 1bd66bb

File tree

2 files changed

+50
-62
lines changed

2 files changed

+50
-62
lines changed

modules/ROOT/images/call_subquery_graph.svg

Lines changed: 5 additions & 5 deletions
Loading

modules/ROOT/pages/subqueries/call-subquery.adoc

Lines changed: 45 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,9 @@ CREATE (teamA:Team {name: 'Team A'}),
3030
(playerF:Player {name: 'Player F', age: 35}),
3131
(playerA)-[:PLAYS_FOR]->(teamA),
3232
(playerB)-[:PLAYS_FOR]->(teamA),
33-
(playerC)-[:PLAYS_FOR]->(teamA),
3433
(playerD)-[:PLAYS_FOR]->(teamB),
3534
(playerE)-[:PLAYS_FOR]->(teamC),
3635
(playerF)-[:PLAYS_FOR]->(teamC),
37-
(playerA)-[:FRIEND_OF]->(playerB),
38-
(playerA)-[:FRIEND_OF]->(playerC),
39-
(playerB)-[:FRIEND_OF]->(playerF),
40-
(playerC)-[:FRIEND_OF]->(playerD),
4136
(teamA)-[:OWES {dollars: 1500}]->(teamB),
4237
(teamA)-[:OWES {dollars: 3000}]->(teamB),
4338
(teamB)-[:OWES {dollars: 1700}]->(teamC),
@@ -92,7 +87,6 @@ CALL () {
9287
SET p.age = p.age + 1
9388
RETURN p.age AS newAge
9489
}
95-
WITH x, newAge
9690
MATCH (p:Player {name: 'Player A'})
9791
RETURN x AS iteration, newAge, p.age AS totalAge
9892
----
@@ -139,7 +133,7 @@ RETURN t AS team, players
139133
| players
140134
141135
| (:Team {name: "Team A"})
142-
| [(:Player {name: "Player C", age: 19}), (:Player {name: "Player B", age: 23}), (:Player {name: "Player A", age: 24})]
136+
| (:Player {name: "Player B", age: 23}), (:Player {name: "Player A", age: 24})]
143137
144138
| (:Team {name: "Team B"})
145139
| [(:Player {name: "Player D", age: 30})]
@@ -432,60 +426,60 @@ Similar to xref:clauses/optional-match.adoc[`OPTIONAL MATCH`] any empty rows pro
432426
.Difference between using `CALL` and `OPTIONAL CALL`
433427
====
434428
435-
This example, which finds the friends of each `Player` and xref:functions/aggregating.adoc#functions-count[counts] the number of friends per player, highlights the difference between using `CALL` and `OPTIONAL CALL`.
429+
This example, which finds the team that each `Player` plays for, highlights the difference between using `CALL` and `OPTIONAL CALL`.
436430
437431
.Regular subquery `CALL`
438432
[source, cypher]
439433
----
440434
MATCH (p:Player)
441435
CALL (p) {
442-
MATCH (p)-[:FRIEND_OF]->(friend:Player)
443-
RETURN friend
436+
MATCH (p)-[:PLAYS_FOR]->(team:Team)
437+
RETURN team
444438
}
445-
RETURN p.name AS playerName, count(friend) AS numberOfFriends
446-
ORDER BY numberOfFriends
439+
RETURN p.name AS playerName, team.name AS team
447440
----
448441
449-
.Optional subquery `CALL`
442+
.Result
450443
[role="queryresult",options="header,footer",cols="2*m"]
451444
|===
452-
| playerName | numberOfFriends
445+
| playerName | team
453446
454-
| "Player B" | 1
455-
| "Player C" | 1
456-
| "Player A" | 2
447+
| "Player A" | "Team A"
448+
| "Player B" | "Team A"
449+
| "Player D" | "Team B"
450+
| "Player E" | "Team C"
451+
| "Player F" | "Team C"
457452
458-
2+d|Rows: 3
453+
2+d|Rows: 5
459454
|===
460455
461-
Note that no results are returned for `Player D`, `Player E`, and `Player F`, since they have no outgoing `FRIEND_OF` relationships connected to them.
456+
Note that no results are returned for `Player C`, since they are not connected to any `Team` with a `PLAYS_FOR` relationship.
462457
463458
.Query using regular `OPTIONAL CALL`
464459
[source, cypher]
465460
----
466461
MATCH (p:Player)
467462
OPTIONAL CALL (p) {
468-
MATCH (p)-[:FRIEND_OF]->(friend:Player)
469-
RETURN friend
463+
MATCH (p)-[:PLAYS_FOR]->(team:Team)
464+
RETURN team
470465
}
471-
RETURN p.name AS playerName, count(friend) AS numberOfFriends
472-
ORDER BY numberOfFriends
466+
RETURN p.name AS playerName, team.name AS team
473467
----
474468
475-
Now, all `Player` nodes, regardless of whether they have any friends or not, are returned.
476-
(Those without any outgoing `FRIEND_OF` relationships are returned with the result `0` because `count()` ignores `null` values.)
469+
Now all `Player` nodes, regardless of whether they have any `PLAYS_FOR` relationships connected to a `Team`, are returned.
477470
471+
.Result
478472
.Result
479473
[role="queryresult",options="header,footer",cols="2*m"]
480474
|===
481-
| playerName | numberOfFriends
475+
| playerName | team
482476
483-
| "Player D" | 0
484-
| "Player E" | 0
485-
| "Player F" | 0
486-
| "Player B" | 1
487-
| "Player C" | 1
488-
| "Player A" | 2
477+
| "Player A" | "Team A"
478+
| "Player B" | "Team A"
479+
| "Player C" | NULL
480+
| "Player D" | "Team B"
481+
| "Player E" | "Team C"
482+
| "Player F" | "Team C"
489483
490484
2+d|Rows: 6
491485
|===
@@ -495,8 +489,8 @@ Now, all `Player` nodes, regardless of whether they have any friends or not, are
495489
[[call-execution-order]]
496490
== Execution order of CALL subqueries
497491

498-
The order in which subqueries are executed is not defined.
499-
If a query result depends on the order of execution of subqueries, an `ORDER BY` clause should precede the `CALL` clause.
492+
The order in which rows from the outer scope are passed into subqueries is not defined.
493+
If the results of the subquery depend on the order of these rows, use an `ORDER BY` clause before the `CALL` clause to guarantee a specific processing order for the rows.
500494

501495
.Ordering results before `CALL` subquery
502496
====
@@ -590,13 +584,13 @@ UNION
590584
ORDER BY p.age DESC
591585
LIMIT 1
592586
}
593-
RETURN p.name AS name, p.age AS age
587+
RETURN p.name AS playerName, p.age AS age
594588
----
595589
596590
.Result
597591
[role="queryresult",options="header,footer",cols="2*<m"]
598592
|===
599-
| name | age
593+
| playerName | age
600594
| "Player C" | 19
601595
| "Player F" | 35
602596
2+d|Rows: 2
@@ -641,39 +635,33 @@ The result of the `CALL` subquery is the combined result of evaluating the subqu
641635

642636
.`CALL` subquery changing returned rows of outer query
643637
====
644-
The following example finds the name of each `Player` and the names of their friends.
645-
No rows are returned for the `Player` nodes without any `FRIEND_OF` relationships, the number of results of the subquery thus changed the number of results of the enclosing query.
638+
The following example finds the name of each `Player` and the team they play for.
639+
No rows are returned for `Player C`, since they are not connected to a `Team` with a `PLAYS_FOR` relationship.
640+
The number of results of the subquery thus changed the number of results of the enclosing query.
646641
647642
.Find the friends of players
648643
[source, cypher]
649644
----
650645
MATCH (p:Player)
651646
CALL (p) {
652-
MATCH (p)-[:FRIEND_OF]->(p2:Player)
653-
RETURN p2.name AS friend
647+
MATCH (p)-[:PLAYS_FOR]->(team:Team)
648+
RETURN team.name AS team
654649
}
655-
RETURN p.name AS player, friend
650+
RETURN p.name AS playerName, team
656651
----
657652
658653
.Result
659-
[role="queryresult",options="header,footer",cols="2*<m"]
654+
[role="queryresult",options="header,footer",cols="2*m"]
660655
|===
661-
| player
662-
| friend
663-
664-
| "Player A"
665-
| "Player B"
666-
667-
| "Player A"
668-
| "Player C"
656+
| playerName | team
669657
670-
| "Player B"
671-
| "Player F"
672-
673-
| "Player C"
674-
| "Player D"
658+
| "Player A" | "Team A"
659+
| "Player B" | "Team A"
660+
| "Player D" | "Team B"
661+
| "Player E" | "Team C"
662+
| "Player F" | "Team C"
675663
676-
2+d|Rows: 4
664+
2+d|Rows: 5
677665
|===
678666
679667
====
@@ -771,7 +759,7 @@ Labels added: 18
771759

772760
* `CALL` subqueries optimize data handling and query efficiency, and can perform changes to the database.
773761

774-
* `CALL` subqueries enable progressive data transformation and can accumulate results across multiple row executions.
762+
* `CALL` subqueries allow for row-by-row data transformation and enable the accumulation of results across multiple rows, facilitating complex operations that depend on intermediate or aggregated data.
775763

776764
* `CALL` subqueries can only refer to variables from the enclosing query if they are explicitly imported by either a variable scope clause or an importing `WITH` clause (deprecated).
777765

0 commit comments

Comments
 (0)