CALL clause
The CALL clause is used to call a subquery inside the existing query.
MAGE procedures are also run with a query with the CALL clause at the beginning.
Switch to MAGE documentation if you want to CALL a graph algorithm or some other procedure from the MAGE library.
-
Uses of CALL subquery
1.1. Cartesian products
1.2. Cartesian products with bounded symbols
1.3. Post-union processing
1.4. Observing changes from previous executions
1.5. Unit subqueries
1.6. Scoped variables withCALL (...)
1.7. Optional subqueries withOPTIONAL CALL
1.8. Conditional subqueries withWHEN -
Invalid uses of CALL subquery
2.1. Returning variables with the same name as those in the outer scope
2.2. Returning non-aliased expressions
2.3. Referencing outer scope variables that don’t exist
2.4. UsingOPTIONALwith a procedure call
1. Uses of CALL subquery
1.1. Cartesian products
CALL subquery is executed once for each incoming row. If multiple rows are produced from the
CALL subquery, the result is a Cartesian product of results. It is an output combined from 2 branches,
one being called the input branch (rows produced before calling the subquery), and the subquery branch
(rows produced by the subquery).
Imagine the data includes two :Person nodes, one named John and one named Alice,
as well as two :Animal nodes, one named Rex and one named Lassie.
Running the following query would produce the output below:
MATCH (p:Person)
CALL {
MATCH (a:Animal)
RETURN a.name as animal_name
}
RETURN p.name as person_name, animal_nameOutput:
+-------------+-------------+
| person_name | animal_name |
+---------------------------+
| 'John' | 'Rex' |
| 'John' | 'Lassie' |
| 'Alice' | 'Rex' |
| 'Alice' | 'Lassie' |
+---------------------------+1.2. Cartesian products with bounded symbols
To reference variables from the outer scope in the subquery, start the subquery with the WITH clause.
It allows using the same symbols to expand on the neighborhood of the referenced nodes or relationships.
Otherwise, the subquery will behave as it sees the variable for the first time.
In the following query, the WITH clause expanded the meaning of the variable person to the node with the
label :Person matched in the outer scope of the subquery:
MATCH (person:Person)
CALL {
WITH person
MATCH (person)-[:HAS_PARENT]->(parent:Parent)
RETURN parent
}
RETURN person.name, parent.nameOutput:
+-------------+-------------+
| person_name | parent_name |
+---------------------------+
| 'John' | 'John Sr.' |
| 'John' | 'Anna' |
| 'Alice' | 'Roxanne' |
| 'Alice' | 'Bill' |
+---------------------------+1.3. Post-union processing
Output from all UNION queries inside a subquery can be combined and forwarded as a single output to make the queries more expressive:
CALL {
MATCH (n:Person)
RETURN n.name AS name, n.ssn AS ID_number
UNION
MATCH (n:Company)
RETURN n.name AS name, n.corporate_id AS ID_number
}
RETURN name, ID_numberOutput:
+------------+-------------+
| name | ID_number |
+--------------------------+
| 'John' | '123456789' |
| 'Memgraph' | '555555555' |
+--------------------------+1.4. Observing changes from previous executions
Each execution of a CALL clause 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 totalCountOutput:
+------------+-------------+
| innerCount | totalCount |
+--------------------------+
| 1 | 3 |
| 2 | 3 |
| 3 | 3 |
+--------------------------+1.5. Unit subqueries
Unit subqueries are used to perform a single action for every node from the input branch.
If the starting state of the database is that there is only one :Person node in the graph,
the following query will clone the node with desired preferences defined in the FOREACH clause.
MATCH (p:Person)
CALL {
FOREACH (i IN range(1, 5) | CREATE(:Person {id: i}))
}
MATCH (n) RETURN COUNT(n) AS no_created_nodes;Output:
+------------------+
| no_created_nodes |
+------------------+
| 6 |
+------------------+1.6. Scoped variables with CALL (...)
Instead of importing outer-scope variables with a leading WITH clause, you
can list them in parentheses directly after CALL. Three forms are
supported:
CALL () { ... }— no outer variables are imported. The subquery sees only what it produces internally.CALL (v1, v2, ...) { ... }— only the listed variables are imported.CALL (*) { ... }— every variable currently in the outer scope is imported.
Importing only p from the outer scope:
MATCH (p:Player)-[:PLAYS_FOR]->(t:Team)
CALL (p) {
WITH p.age / 100.0 AS rating
SET p.rating = rating
RETURN p.rating AS rating
}
RETURN p.name AS playerName, rating, t AS team;Importing every outer variable with CALL (*):
MATCH (p:Player)-[:PLAYS_FOR]->(t:Team)
CALL (*) {
SET p.lastUpdated = timestamp()
SET t.lastUpdated = timestamp()
}
RETURN p.name, t.name;Running a subquery that does not depend on the outer row with CALL ():
UNWIND [0, 1, 2] AS x
CALL () {
RETURN 'hello' AS innerReturn
}
RETURN x, innerReturn;The scoped form is equivalent to using WITH to import variables, but
narrows the subquery’s scope explicitly at the boundary, which makes the
import list visible at a glance.
1.7. Optional subqueries with OPTIONAL CALL
CALL drops an input row when the subquery returns no rows for it. OPTIONAL CALL keeps that row and sets the columns the subquery returns to null, which
is the same contract as OPTIONAL MATCH.
Imagine the data from 1.2, plus a
third :Person named Carol who has no :HAS_PARENT relationship:
MATCH (person:Person)
OPTIONAL CALL (person) {
MATCH (person)-[:HAS_PARENT]->(parent:Parent)
RETURN parent.name AS parent_name
}
RETURN person.name AS person_name, parent_nameOutput:
+-------------+-------------+
| person_name | parent_name |
+---------------------------+
| 'John' | 'John Sr.' |
| 'John' | 'Anna' |
| 'Alice' | 'Roxanne' |
| 'Alice' | 'Bill' |
| 'Carol' | null |
+---------------------------+Without OPTIONAL, Carol does not appear in the result at all.
OPTIONAL can be combined with every scope form (CALL (v1, v2), CALL (*),
CALL (), or no scope clause), with a UNION subquery, with nested subqueries,
and with IN TRANSACTIONS.
Variables imported from the outer scope keep their values; only the columns the
subquery itself introduces are set to null. A
unit subquery returns no columns, so OPTIONAL has
nothing to set to null and the number of rows is the same either way.
1.8. Conditional subqueries with WHEN
A conditional subquery runs, for each input row, the first branch whose
predicate is true:
CALL (...) {
WHEN predicate THEN body
[WHEN predicate THEN body ...]
[ELSE body]
}UNWIND [{name: 'Ana', age: 65}, {name: 'Ivan', age: 25}, {name: 'Marko', age: 39}] AS person
CALL (person) {
WHEN person.age > 60 THEN RETURN 'senior' AS bracket
WHEN person.age > 30 THEN RETURN 'adult' AS bracket
ELSE RETURN 'young' AS bracket
}
RETURN person.name AS name, bracket;Output:
+---------+----------+
| name | bracket |
+---------+----------+
| "Ana" | "senior" |
| "Ivan" | "young" |
| "Marko" | "adult" |
+---------+----------+A body is a single query, or a query in braces that may contain UNION or
another WHEN:
UNWIND [1, 2] AS i
CALL (i) {
WHEN i = 1 THEN {
RETURN 'a' AS x
UNION
RETURN 'b' AS x
}
ELSE {
WHEN i > 5 THEN RETURN 'big' AS x
ELSE RETURN 'small' AS x
}
}
RETURN i, x;Output:
+---+---------+
| i | x |
+---+---------+
| 1 | "a" |
| 1 | "b" |
| 2 | "small" |
+---+---------+When no branch matches a row, the result depends on the body:
- A body that returns rows drops the input row, as a
CALLsubquery that returns no rows does.OPTIONAL CALLkeeps the row and sets the returned columns tonull. - A body without
RETURN, such as one that only updates the graph, keeps every input row, as a unit subquery does.
UNWIND [1, 2, 3] AS i
CALL (i) {
WHEN i = 1 THEN CREATE (:Tier {i: i, level: 'gold'})
WHEN i = 2 THEN CREATE (:Tier {i: i, level: 'silver'})
}
RETURN i;The query returns the rows 1, 2 and 3, and creates two :Tier nodes.
The branches of one conditional subquery must agree:
- All branches return rows, all branches update the graph without
RETURN, or all branches are a standalone procedure call. - Branches that return rows return the same column names. The order of the columns can differ.
A conditional subquery needs a scope clause: CALL { WHEN ... } is an error, and
CALL () { WHEN ... } is valid. A branch cannot set a query memory limit or
start with USING. The whole conditional subquery can run
IN TRANSACTIONS.
2. Invalid uses of CALL subquery
2.1. Returning variables with the same name as those in the outer scope
Invalid use:
MATCH (n:Person)
CALL {
MATCH (n:Parent)
RETURN n
}
RETURN n;The above query results in a semantic exception because the variable n has
already been used in the outer scope of the query. The query will
successfully execute by renaming either the outer scope variable or the subquery variable.
Valid use:
MATCH (n:Person)
CALL {
MATCH (p:Parent)
RETURN p
}
RETURN n, p;2.2. Returning non-aliased expressions
Invalid use:
MATCH (n:Person)
CALL {
WITH n
MATCH (n)-[:HAS_PARENT]->(parent:Parent)
RETURN parent.age
}
RETURN n, parent.age;The above query results in a semantic exception since the expression returned in the subquery has not been aliased and can not be interpreted correctly. By aliasing the returned expression upon exiting the subquery, it can be used in the outer scope.
Valid use:
MATCH (n:Person)
CALL {
WITH n
MATCH (n)-[:HAS_PARENT]->(parent:Parent)
RETURN parent.age AS parent_age
}
RETURN n, parent_age;2.3. Referencing outer scope variables that don’t exist
Invalid use:
MATCH (n:Person)
CALL {
WITH o
MATCH (o)-[:HAS_CHILD]->(child:Parent)
RETURN child
}
RETURN DISTINCT n;The above query results in a semantic exception because the variable from the outer scope does not exist.
Queries can be executed only by referencing variables bounded in the input branch to the subquery.
By renaming the variable to the already bounded variable n, the query will be correctly executed.
Valid use:
MATCH (n:Person)
CALL {
WITH n
MATCH (n)-[:HAS_CHILD]->(child:Parent)
RETURN child
}
RETURN DISTINCT n;2.4. Using OPTIONAL with a procedure call
OPTIONAL applies to a CALL subquery, not to a procedure call:
MATCH (p:Person)
OPTIONAL CALL algo.procedure(p)
YIELD result
RETURN p.name, result;The above query results in an error. Wrap the procedure call in a subquery instead:
MATCH (p:Person)
OPTIONAL CALL (p) {
CALL algo.procedure(p) YIELD result
RETURN result
}
RETURN p.name, result;