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.

  1. 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

  2. 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

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_name

Output:

+-------------+-------------+
| 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.name

Output:

+-------------+-------------+
| 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_number

Output:

+------------+-------------+
| 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 totalCount

Output:

+------------+-------------+
| 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                |
+------------------+

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;