These tools express complex questions in one query without row explosions or client-side loops.
Lists are first-class. UNWIND list AS x turns a list into rows, which is the standard way to write many items from one parameter: UNWIND $rows AS row MERGE .... In the other direction, collect() turns rows into a list.
Comprehensions transform lists inline:
- list comprehension:
[x IN list WHERE x > 0 | x * 2] - pattern comprehension:
[(p)-[:FOLLOWS]->(f) | f.name]collects matches of a pattern per row, without an OPTIONAL MATCH - functions:
size,reduce,range,head,last, slicinglist[1..3]
Subquery expressions evaluate a pattern or query per row:
EXISTS { (p)-[:WORKS_AT]->(:Company {name: 'Acme'}) }true or falseCOUNT { (p)<-[:FOLLOWS]-() }a number, e.g. degreeCOLLECT { MATCH (p)-[:FOLLOWS]->(f) RETURN f.name ORDER BY f.name }a list
CALL subqueries run a full query per incoming row: CALL (p) { ... RETURN ... }. They allow per-row ORDER BY/LIMIT (top 3 per person), UNION post-processing, and batching writes with IN TRANSACTIONS. The variables in parentheses are imported into the subquery's scope.
These keep each part of the computation local, so you avoid the cartesian growth that multiple OPTIONAL MATCHes cause.
Example
// batch write from a parameter list (driver passes $rows)
UNWIND $rows AS row
MERGE (p:Person {email: row.email})
SET p.name = row.name;
// per person: follower count, whether at Acme, top 2 followees by name
MATCH (p:Person)
RETURN p.name,
COUNT { (p)<-[:FOLLOWS]-() } AS followers,
EXISTS { (p)-[:WORKS_AT]->(:Company {name: 'Acme'}) } AS atAcme,
COLLECT { MATCH (p)-[:FOLLOWS]->(f) RETURN f.name ORDER BY f.name LIMIT 2 } AS follows;
// top follower per company with a CALL subquery
MATCH (c:Company)
CALL (c) {
MATCH (c)<-[:WORKS_AT]-(e)<-[:FOLLOWS]-(fan)
RETURN fan.name AS topFan, count(*) AS n ORDER BY n DESC LIMIT 1
}
RETURN c.name, topFan, n;Remember
UNWINDturns lists into rows; ideal for batched writes from parameters- List and pattern comprehensions build lists inline
- EXISTS, COUNT and COLLECT subqueries evaluate per row
CALL (vars) { ... }runs a full subquery per row, enabling top-N per group
Watch out for
- Sending one query per item from the client instead of UNWINDing a list parameter in one query.