Fourteen graph methods,
shipped.
Not a graph database that stored your edges and stopped there. PageRank, betweenness, eigenvector centrality, label-propagation communities, plus Node2Vec and GraphSAGE node embeddings — all on the same direction-tagged relation keys. Per-hop EXPLAIN ANALYZE shows you what every step cost.
Four families. Fourteen methods.
Traverse to answer path questions, rank to find who matters, cluster to find who belongs together, embed to hand the graph to a model. All fourteen run against the same relation keys your rows already have — no export, no sync, no second engine.
Per-hop WHERE filters the frontier before it fans out into the next hop. The bidirectional all-paths variant meets in the middle — 5-20x faster than forward-only on real graphs, same answer.
PageRank by power-method iteration. Betweenness via Brandes. Eigenvector with a bipartite-safe A+I shift, so it converges on graphs where plain power iteration oscillates.
Components on Union-Find. Label propagation is seedable and typically converges in ~10-20 iterations — same seed, same communities. Triangles for clustering coefficient and motif counts.
Node2Vec with tunable p/q walk bias and disk persistence. GraphSAGE folds node attributes in through a choice of three aggregators, with a deterministic per-(node, layer) shuffle — reruns reproduce.
Fan-out is the enemy. Watch it happen.
A traversal that touches 12 nodes at depth 1 can touch thousands by depth 4 — each hop multiplies the frontier. When a path query gets slow, the question is always the same: which hop exploded, and would a per-hop WHERE have stopped it?
EXPLAIN ANALYZE answers per hop: frontier size, edges considered, milliseconds spent. In the trace here, depth 4 alone accounts for 19.7 of the 26.0 ms total — that's where a tighter predicate pays. How traversal fits the rest of the engine is on the architecture page.
depth frontier edges ms
1 12 38 0.4
2 184 612 1.1
3 1,942 6,204 4.8
4 8,773 28,891 19.7
total 26.0 MATCH (a)-[:KNOWS*1..3]->(b)
WHERE b.city = 'Berlin'
RETURN DISTINCT b.id, b.name; MERGE (a:User {id: 'u_84'})
FOREACH (f IN a.follows |
MERGE (b:User {id: f})
MERGE (a)-[:FOLLOWS]->(b)); The pattern language, not a subset of it.
Write the traversal the way you'd draw it. Cypher v3 covers the constructs that separate a demo parser from a working one — CALL subqueries, nested FOREACH, list comprehensions, MERGE upserts, DELETE, and multi-hop undirected patterns.
Three problems, and the methods that fit them.
Accounts sharing a device, an address, a card. Components surface the ring; bidirectional all-paths shows every route between two flagged accounts, bounded by depth and max_paths.
Embed the interaction graph, then nearest-neighbour in embedding space — or stay symbolic and walk *1..N from what the user just bought. Both run on the same relation keys.
Who matters in the network, three definitions: PageRank for global weight, betweenness for brokers sitting on shortest paths, eigenvector for influence by association.