Overview
Variable-Length Paths
*1..3 / shortestPath / allShortestPaths — traversals that don't know their depth in advance.
Why it matters
Always bound your stars. MATCH (a)-[*]->(b) will happily walk the whole graph; MATCH (a)-[*1..3]->(b) won't.
Going deeper
Patterns you'll actually use, in increasing specificity:
| Pattern | Returns | Cost shape |
|---|---|---|
(a)-[:R*1..3]->(b) | Every path up to depth 3 | Bounded fan-out per hop |
shortestPath((a)-[*..6]-(b)) | One shortest path, any direction | Bidirectional BFS, very fast |
allShortestPaths((a)-[*..6]-(b)) | Every path of the same shortest length | One BFS + enumeration of ties |
(a)-[*]->(b) (no bound) | Anything reachable | Avoid — unbounded traversal |
Three habits that keep variable-length queries fast:
- Always upper-bound the star — even if the bound is generous (e.g.
*..15). It changes 'unbounded' into 'bounded but loose', and that flips the planner. - Type-constrain the relationship —
[:KNOWS*1..4]instead of[*1..4]. Skips every edge of a different type at planning time. - Direction matters —
(a)-[*1..4]->(b)only follows outgoing edges; omitting the arrow doubles the fan-out at every hop.