126 lines
3.5 KiB
TypeScript
126 lines
3.5 KiB
TypeScript
import { useMemo } from "react";
|
|
import codeCache from "@/lib/cache/codeCache";
|
|
|
|
/**
|
|
* 엔티티 조인 최적화 훅
|
|
* 테이블 간의 관계를 분석하여 최적화된 조인 전략을 제공합니다.
|
|
*/
|
|
|
|
interface JoinOptimization {
|
|
strategy: "eager" | "lazy" | "batch";
|
|
priority: number;
|
|
estimatedCost: number;
|
|
}
|
|
|
|
interface EntityRelation {
|
|
fromTable: string;
|
|
toTable: string;
|
|
joinType: "inner" | "left" | "right" | "full";
|
|
cardinality: "one-to-one" | "one-to-many" | "many-to-one" | "many-to-many";
|
|
}
|
|
|
|
export const useEntityJoinOptimization = (
|
|
relations: EntityRelation[],
|
|
queryContext?: {
|
|
expectedResultSize?: number;
|
|
performanceProfile?: "fast" | "balanced" | "memory-efficient";
|
|
},
|
|
) => {
|
|
const optimization = useMemo(() => {
|
|
const cacheKey = `join-optimization:${JSON.stringify(relations)}:${JSON.stringify(queryContext)}`;
|
|
|
|
// 캐시에서 먼저 확인
|
|
const cached = codeCache.get(cacheKey);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
|
|
// 최적화 전략 계산
|
|
const optimizations: Record<string, JoinOptimization> = {};
|
|
|
|
relations.forEach((relation) => {
|
|
const key = `${relation.fromTable}-${relation.toTable}`;
|
|
|
|
// 카디널리티에 따른 기본 전략
|
|
let strategy: JoinOptimization["strategy"] = "eager";
|
|
let priority = 1;
|
|
let estimatedCost = 1;
|
|
|
|
switch (relation.cardinality) {
|
|
case "one-to-one":
|
|
strategy = "eager";
|
|
priority = 3;
|
|
estimatedCost = 1;
|
|
break;
|
|
case "one-to-many":
|
|
strategy = "lazy";
|
|
priority = 2;
|
|
estimatedCost = 2;
|
|
break;
|
|
case "many-to-one":
|
|
strategy = "eager";
|
|
priority = 2;
|
|
estimatedCost = 1.5;
|
|
break;
|
|
case "many-to-many":
|
|
strategy = "batch";
|
|
priority = 1;
|
|
estimatedCost = 3;
|
|
break;
|
|
}
|
|
|
|
// 성능 프로필에 따른 조정
|
|
if (queryContext?.performanceProfile === "fast") {
|
|
if (strategy === "lazy") strategy = "eager";
|
|
priority += 1;
|
|
} else if (queryContext?.performanceProfile === "memory-efficient") {
|
|
if (strategy === "eager") strategy = "lazy";
|
|
estimatedCost *= 0.8;
|
|
}
|
|
|
|
// 예상 결과 크기에 따른 조정
|
|
if (queryContext?.expectedResultSize && queryContext.expectedResultSize > 1000) {
|
|
if (strategy === "eager") strategy = "batch";
|
|
estimatedCost *= 1.2;
|
|
}
|
|
|
|
optimizations[key] = {
|
|
strategy,
|
|
priority,
|
|
estimatedCost,
|
|
};
|
|
});
|
|
|
|
// 결과를 캐시에 저장 (1분 TTL)
|
|
codeCache.set(cacheKey, optimizations, 60 * 1000);
|
|
|
|
return optimizations;
|
|
}, [relations, queryContext]);
|
|
|
|
const getOptimizationForRelation = (fromTable: string, toTable: string): JoinOptimization | null => {
|
|
const key = `${fromTable}-${toTable}`;
|
|
return optimization[key] || null;
|
|
};
|
|
|
|
const getSortedRelations = (): Array<{ relation: EntityRelation; optimization: JoinOptimization }> => {
|
|
return relations
|
|
.map((relation) => ({
|
|
relation,
|
|
optimization: getOptimizationForRelation(relation.fromTable, relation.toTable)!,
|
|
}))
|
|
.filter((item) => item.optimization)
|
|
.sort((a, b) => b.optimization.priority - a.optimization.priority);
|
|
};
|
|
|
|
const getTotalEstimatedCost = (): number => {
|
|
return Object.values(optimization).reduce((total, opt) => total + opt.estimatedCost, 0);
|
|
};
|
|
|
|
return {
|
|
optimization,
|
|
getOptimizationForRelation,
|
|
getSortedRelations,
|
|
getTotalEstimatedCost,
|
|
};
|
|
};
|