Graph Analytics
Preview — three whole-graph algorithms ship on the SQL surface today (
graph_pagerank,graph_components,graph_bfs). Signatures and output shapes below are the stable contract; the algorithm set is still growing.
Beyond single-start traversals, the engine ships whole-graph algorithms as SQL table functions. Each returns typed rows and composes into a larger statement exactly like the traversals — see Composing traversals with SQL.
All analytics are eventual-only: they run over the pinned artifact (there
is no 'strong' freshness argument — a read-your-writes overlay isn’t
meaningful for a full-graph sweep). Rebuilds on table drain keep them fresh;
see Graph DDL.
graph_pagerank
SELECT * FROM graph_pagerank('<graph>' [, <damping> [, <max_iters>]]);| arg | type | default | constraint |
|---|---|---|---|
damping | float | 0.85 | finite, 0.0 – 1.0 (out of range → loud error) |
max_iters | int | 100 | non-negative |
Output: (node BIGINT, rank DOUBLE) — one row per node, ranks form a
probability distribution (Σ ≈ 1.0). Convergence tolerance is fixed at
1e-9 (L1 delta between sweeps); iteration stops at the tolerance or
max_iters, whichever first.
Semantics (canonical PageRank, matching Neo4j GDS / NetworkX):
- Standard power iteration with uniform dangling-mass redistribution — a node with no out-edges donates its rank to all nodes, so mass never leaks.
- Parallel edges count: a
u→vedge stored twice sendsvtwo shares ofu’s rank. Self-loops count as ordinary edges. - Deterministic: identical input ⇒ bit-identical output.
SELECT * FROM graph_pagerank('social'); -- defaults
SELECT * FROM graph_pagerank('social', 0.5, 50); -- tunedgraph_components
SELECT * FROM graph_components('<graph>');Output: (node BIGINT, component INT) — one row per node, including
isolated nodes (each its own component).
- Components are weak (edges treated as undirected — the graph-DB
default;
1→2with no back-edge is still one component). Strongly-connected components are not offered. - Component ids are dense
0..kand deterministic: numbered ascending by each component’s smallest node key — stable across rebuilds and edge reordering.
-- fixture: web {1..7} + disconnected pair 99→98
SELECT * FROM graph_components('social');
-- {1..7} → component 0 (smallest key 1), {98,99} → component 1graph_bfs
SELECT * FROM graph_bfs('<graph>', <start> [, '<direction>']);direction ∈ 'out' (default) · 'in' · 'both'.
Output: (node BIGINT, level INT) — every node reachable from start
under the direction, once, at its minimum hop level. The start itself is the
first row at level 0.
- Unreachable nodes are absent — no level-∞ rows.
- Unknown start key → zero rows (not an error).
- This is
graph_khoprun to exhaustion plus the level-0 start row — same minimum-distance semantics.
SELECT * FROM graph_bfs('social', 1);
-- node | level (fixture 1→2→3→4→5, 2→6→7)
-- 1 | 0
-- 2 | 1
-- 3 | 2 6 | 2
-- 4 | 3 7 | 3
-- 5 | 4 -- 98/99 absent (disconnected)
SELECT * FROM graph_bfs('social', 7, 'both'); -- walk back up the chainError behavior
Malformed analytics calls are loud invalid_argument errors, never silent
fall-throughs — wrong arity (graph_components('g', 3)), out-of-range
damping (graph_pagerank('g', 1.5)), missing start (graph_bfs('g')) all
reject with a message naming the expected signature.
In the engine but not (yet) on the SQL surface
The engine core also implements per-node degree and a whole-graph
degree_distribution histogram, plus capped/filtered expansion variants.
These have no graph_* SQL function today — don’t look for
graph_degree(…). Degree is easy to get from what is exposed:
-- out-degree of one node = row count of its neighbors
SELECT count(*) AS out_degree FROM graph_neighbors('social', 2);Elsewhere on the wire
SQL is the full analytics surface. The other doors are narrower:
| Door | Analytics? |
|---|---|
SQL — Postgres wire, Execute, dodil data sql | all three functions, composable into a larger statement |
HTTP — POST /v1/graph/:name/analytics | all three, one per request: the JSON body carries exactly one of pagerank ({iters, damping}), bfs ({startId}), or connectedComponents ({}) — supplying zero or more than one is a 400 |
gRPC — TablesGraph.RunGraphAnalytics | all three, as a oneof algo with the same three arms. Zero-valued PageRank knobs take the engine defaults above |
| Bolt / Cypher | none — the Cypher subset has no analytics spelling |
| GraphQL | none — it exposes the traversal root fields only |
-- the portable spelling
SELECT * FROM graph_pagerank('social', 0.85, 100);See also
- Graph → Cypher & Traversals — single-start traversals and the openCypher subset
- Graph → DDL — defining a graph and its build lifecycle
- Tables → SQL Compatibility — the SQL surface these table functions run on