Reachability, hierarchies and 'how are these connected' questions are where graph databases beat SQL most clearly.
When the number of hops is not fixed, use a quantified relationship: (a)-[:FOLLOWS]->{1,3}(b) matches paths of one to three FOLLOWS hops. + means one or more and * zero or more. The older syntax -[:FOLLOWS*1..3]-> still works but is not GQL-conformant.
Quantified path patterns (Cypher 5.9+) repeat a whole sub-pattern and can filter inside it:
MATCH (a:Stop)((x)-[r:NEXT]->(y) WHERE r.minutes < 10){1,5}(b:Stop)Inside the group, variables become lists across the repetitions.
Assign a path to a variable with p = ... and use length(p), nodes(p) and relationships(p).
For shortest paths, Cypher 5.21+ supports SHORTEST k, ALL SHORTEST and SHORTEST k GROUPS in front of a path pattern; the older shortestPath() and allShortestPaths() functions remain. Shortest path here means fewest hops; weighted shortest paths (by distance or cost) come from Graph Data Science or APOC.
Always bound the length: an unbounded * or + over a dense graph can explore millions of paths. Use labels and relationship types, set an upper limit, and filter as early as possible.
Example
// everyone Ann can reach in 1 to 3 FOLLOWS hops, with the distance
MATCH p = (:Person {name: 'Ann'})-[:FOLLOWS]->{1,3}(x:Person)
RETURN x.name, min(length(p)) AS hops
ORDER BY hops;
// the shortest connection between Ann and Dan, any direction
MATCH p = SHORTEST 1 (a:Person {name: 'Ann'})-[:FOLLOWS]-+(d:Person {name: 'Dan'})
RETURN [n IN nodes(p) | n.name] AS route, length(p) AS hops;
// management chain upward, at most 10 levels
MATCH (e:Employee {name: 'Cy'})-[:REPORTS_TO]->{1,10}(boss:Employee)
RETURN collect(boss.name) AS chain;Remember
-[:T]->{1,3},+and*quantify hops;*1..3is the legacy form- Quantified path patterns repeat sub-patterns with inline WHERE
SHORTEST k/shortestPath()find fewest-hop paths- Bound path lengths to avoid exploding traversals
Watch out for
- Running an unbounded variable-length match on a dense graph and timing out.