Cypher: describe a shape
Cypher lets you draw the shape you want as ASCII-art and the engine compiles it into a traversal — picking a bound anchor and expanding along the pattern with index-free adjacency — so unlike SQL there is no join planner for expansion, because the pattern already IS the pointer walk.
Raw pointer-walks don't scale to humans. Cypher fixes that: you draw the shape — Alice's friends' friends — and the engine turns the pattern straight into a traversal, no join planner for the expand. But that pattern has two ends, and the engine had to CHOOSE which one to start from. Pick wrong and even index-free adjacency can't save you.
Scene 07
Cypher: describe a shape
- Watch
- Try it
- Predict
- Capture
Nobody hand-writes pointer-walks. In Cypher you DRAW the shape you want, in ASCII-art: (a:User {name:'Alice'})-[:FRIEND]->(b)-[:FRIEND]->(c) — read it left to right: a User named Alice, an arrow to a friend b, another arrow to b's friend c. Watch what the engine does with it. First it binds the anchor — the node with a concrete value, Alice — and rings her. Then it walks the pattern: each -[:FRIEND]-> is one pointer follow from the node it's standing on, and the matched subgraph lights up hop by hop. The shape you typed and the walk that runs are the SAME thing.
Highlighted lines are the ones running in the diagram right now.
def run(pattern):# the ONLY index lookup: find the bound anchor by valuestart = index_seek(pattern.anchor) # Alice, by namefrontier = {start}for arrow in pattern.arrows: # each -[:FRIEND]->next = set()for node in frontier:# EXPAND: follow relationship pointers, no joinnext |= follow(node, arrow.relType)frontier = nextreturn frontier # the matched subgraph
def bind_anchor(pattern):# the bound end has a concrete value; the other ends are freebound = [p for p in pattern.nodes if p.has_value]anchor = most_selective(bound) # here: name='Alice'# the ONE index lookup the whole expand needsstart = index_seek(anchor) # O(log N), oncereturn start # ringed in the diagram
def follow(node, rel_type): # one -[:FRIEND]-> arrowout = set()rel = node.first_rel # pointer on the node recordwhile rel is not None: # the doubly-linked chainif rel.type == rel_type:out.add(rel.other_end(node)) # O(1) per edgerel = rel.next_rel # next pointer, not a seekreturn out # the hop's reached nodes
Where this sits in Build a graph database (Neo4j / Dgraph-style)
Scene 07 of 16, in the The language act — Cypher is a shape; the planner picks an anchor.. A Cypher MATCH (a:User {name:'Alice'})-[:FRIEND]->(b)-[:FRIEND]->(c) is a declarative description of a pattern, and for expansion it maps directly onto pointer-chasing — no join planner needed.
Up next. Cypher compiled our pattern into a walk — but it quietly made a decision we didn't: which node to anchor on. Start from Alice (a few friends) versus start from the movie 1M people rated, and the same pattern costs a thousand times more. The planner's real job isn't joins — it's anchor selection and guessing how many nodes each end pulls in.
All 16 scenes in Build a graph database (Neo4j / Dgraph-style) · Every curriculum