· neo4j cypher

Neo4j: Cypher - FOREACH vs CALL {} (subquery)

I recently wanted to create a graph based on an adjacency list, and in this post we’ll learn how to do that using the FOREACH clause and then with the new CALL {} subquery clause.

We’ll start with the following map of ids → arrays of ids:

:param list => ({`0`: [7, 9], `1`: [2, 4, 5, 6, 8, 9], `2`: [0, 6, 8, 9], `3`: [1, 2, 6, 9], `4`: [1, 2, 3, 7], `5`: [8, 9], `6`: [2, 4, 5, 7, 8, 9], `7`: [0, 3, 4, 6, 8, 9], `8`: [1, 6, 9], `9`: [0, 1, 3, 5]})

We want to create one node per id and create a relationship from each node to the nodes in its array. So for example, we’ll have a relationship from 07, 09, 12, and so on.

We can do this using the FOREACH clause, as shown in the following query

UNWIND keys($list) AS key
MERGE (n:Node {id: toInteger(key)})
FOREACH(item IN $list[key] |
  MERGE (m:Node {id: item})
  MERGE (n)-[:CONNECTED_TO]->(m)
)
RETURN n.id;

If we run this query, we’ll see the following output:

Table 1. Results
n.id

0

1

2

3

4

5

6

7

8

9

But what if along with the id, we’d also like to return the number of outgoing relationships from each node? One way to do this would be to use the UNWIND clause to iterate through the values in the array, as shown below:

UNWIND keys($list) AS key
MERGE (n:Node {id: toInteger(key)})
WITH n, key
UNWIND $list[key] AS item
MERGE (m:Node {id: item})
MERGE (n)-[:CONNECTED_TO]->(m)
RETURN n.id, count(*);

If we run this query, we’ll see the following output:

Table 2. Results
n.id count

0

2

1

6

2

4

3

4

4

4

5

2

6

6

7

6

8

3

9

4

This works, but we are creating a lot of extra rows between lines 4-6, before squashing them back again on line 7. In this example it’s not too much of a problem, but in queries with multiple UNWIND clauses, we can simplify things by isolating the side effects in a CALL {} subquery.

If we do that, we’ll have the query below:

UNWIND keys($list) AS key
MERGE (n:Node {id: toInteger(key)})
CALL {
  WITH key, n
  UNWIND $list[key] AS item
  MERGE (m:Node {id: item})
  MERGE (n)-[:CONNECTED_TO]->(m)
  RETURN count(*) AS count
}
RETURN n.id, count

If we run that query, we’ll see the following output:

Results
WITH is required between MERGE and CALL (line 3, column 1 (offset: 62))
"CALL {"
 ^

Hmmm, not quite. Let’s add a WITH clause for n and key:

UNWIND keys($list) AS key
MERGE (n:Node {id: toInteger(key)})
WITH n, key
CALL {
  UNWIND $list[key] AS item
  MERGE (m:Node {id: item})
  MERGE (n)-[:CONNECTED_TO]->(m)
  RETURN count(*) AS count
}
RETURN n.id, count;

And run our query again:

Results
Variable `key` not defined (line 5, column 16 (offset: 96))
"  UNWIND $list[key] AS item"
                ^

Not quite! We also need to add a WITH clause with each of these values to the start of the CALL{} clause:

UNWIND keys($list) AS key
MERGE (n:Node {id: toInteger(key)})
WITH n, key
CALL {
  WITH key, n
  UNWIND $list[key] AS item
  MERGE (m:Node {id: item})
  MERGE (n)-[:CONNECTED_TO]->(m)
  RETURN count(*) AS count
}
RETURN n.id, count

And now if we run the query, we’ll see the following output:

Table 3. Results
n.id count

0

2

1

6

2

4

3

4

4

4

5

2

6

6

7

6

8

3

9

4

  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket