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 -- 버튼 및 다이얼로그의 기존 접근성 속성을 그대로 유지하고 동일한 규칙을 따라 작성했습니다.