diff --git a/backend/src/services/suggestions/index.js b/backend/src/services/suggestions/index.js index e909207..fcea5d2 100644 --- a/backend/src/services/suggestions/index.js +++ b/backend/src/services/suggestions/index.js @@ -15,8 +15,8 @@ const inko = new Inko(); const CONFIG = { // 추천 검색어 최소 검색 횟수 비율 (최대 대비) MIN_COUNT_RATIO: 0.01, - // 최소 임계값 (데이터 적을 때) - MIN_COUNT_FLOOR: 5, + // 최소 임계값 (데이터 적을 때 오타 방지) + MIN_COUNT_FLOOR: 10, // Redis 키 prefix REDIS_PREFIX: 'suggest:', // 캐시 TTL (초) @@ -24,7 +24,6 @@ const CONFIG = { PREFIX: 3600, // prefix 검색: 1시간 BIGRAM: 86400, // bi-gram: 24시간 POPULAR: 600, // 인기 검색어: 10분 - MAX_COUNT: 3600, // 최대 횟수: 1시간 }, }; @@ -208,6 +207,7 @@ export class SuggestionService { /** * Prefix 매칭 + * - GREATEST()로 동적 임계값 적용: MAX(count) * 1% 또는 최소 10 중 더 큰 값 */ async getPrefixSuggestions(prefix, koreanPrefix, limit) { try { @@ -216,19 +216,21 @@ export class SuggestionService { if (koreanPrefix) { // 영어 + 한글 변환 둘 다 검색 [rows] = await this.db.query( - `SELECT query, count FROM suggestion_queries - WHERE query LIKE ? OR query LIKE ? + `SELECT query FROM suggestion_queries + WHERE (query LIKE ? OR query LIKE ?) + AND count >= GREATEST((SELECT MAX(count) * ? FROM suggestion_queries), ?) ORDER BY count DESC, last_searched_at DESC LIMIT ?`, - [`${prefix}%`, `${koreanPrefix}%`, limit] + [`${prefix}%`, `${koreanPrefix}%`, CONFIG.MIN_COUNT_RATIO, CONFIG.MIN_COUNT_FLOOR, limit] ); } else { [rows] = await this.db.query( - `SELECT query, count FROM suggestion_queries + `SELECT query FROM suggestion_queries WHERE query LIKE ? + AND count >= GREATEST((SELECT MAX(count) * ? FROM suggestion_queries), ?) ORDER BY count DESC, last_searched_at DESC LIMIT ?`, - [`${prefix}%`, limit] + [`${prefix}%`, CONFIG.MIN_COUNT_RATIO, CONFIG.MIN_COUNT_FLOOR, limit] ); } @@ -241,15 +243,17 @@ export class SuggestionService { /** * 초성 검색 + * - GREATEST()로 동적 임계값 적용 */ async getChosungSuggestions(chosung, limit) { try { const [rows] = await this.db.query( - `SELECT word, count FROM suggestion_chosung + `SELECT word FROM suggestion_chosung WHERE chosung LIKE ? + AND count >= GREATEST((SELECT MAX(count) * ? FROM suggestion_chosung), ?) ORDER BY count DESC LIMIT ?`, - [`${chosung}%`, limit] + [`${chosung}%`, CONFIG.MIN_COUNT_RATIO, CONFIG.MIN_COUNT_FLOOR, limit] ); return rows.map(r => r.word); @@ -261,6 +265,7 @@ export class SuggestionService { /** * 인기 검색어 조회 + * - GREATEST()로 동적 임계값 적용 */ async getPopularQueries(limit = 10) { try { @@ -272,12 +277,13 @@ export class SuggestionService { return JSON.parse(cached); } - // DB 조회 + // DB 조회 (동적 임계값 이상만) const [rows] = await this.db.query( - `SELECT query, count FROM suggestion_queries + `SELECT query FROM suggestion_queries + WHERE count >= GREATEST((SELECT MAX(count) * ? FROM suggestion_queries), ?) ORDER BY count DESC LIMIT ?`, - [limit] + [CONFIG.MIN_COUNT_RATIO, CONFIG.MIN_COUNT_FLOOR, limit] ); const result = rows.map(r => r.query);