From c75a8bf41c9ea8459c6fbab37d1ee7aa2beb8739 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 13 Oct 2026 18:08:37 +0000 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20Prisma=20Exp?= =?UTF-8?q?ort=20Dictionary=20Complexity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/bolt.md | 4 ++++ frontend/src/erd/prisma.ts | 21 ++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index f1a8c1466..7e139790e 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -77,3 +77,7 @@ Optimized metric route processing to O(N) by creating a mapping of routes direct ## 2024-07-13 - [Optimize Export Dictionary FK lookups] **Learning:** Found O(N * C * E) performance bottleneck in ERD export dictionaries due to repeated array searching with `edges.some()` inside a nested loop over nodes and columns. **Action:** Replace repeated linear array scans for edges by precomputing O(1) Set lookups of foreign key column handles per node before looping. + +## 2024-10-13 - Optimize O(N * C * E) Complexity in Prisma Export Dictionary +**Learning:** Nested array iterations (`for (const [_, edgeInfo] of edgesProcessed)`) inside loops parsing graph models and fields (`node.data.columns`) result in O(N * C * E) complexity, creating significant bottlenecks during Prisma schema export for large graphs. +**Action:** Replace repeated linear edge evaluations by precomputing an O(1) Lookup Map (`edgesProcessedBySource.set(sourceKey, edgeInfo)`) before looping over fields, bringing complexity down to O(E + N * C). diff --git a/frontend/src/erd/prisma.ts b/frontend/src/erd/prisma.ts index 211dfdd8d..59bed5fa6 100644 --- a/frontend/src/erd/prisma.ts +++ b/frontend/src/erd/prisma.ts @@ -60,6 +60,8 @@ export function exportPrisma( const fkNodesWithoutHandles = new Set(); const incomingRelationsByNode = new Map>(); const edgesProcessed = new Map(); + // ⚡ Bolt: Precompute edge lookups by sourceModel+sourceField to avoid O(N*C*E) loops + const edgesProcessedBySource = new Map>(); for (const edge of edges) { const sourceNode = nodesById.get(edge.source); @@ -93,13 +95,19 @@ export function exportPrisma( }); incomingRelationsByNode.set(edge.target, relList); - edgesProcessed.set(edge.id, { + const edgeInfo = { sourceModel: sanitizeName(sourceNode.data.title), targetModel: sanitizeName(targetNode.data.title), sourceFields: [sanitizeName(sourceField)], targetFields: [sanitizeName(targetField)], relationName: relName - }); + }; + edgesProcessed.set(edge.id, edgeInfo); + + const sourceKey = `${edgeInfo.sourceModel}:${edgeInfo.sourceFields[0]}`; + const fieldEdges = edgesProcessedBySource.get(sourceKey) || []; + fieldEdges.push(edgeInfo); + edgesProcessedBySource.set(sourceKey, fieldEdges); } } @@ -136,12 +144,11 @@ export function exportPrisma( // Determine if there is a relation defined on this field let relationDef = ""; - for (const [_, edgeInfo] of edgesProcessed) { - if (edgeInfo.sourceModel === modelName && edgeInfo.sourceFields.includes(fieldName)) { - // This field is a foreign key, but in Prisma, we typically define the relation object field - // alongside the scalar field. We will add the relation object field here. + const fieldEdges = edgesProcessedBySource.get(`${modelName}:${fieldName}`); + if (fieldEdges) { + for (const edgeInfo of fieldEdges) { const relField = sanitizeName(edgeInfo.targetModel) + "_" + fieldName; - relationDef = `\n ${relField} ${edgeInfo.targetModel}${optional} @relation("${edgeInfo.relationName}", fields: [${fieldName}], references: [${edgeInfo.targetFields[0]}])`; + relationDef += `\n ${relField} ${edgeInfo.targetModel}${optional} @relation("${edgeInfo.relationName}", fields: [${fieldName}], references: [${edgeInfo.targetFields[0]}])`; } } From 696784754d081662912a39462795f93164facb7f Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 13 Oct 2026 18:09:52 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20Prisma=20Exp?= =?UTF-8?q?ort=20Dictionary=20Complexity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 8eda59ae491a96489e92b3e230801a1d840ad40c Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 13 Oct 2026 18:12:04 +0000 Subject: [PATCH 3/4] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20Prisma=20Exp?= =?UTF-8?q?ort=20Dictionary=20Complexity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 1c7663d3d65dadb318c56edb3519ca65fc21ee37 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 13 Oct 2026 18:14:19 +0000 Subject: [PATCH 4/4] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20Prisma=20Exp?= =?UTF-8?q?ort=20Dictionary=20Complexity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit