Advanced Cypher querying

This section documents the current Neo4j 2026.x calendar-versioned line — Neo4j moved from semantic to calendar versioning (YYYY.MM) in 2025, and the same line applies to the Graph Data Science library — as published at the Neo4j documentation, which is the reference these pages are written and verified against. No specific monthly patch is pinned. Some areas (Aura’s internal infrastructure, the Raft consensus implementation details, and the GDS Pregel API’s low-level internals) are linked, not documented in depth.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, as Neo4j iterates quickly.

This section’s bibliography lists the reference material consulted while preparing these pages.

Beyond a single MATCH / RETURN, Cypher queries are built by chaining intermediate result sets and by traversing paths whose length or endpoints are not known in advance. This page assumes the Cypher fundamentals page’s :Person / :Movie / ACTED_IN domain and builds on it.

WITH for query chaining

WITH passes a projected, optionally aggregated or filtered, result set from one part of a query into the next — the same variables an earlier MATCH bound are not automatically visible after a WITH unless they are re-listed in it.

// Actors with 3+ movies, then the titles of those movies
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WITH p, count(m) AS movieCount
WHERE movieCount >= 3
MATCH (p)-[:ACTED_IN]->(m:Movie)
RETURN p.name, movieCount, collect(m.title) AS titles
ORDER BY movieCount DESC;

WITH also carries pagination between stages, ordering results before a downstream MATCH narrows them further:

MATCH (p:Person)-[:ACTED_IN]->(:Movie)
WITH p, count(*) AS credits
ORDER BY credits DESC
LIMIT 5
RETURN p.name, credits;

CALL \{ \} subqueries

A CALL \{ …​ \} subquery runs an independent unit of work per incoming row (or once, if uncorrelated) and returns its results into the outer query. It is the modern replacement for most cases that used to need FOREACH tricks or repeated MATCH clauses, and it is required for post-UNION continuation and for per-row aggregation that must not collapse the outer rows.

// For every person, the title of their most recent movie -- computed per row
MATCH (p:Person)
CALL (p) {
  MATCH (p)-[:ACTED_IN]->(m:Movie)
  RETURN m.title AS latestTitle
  ORDER BY m.released DESC
  LIMIT 1
}
RETURN p.name, latestTitle;

CALL \{ \} IN TRANSACTIONS runs a subquery in separate, batched transactions — used for large writes rather than in read-heavy analytical queries like the one above (CALL \{ \} IN TRANSACTIONS).

UNION and UNION ALL

UNION combines the results of two or more queries that return the same column names, de-duplicating rows; UNION ALL keeps every row, including duplicates, and is cheaper when duplicates are known not to occur or do not matter.

MATCH (p:Person)-[:ACTED_IN]->(:Movie {title: 'The Matrix'})
RETURN p.name AS name, 'actor' AS role
UNION ALL
MATCH (p:Person)-[:DIRECTED]->(:Movie {title: 'The Matrix'})
RETURN p.name AS name, 'director' AS role;

List and map comprehensions

A list comprehension filters and/or transforms a list inline, without a separate UNWIND / collect() round trip. A map comprehension does the same over map entries.

MATCH (p:Person {name: 'Tom Hanks'})-[:ACTED_IN]->(m:Movie)
RETURN [movie IN collect(m) WHERE movie.released >= 2000 | movie.title] AS recentTitles;
// Map comprehension over a literal map's entries
WITH {matrix: 1999, inception: 2010, dune: 2021} AS releaseYears
RETURN [key IN keys(releaseYears) WHERE releaseYears[key] >= 2010 | key] AS from2010On;

UNWIND

UNWIND expands a list into one row per element — the inverse of collect() — commonly used to turn a parameter list into rows for matching, or to flatten a list produced earlier in the same query.

UNWIND ['The Matrix', 'Inception', 'Dune'] AS title
MATCH (m:Movie {title: title})<-[:ACTED_IN]-(p:Person)
RETURN title, collect(p.name) AS cast;

OPTIONAL MATCH

OPTIONAL MATCH behaves like an outer join: when the pattern has no match for a given row, its variables are bound to null instead of dropping the row, so a person with no directing credits still appears once in the results.

MATCH (p:Person {name: 'Keanu Reeves'})
OPTIONAL MATCH (p)-[:DIRECTED]->(directed:Movie)
RETURN p.name, directed.title;
// directed.title is null if Keanu has never directed a movie

Together, WITH, CALL \{ \}, UNION / UNION ALL, comprehensions, UNWIND and OPTIONAL MATCH cover the query-composition material in Queries and Clauses.

Variable-length paths

A relationship pattern can specify a hop range with min..max instead of a single hop, matching every path whose length falls in that range. Omitting either bound defaults it ( alone means *0.., unbounded).

// Actors reachable from Tom Hanks within 1 to 3 ACTED_IN hops through shared movies
MATCH (origin:Person {name: 'Tom Hanks'})-[:ACTED_IN*1..3]-(coActor:Person)
WHERE coActor <> origin
RETURN DISTINCT coActor.name
LIMIT 25;

An unbounded or loosely bounded variable-length pattern can traverse a very large part of the graph; always pair it with a LIMIT, a tight upper bound, or a WHERE predicate that prunes the search (variable-length relationships).

shortestPath() and allShortestPaths()

shortestPath() returns one shortest path between two already-bound nodes; allShortestPaths() returns every path tied for shortest. Both require the two endpoint nodes to be matched first and take an upper bound on hops so the search terminates.

// The shortest chain of shared-movie connections between two actors
MATCH (a:Person {name: 'Tom Hanks'}), (b:Person {name: 'Keanu Reeves'})
MATCH path = shortestPath((a)-[:ACTED_IN*..6]-(b))
RETURN [n IN nodes(path) | coalesce(n.name, n.title)] AS chain, length(path) AS hops;
// Every co-acting connection tied for shortest, not just one of them
MATCH (a:Person {name: 'Tom Hanks'}), (b:Person {name: 'Keanu Reeves'})
MATCH path = allShortestPaths((a)-[:ACTED_IN*..6]-(b))
RETURN [n IN nodes(path) | coalesce(n.name, n.title)] AS chain, length(path) AS hops;

See shortest paths for the constraints on mixing these functions with other predicates in the same MATCH.

Continue with Cypher fundamentals for the MATCH / WHERE / RETURN basics these techniques build on.