From 21f243488f0c1e1cbf1fb3eaaa6f71a26142fbce Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 23 Aug 2026 22:00:29 +0000 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9A=A1=20Bolt:=20[=EC=84=B1=EB=8A=A5=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0]=20Prisma=20=EB=82=B4=EB=B3=B4=EB=82=B4?= =?UTF-8?q?=EA=B8=B0=20=EC=97=A3=EC=A7=80=20=EA=B4=80=EA=B3=84=20O(1)=20Ma?= =?UTF-8?q?p=20=EC=A1=B0=ED=9A=8C=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace O(N * C * E) nested array search `edgesProcessed` loop with O(1) `edgeRelationsByModelAndField` Map lookup during Prisma string construction. - Document performance learning in `.jules/bolt.md`. --- .jules/bolt.md | 3 +++ frontend/src/erd/prisma.ts | 22 ++++++++++------------ pr_body.txt | 15 --------------- 3 files changed, 13 insertions(+), 27 deletions(-) delete mode 100644 pr_body.txt diff --git a/.jules/bolt.md b/.jules/bolt.md index f1a8c1466..765b953e8 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -77,3 +77,6 @@ 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-05-18 - [Optimize Prisma Export Edge Lookups] +**Learning:** Prisma 내보내기 시 모든 노드의 모든 컬럼을 순회하며 `edgesProcessed` 배열을 찾는 과정에서 `O(N * C * E)` 성능 병목이 발생했습니다. +**Action:** 엣지 순회를 반복하는 대신 모델과 필드를 키로 사용하는 `O(1)` Map 조회를 미리 계산하여 성능을 개선합니다. diff --git a/frontend/src/erd/prisma.ts b/frontend/src/erd/prisma.ts index 211dfdd8d..b30a2e609 100644 --- a/frontend/src/erd/prisma.ts +++ b/frontend/src/erd/prisma.ts @@ -59,7 +59,8 @@ export function exportPrisma( const fkNodeColumnPairs = new Set(); const fkNodesWithoutHandles = new Set(); const incomingRelationsByNode = new Map>(); - const edgesProcessed = new Map(); + // ⚡ Bolt: Precompute edge relations by model and field to avoid O(N * C * E) nested loop lookups + const edgeRelationsByModelAndField = new Map(); for (const edge of edges) { const sourceNode = nodesById.get(edge.source); @@ -93,11 +94,9 @@ export function exportPrisma( }); incomingRelationsByNode.set(edge.target, relList); - edgesProcessed.set(edge.id, { - sourceModel: sanitizeName(sourceNode.data.title), + edgeRelationsByModelAndField.set(`${sanitizeName(sourceNode.data.title)}:${sanitizeName(sourceField)}`, { targetModel: sanitizeName(targetNode.data.title), - sourceFields: [sanitizeName(sourceField)], - targetFields: [sanitizeName(targetField)], + targetField: sanitizeName(targetField), relationName: relName }); } @@ -136,13 +135,12 @@ 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 relField = sanitizeName(edgeInfo.targetModel) + "_" + fieldName; - relationDef = `\n ${relField} ${edgeInfo.targetModel}${optional} @relation("${edgeInfo.relationName}", fields: [${fieldName}], references: [${edgeInfo.targetFields[0]}])`; - } + const edgeInfo = edgeRelationsByModelAndField.get(`${modelName}:${fieldName}`); + if (edgeInfo) { + // 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 relField = sanitizeName(edgeInfo.targetModel) + "_" + fieldName; + relationDef = `\n ${relField} ${edgeInfo.targetModel}${optional} @relation("${edgeInfo.relationName}", fields: [${fieldName}], references: [${edgeInfo.targetField}])`; } output += ` ${fieldName} ${prismaType}${optional}${attributes}${relationDef}\n`; diff --git a/pr_body.txt b/pr_body.txt deleted file mode 100644 index 0216bbe06..000000000 --- a/pr_body.txt +++ /dev/null @@ -1,15 +0,0 @@ -💡 What -- ExportModal에 DBML 다운로드 버튼 추가 -- 메인 화면에 있던 독립된 DBML 다운로드 버튼을 제거하고, ExportModal 안으로 통합하였습니다. -- 관련된 통합테스트 및 유닛 테스트 업데이트 (`App.coverage.test.tsx`, `ExportModal.test.tsx`) - -🎯 Why -- 기존에 분리되어 있던 DBML 내보내기 버튼을 모달 안으로 통합하여 사용자 인터페이스의 일관성을 높였습니다. -- SVG, PlantUML 등 다른 파일 형식과 동일한 경험을 제공합니다. - -📸 Before/After -- Before: 메인 도구 모음에 `DBML 내보내기` 버튼이 존재 -- After: `공유 및 내보내기` 모달 안 `산출물` 목록에 DBML 항목으로 이동됨 - -♿ Accessibility -- 버튼 및 다이얼로그의 기존 접근성 속성을 그대로 유지하고 동일한 규칙을 따라 작성했습니다. From 3f1c732d7e0b5538a6e75199d0aebcc8fe78c3d7 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 23 Aug 2026 22:38:34 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9A=A1=20Bolt:=20[=EC=84=B1=EB=8A=A5=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0]=20Prisma=20=EB=82=B4=EB=B3=B4=EB=82=B4?= =?UTF-8?q?=EA=B8=B0=20=EC=97=A3=EC=A7=80=20=EA=B4=80=EA=B3=84=20O(1)=20Ma?= =?UTF-8?q?p=20=EC=A1=B0=ED=9A=8C=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace O(N * C * E) nested array search `edgesProcessed` loop with O(1) `edgeRelationsByModelAndField` Map lookup during Prisma string construction. - Document performance learning in `.jules/bolt.md`. --- pr_body.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 pr_body.txt diff --git a/pr_body.txt b/pr_body.txt new file mode 100644 index 000000000..0216bbe06 --- /dev/null +++ b/pr_body.txt @@ -0,0 +1,15 @@ +💡 What +- ExportModal에 DBML 다운로드 버튼 추가 +- 메인 화면에 있던 독립된 DBML 다운로드 버튼을 제거하고, ExportModal 안으로 통합하였습니다. +- 관련된 통합테스트 및 유닛 테스트 업데이트 (`App.coverage.test.tsx`, `ExportModal.test.tsx`) + +🎯 Why +- 기존에 분리되어 있던 DBML 내보내기 버튼을 모달 안으로 통합하여 사용자 인터페이스의 일관성을 높였습니다. +- SVG, PlantUML 등 다른 파일 형식과 동일한 경험을 제공합니다. + +📸 Before/After +- Before: 메인 도구 모음에 `DBML 내보내기` 버튼이 존재 +- After: `공유 및 내보내기` 모달 안 `산출물` 목록에 DBML 항목으로 이동됨 + +♿ Accessibility +- 버튼 및 다이얼로그의 기존 접근성 속성을 그대로 유지하고 동일한 규칙을 따라 작성했습니다. From 746123180ed7b8cd727bf21678f32e7cbba8286e Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sun, 23 Aug 2026 23:03:50 +0000 Subject: [PATCH 3/4] =?UTF-8?q?=E2=9A=A1=20Bolt:=20[=EC=84=B1=EB=8A=A5=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0]=20Prisma=20=EB=82=B4=EB=B3=B4=EB=82=B4?= =?UTF-8?q?=EA=B8=B0=20=EC=97=A3=EC=A7=80=20=EA=B4=80=EA=B3=84=20O(1)=20Ma?= =?UTF-8?q?p=20=EC=A1=B0=ED=9A=8C=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace O(N * C * E) nested array search `edgesProcessed` loop with O(1) `edgeRelationsByModelAndField` Map lookup during Prisma string construction. - Document performance learning in `.jules/bolt.md`. From ff83b1aa67996c005a61b66d39faf1f0abdd5194 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 24 Aug 2026 00:07:59 +0000 Subject: [PATCH 4/4] =?UTF-8?q?=E2=9A=A1=20Bolt:=20[=EC=84=B1=EB=8A=A5=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0]=20Prisma=20=EB=82=B4=EB=B3=B4=EB=82=B4?= =?UTF-8?q?=EA=B8=B0=20=EC=97=A3=EC=A7=80=20=EA=B4=80=EA=B3=84=20O(1)=20Ma?= =?UTF-8?q?p=20=EC=A1=B0=ED=9A=8C=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace O(N * C * E) nested array search `edgesProcessed` loop with O(1) `edgeRelationsByModelAndField` Map lookup during Prisma string construction. - Document performance learning in `.jules/bolt.md`. --- pr_body.txt | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 pr_body.txt diff --git a/pr_body.txt b/pr_body.txt deleted file mode 100644 index 0216bbe06..000000000 --- a/pr_body.txt +++ /dev/null @@ -1,15 +0,0 @@ -💡 What -- ExportModal에 DBML 다운로드 버튼 추가 -- 메인 화면에 있던 독립된 DBML 다운로드 버튼을 제거하고, ExportModal 안으로 통합하였습니다. -- 관련된 통합테스트 및 유닛 테스트 업데이트 (`App.coverage.test.tsx`, `ExportModal.test.tsx`) - -🎯 Why -- 기존에 분리되어 있던 DBML 내보내기 버튼을 모달 안으로 통합하여 사용자 인터페이스의 일관성을 높였습니다. -- SVG, PlantUML 등 다른 파일 형식과 동일한 경험을 제공합니다. - -📸 Before/After -- Before: 메인 도구 모음에 `DBML 내보내기` 버튼이 존재 -- After: `공유 및 내보내기` 모달 안 `산출물` 목록에 DBML 항목으로 이동됨 - -♿ Accessibility -- 버튼 및 다이얼로그의 기존 접근성 속성을 그대로 유지하고 동일한 규칙을 따라 작성했습니다.