feat(viewer): schema linking renders a Mermaid erDiagram (tables + relations)
This commit is contained in:
@@ -33,24 +33,37 @@ function reasonFor(c: Candidate): string {
|
||||
return "";
|
||||
}
|
||||
|
||||
function buildFlowchart(promoted: Candidate[], joins: Join[]): string {
|
||||
const nodeId = (name: string) => name.replace(/[^a-zA-Z0-9]/g, "_");
|
||||
export function buildErDiagram(promoted: Candidate[], joins: Join[]): string {
|
||||
const sanitize = (name: string) => name.replace(/[^a-zA-Z0-9]/g, "_");
|
||||
const tables = promoted.filter((c) => c.kind === "table");
|
||||
const tableNames = new Set(tables.map((t) => t.name));
|
||||
const columns = promoted.filter((c) => c.kind === "column");
|
||||
|
||||
const lines: string[] = ["flowchart TB"];
|
||||
const lines: string[] = ["erDiagram"];
|
||||
|
||||
for (const c of promoted) {
|
||||
const id = nodeId(c.name);
|
||||
const reason = reasonFor(c);
|
||||
const tooltip = reason ? ` %% ${reason}` : "";
|
||||
const shape = c.kind === "table" ? `[${c.name}]` : `(${c.name})`;
|
||||
lines.push(` ${id}${shape}${tooltip}`);
|
||||
for (const t of tables) {
|
||||
const id = sanitize(t.name);
|
||||
const cols = columns.filter((col) => col.name.startsWith(t.name + "."));
|
||||
lines.push(` ${id} {`);
|
||||
for (const col of cols) {
|
||||
lines.push(` col ${sanitize(col.name.slice(t.name.length + 1))}`);
|
||||
}
|
||||
lines.push(` }`);
|
||||
}
|
||||
|
||||
const promotedNames = new Set(promoted.map((c) => c.name));
|
||||
// A join endpoint may be "table" or "table.column"; resolve to its owning table.
|
||||
// Draw a relationship only between two DISTINCT promoted tables, once per pair.
|
||||
const owningTable = (ref: string) =>
|
||||
ref.includes(".") ? ref.slice(0, ref.indexOf(".")) : ref;
|
||||
const seen = new Set<string>();
|
||||
for (const j of joins) {
|
||||
if (promotedNames.has(j.from) && promotedNames.has(j.to)) {
|
||||
lines.push(` ${nodeId(j.from)} --> ${nodeId(j.to)}`);
|
||||
}
|
||||
const a = owningTable(j.from);
|
||||
const b = owningTable(j.to);
|
||||
if (a === b || !tableNames.has(a) || !tableNames.has(b)) continue;
|
||||
const key = [a, b].sort().join("::");
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
lines.push(` ${sanitize(a)} }o--o{ ${sanitize(b)} : join`);
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
@@ -89,7 +102,7 @@ export function SchemaLinkingViewer({ linking }: { linking: SchemaLinking }) {
|
||||
useEffect(() => {
|
||||
if (view !== "chart" || oversized) return;
|
||||
let cancelled = false;
|
||||
const def = buildFlowchart(promoted, linking.joins);
|
||||
const def = buildErDiagram(promoted, linking.joins);
|
||||
renderMermaid(def).then((s) => {
|
||||
if (!cancelled) setSvg(s);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user