rel2graph — analyze a relational DB → propose/describe → transform to a graph (2026-07-26, Rickard)
Analyze a relational database, propose + describe a property-graph model for it, and (optionally) execute the transformation into a graph store (FalkorDB). Engine lives in knut (the graph/lake bridge); it's surfaced as one function in korp's Tools menu; storage in skade.
Placement (decided)
- Engine → knut (
knut/crates/knut-rel2graph). knut is already the graph/lake bridge — Iceberg, Flight SQL, FalkorDB. Today knut moves relational→graph; rel2graph adds the front half: analyze → propose → then move. Same concern, deeper — NOT a new repo (a new repo would split the graph↔relational seam + duplicate the FalkorDB/FlightSQL plumbing; extract later only if it becomes a standalone product). - Host / UI → korp, as a function in korp's Tools menu (korp's
menu_button). korp is the app that hosts the data/analysis Tools menu (this is why the korp-style Tools pane is being REMOVED from dwarves — that menu belongs to korp).korp Tools → Relational-to-Graphdrives knut-rel2graph and renders the proposal in korp's PALANTIR graph/ontology view for review/edit → then execute. - Storage → skade — the reflected schema + the mapping spec land as versioned warehouse tables, so proposals are diffable over time.
- Contrast: dwarves' "tools" are Mímir lenses (dedupe/Trust/… under find→fix), a different kind from korp's Tools menu. dwarves = Mímir lenses + Build Thing/CI; korp = Tools-menu host for data tools.
Pipeline
-
Schema reflection — connect to the source (knut Flight SQL, or a relational connector for Postgres/MySQL/etc.) and reflect: tables, columns + types, PK, FKs, indexes, and sampled cardinality (1:1 / 1:N / M:N).
-
Model inference (relational → property graph):
- table → node label; row → node; PK → node identity.
- non-FK columns → node properties (type-mapped).
- FK → relationship (direction from cardinality; the "many" side points to the "one").
- junction/join table (≥2 FKs, few/no other columns) → collapse to an edge (the classic M:N).
- self-referential FK → self-loop relationship (hierarchies/trees).
- small lookup/enum tables → optionally fold into a property or an enum label (heuristic, user-editable).
-
Propose + describe — emit a mapping spec (relational→graph, machine-readable) + a human-readable rationale ("
orders.customer_id→(:Order)-[:PLACED_BY]->(:Customer)") + a migration plan + caveats (ambiguous FKs, missing constraints, big tables). -
Review (korp) — render the proposed graph; let the user edit the mapping (rename labels/rels, promote/demote columns, keep/collapse a join table); re-describe on edit.
-
Execute (optional) — run the ETL into FalkorDB via knut's existing bridge, backend-agnostic through the planned
Graphtrait (petgraph / FalkorDB / cozo). Idempotent; report counts.
Output EMITTERS — one analyzed model → many pipelines (Rickard)
rel2graph produces a typed mapping spec (the model), then emitters render it to executable targets — describe-first, execute-anywhere:
.runa.ron(native, PRIMARY) — a typed runa pipeline our own engine runs:dwarves run x.runa.ron→dwarves_runa::Steps →runa_exec→ jera WorkPayloads (Process/Container/KVM). RON IS our native pipeline format (runa_backend.rs), so rel2graph emitting.runa.ronmeans the relational→graph ETL is a first-class, replayable, historized forge pipeline. Yes — the engine reads RON.- Spark pipeline ("SPD") — a Spark-Connect job / Spark SQL for the FAT / Spark-4.1 + Iceberg infra (korp's Spark-Connect lane,
korp/src/tools.rs) — run the ETL at scale on Spark when that infra is up. - Cypher / openCypher — direct FalkorDB load script (fastest path for the graph store).
- the mapping spec itself as RON — a typed, reviewable, diffable model object (skade-versioned). The emitter set is open: same analyzed model, pick the target by deployment (jera-native for the appliance, Spark for scale, Cypher for a quick FalkorDB load). This mirrors the
ContainerBackend/Graph-trait pattern — one model, swappable backends.
Symmetry with Mímir
Same pattern as Mímir, different source: Mímir analyzes CODE and models it as a graph; rel2graph analyzes a RELATIONAL DB and models it as a graph. Two instances of "analyze a structured source → propose a graph model." Reuse the backend-agnostic Graph trait as the output target.
Reuse
knut: FalkorDB client, Flight SQL, (new) relational connector. skade: schema + mapping storage. The Graph trait (planned, nornir-leaf-extraction) for backend-agnostic output. korp/facett: the graph viewer + Tools-menu host. Cozo/FalkorDB for recursive/graph queries over the result.
Status
Design capture only (fleet stays on the Tuesday korp demo). Build after the demo: start with the schema-reflection + model-inference + mapping-spec (describe-only), then the korp Tools-menu review UI, then the FalkorDB ETL execute.