Org Chart: Find everyone under a VP

Traverse a 4-level reporting hierarchy with variable-length paths to list every direct and indirect report

All recipes· graph· 5 minutesbeginnercypher

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 RECURSIVE block, 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;

Tags

graphcypherhierarchybeginner

Run this on your own machine

Install SynapCores Community Edition free, paste the SQL or Cypher above into the bundled web UI, and watch it run.

Download Free CE