Org Chart: Find everyone under a VP
Objective
Answer "who reports to VP Diana — directly or transitively, however deep the tree?" Recursive CTEs in SQL solve this but require careful base-case and termination logic. In Cypher, a single variable-length pattern walks the whole subtree of an org chart in one line.
Step 1: Create the graph
// Build a 4-level org: CEO -> VPs -> Directors -> Managers -> ICs
MERGE (ceo:Employee {name: "Adrian Cole", title: "CEO"})
MERGE (diana:Employee {name: "Diana Foster", title: "VP Engineering"})
MERGE (marcus:Employee {name: "Marcus Liu", title: "VP Sales"})
MERGE (priya:Employee {name: "Priya Shah", title: "Director Platform"})
MERGE (omar:Employee {name: "Omar Hassan", title: "Director Mobile"})
MERGE (chen:Employee {name: "Chen Wei", title: "Director Field Sales"})
MERGE (jane:Employee {name: "Jane Kim", title: "Engineering Manager"})
MERGE (theo:Employee {name: "Theo Ng", title: "Engineering Manager"})
MERGE (rosa:Employee {name: "Rosa Diaz", title: "Sales Manager"})
MERGE (eng1:Employee {name: "Liam Brown", title: "Senior Engineer"})
MERGE (eng2:Employee {name: "Ava Singh", title: "Engineer"})
MERGE (eng3:Employee {name: "Ben Ortiz", title: "Engineer"})
MERGE (rep1:Employee {name: "Nina Park", title: "Account Executive"})
MERGE (diana)-[:REPORTS_TO]->(ceo)
MERGE (marcus)-[:REPORTS_TO]->(ceo)
MERGE (priya)-[:REPORTS_TO]->(diana)
MERGE (omar)-[:REPORTS_TO]->(diana)
MERGE (chen)-[:REPORTS_TO]->(marcus)
MERGE (jane)-[:REPORTS_TO]->(priya)
MERGE (theo)-[:REPORTS_TO]->(omar)
MERGE (rosa)-[:REPORTS_TO]->(chen)
MERGE (eng1)-[:REPORTS_TO]->(jane)
MERGE (eng2)-[:REPORTS_TO]->(jane)
MERGE (eng3)-[:REPORTS_TO]->(theo)
MERGE (rep1)-[:REPORTS_TO]->(rosa);
Step 2: List everyone in Diana's organisation
// Variable-length path: 1..5 hops up the REPORTS_TO chain back to Diana.
MATCH (report:Employee)-[:REPORTS_TO*1..5]->(vp:Employee {name: "Diana Foster"})
RETURN report.name AS employee, report.title AS title
ORDER BY title, employee;
What's happening
[:REPORTS_TO*1..5]matches paths of length 1 to 5 — direct and indirect reports in one query.- The query returns 6 people across 3 levels without writing any recursion or UNION ALL.
- SQL recursive CTEs need a
WITH RECURSIVEblock, anchor query, and join — typically 15+ lines. - Cypher computes this via index-free adjacency: each node holds pointers to its neighbours, so graph traversal is O(matched edges), not O(table size).
- Swap the VP name to instantly re-run for any subtree — the same pattern works at any scale.
Try this next
MATCH (vp:Employee {name: "Diana Foster"})<-[:REPORTS_TO*]-(report)
RETURN count(report) AS org_size;
MATCH path = (ic:Employee)-[:REPORTS_TO*]->(ceo:Employee {title: "CEO"})
WHERE NOT (ic)<-[:REPORTS_TO]-()
RETURN ic.name AS ic, length(path) AS depth_to_ceo;
MATCH (mgr:Employee)<-[:REPORTS_TO]-(direct)
RETURN mgr.name AS manager, count(direct) AS span_of_control
ORDER BY span_of_control DESC;