CALL subqueries
The CALL
clause can be used to invoke a subquery.
Unlike other subqueries in Cypher®, it can be used to perform changes to the database (e.g. CREATE new nodes), and it requires an importing WITH clause.
The |
Example graph
The following graph is used for the examples below:
To recreate the graph, run the following query in an empty Neo4j database:
CREATE
(a:Person:Child {name: 'Alice', age: 20}),
(b:Person {name: 'Bob', age: 27}),
(c:Person:Parent {name: 'Charlie', age: 65}),
(d:Person {name: 'Dora', age: 30})
CREATE (a)-[:FRIEND_OF]->(b)
CREATE (a)-[:CHILD_OF]->(c)
CREATE (a)-[:OWES {dollars: 20}]->(c)
CREATE (a)-[:OWES {dollars: 25}]->(b)
CREATE (b)-[:OWES {dollars: 35}]->(d)
CREATE (d)-[:OWES {dollars: 15}]->(b)
CREATE (d)-[:OWES {dollars: 30}]->(b)
CREATE (:Counter {count: 0})
Semantics
A CALL
subquery is executed once for each incoming row.
In the below example, the CALL
subquery executes three times, one for each row that the UNWIND
clause outputs.
UNWIND [0, 1, 2] AS x
CALL {
RETURN 'hello' AS innerReturn
}
RETURN innerReturn
innerReturn |
---|
|
|
|
Rows:3 |
Each execution of a CALL
subquery can observe changes from previous executions.
UNWIND [0, 1, 2] AS x
CALL {
MATCH (n:Counter)
SET n.count = n.count + 1
RETURN n.count AS innerCount
}
WITH innerCount
MATCH (n:Counter)
RETURN
innerCount,
n.count AS totalCount
innerCount | totalCount |
---|---|
1 |
3 |
2 |
3 |
3 |
3 |
Rows:3 |
Importing variables into subqueries
Variables are imported into a subquery using an importing WITH
clause.
References to a variable in the outer scope that were not imported will introduce a new variable. |
As the subquery is evaluated for each incoming input row, the imported variables get bound to the corresponding values from the input row in each evaluation.
UNWIND [0, 1, 2] AS x
CALL {
WITH x
RETURN x * 10 AS y
}
RETURN x, y
x | y |
---|---|
|
|
|
|
|
|
Rows: 3 |
An importing WITH
clause must:
-
Consist only of simple references to outside variables - e.g.
WITH x, y, z
. Aliasing or expressions are not supported in importingWITH
clauses - e.g.WITH a AS b
orWITH a+1 AS b
. -
Be the first clause of a subquery (or the second clause, if directly following a
USE
clause).
Execution order of CALL subqueries
The order in which subqueries are executed is not defined.
If a query result depends on the order of execution of subqueries, an ORDER BY
clause should precede the CALL
clause.
This query creates a linked list of all Person
nodes in order of ascending age.
The CALL
clause is relying on the incoming row ordering to ensure that a correctly ordered linked list is created, thus the incoming rows must be ordered with a preceding ORDER BY
clause.
MATCH (person:Person)
WITH person ORDER BY person.age ASC LIMIT 1
SET person:ListHead
WITH *
MATCH (nextPerson: Person&!ListHead)
WITH nextPerson ORDER BY nextPerson.age
CALL {
WITH nextPerson
MATCH (current:ListHead)
REMOVE current:ListHead
SET nextPerson:ListHead
CREATE(current)-[:IS_YOUNGER_THAN]->(nextPerson)
RETURN current AS from, nextPerson AS to
}
RETURN
from.name AS name,
from.age AS age,
to.name AS closestOlderName,
to.age AS closestOlderAge
name | age | closestOlderName | closestOlderAge |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Rows: 3 |
Post-union processing
Call subqueries can be used to further process the results of a UNION query. This example query finds the youngest and the oldest person in the database and orders them by name.
CALL {
MATCH (p:Person)
RETURN p
ORDER BY p.age ASC
LIMIT 1
UNION
MATCH (p:Person)
RETURN p
ORDER BY p.age DESC
LIMIT 1
}
RETURN p.name, p.age
ORDER BY p.name
p.name | p.age |
---|---|
|
|
|
|
Rows: 2 |
If different parts of a result should be matched differently, with some aggregation over the whole result, subqueries need to be used.
The example below query uses a CALL
subquery in combination with UNION ALL
to determine how much each Person
node in the graph owes or is owed.
MATCH (p:Person)
CALL {
WITH p
OPTIONAL MATCH (p)-[o:OWES]->(other:Person)
RETURN o.dollars * -1 AS moneyOwed
UNION ALL
WITH p
OPTIONAL MATCH (other:Person)-[o:OWES]->(p)
RETURN o.dollars AS moneyOwed
}
RETURN p.name, sum(moneyOwed) AS amountOwing
p.name | amountOwing |
---|---|
|
|
|
|
|
|
|
|
Rows: 4 |
Aggregations
Returning subqueries change the number of results of the query.
The result of the CALL
subquery is the combined result of evaluating the subquery for each input row.
The following example finds the name of each person and the names of their friends:
MATCH (p:Person)
CALL {
WITH p
MATCH (p)-[:FRIEND_OF]-(c:Person)
RETURN c.name AS friend
}
RETURN p.name, friend
p.name | friend |
---|---|
|
|
|
|
Rows: 2 |
The number of results of the subquery changed the number of results of the enclosing query. Instead of 4 rows, there are now 2 rows which were found for Alice and Bob respectively. No rows are returned for Charlie and Dora since they have no friends in our example graph.
Subqueries can also perform isolated aggregations.
The below example uses the sum() function to count how much money is owed between the Person
nodes in the graph.
MATCH (p:Person)
CALL {
WITH p
MATCH (p)-[o:OWES]->(c)
RETURN sum(o.dollars) AS owedAmount, c.name AS owedName
}
RETURN p.name, owedAmount, owedName
p.name | owedAmount | owedName |
---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Rows: 4 |
Note on returning subqueries and unit subqueries
The examples above have all used subqueries which end with a RETURN
clause.
These subqueries are called returning subqueries.
A subquery is evaluated for each incoming input row. Every output row of a returning subquery is combined with the input row to build the result of the subquery. That means that a returning subquery will influence the number of rows. If the subquery does not return any rows, there will be no rows available after the subquery.
Subqueries without a RETURN
statement are called unit subqueries.
Unit subqueries are used for their ability to alter the graph with clauses such as CREATE
, MERGE
, SET
, and DELETE
.
They do not explicitly return anything, and this means that the number of rows present after the subquery is the same as was going into the subquery.
Unit subqueries
Unit subqueries are used for their ability to alter the graph with updating clauses. They do not impact the amount of rows returned by the enclosing query.
This example query creates five clones of each existing Person
node in the graph.
As the subquery is a unit subquery, it does not change the number of rows of the enclosing query.
MATCH (p:Person)
CALL {
WITH p
UNWIND range (1, 5) AS i
CREATE (:Person {name: p.name})
}
RETURN count(*)
count(*) |
---|
|
Rows: 1 |
Rules
The following is true for CALL
subqueries:
-
A
CALL
subquery can only refer to variables from the enclosing query if they are explicitly imported. -
A
CALL
subquery cannot return variables with the same names as variables in the enclosing query. -
All variables that are returned from a
CALL
subquery are afterwards available in the enclosing query. -
A
CALL
subquery can be used to perform changes to the database. -
A
CALL
subquery requires an importingWITH
clause.
Was this page helpful?