import { MeiliSearch } from "meilisearch"; // Meilisearch 클라이언트 초기화 const client = new MeiliSearch({ host: "http://fromis9-meilisearch:7700", apiKey: process.env.MEILI_MASTER_KEY, }); const SCHEDULE_INDEX = "schedules"; /** * 인덱스 초기화 및 설정 */ export async function initMeilisearch() { try { // 인덱스 생성 (이미 존재하면 무시) await client.createIndex(SCHEDULE_INDEX, { primaryKey: "id" }); // 인덱스 설정 const index = client.index(SCHEDULE_INDEX); // 검색 가능한 필드 설정 await index.updateSearchableAttributes([ "title", "description", "member_names", "category_name", "source_name", ]); // 필터링 가능한 필드 설정 await index.updateFilterableAttributes(["category_id", "date"]); // 정렬 가능한 필드 설정 await index.updateSortableAttributes(["date", "time"]); // 오타 허용 설정 (typo tolerance) await index.updateTypoTolerance({ enabled: true, minWordSizeForTypos: { oneTypo: 2, twoTypos: 4, }, }); console.log("[Meilisearch] 인덱스 초기화 완료"); } catch (error) { console.error("[Meilisearch] 초기화 오류:", error.message); } } /** * 일정 문서 추가/업데이트 */ export async function addOrUpdateSchedule(schedule) { try { const index = client.index(SCHEDULE_INDEX); // 멤버 이름을 문자열로 변환 const memberNames = schedule.members ? schedule.members.map((m) => m.name).join(" ") : ""; const document = { id: schedule.id, title: schedule.title, description: schedule.description || "", date: schedule.date, time: schedule.time || "", category_id: schedule.category_id, category_name: schedule.category_name || "", category_color: schedule.category_color || "", source_name: schedule.source_name || "", source_url: schedule.source_url || "", member_names: memberNames, }; await index.addDocuments([document]); console.log(`[Meilisearch] 일정 추가/업데이트: ${schedule.id}`); } catch (error) { console.error("[Meilisearch] 문서 추가 오류:", error.message); } } /** * 일정 문서 삭제 */ export async function deleteSchedule(scheduleId) { try { const index = client.index(SCHEDULE_INDEX); await index.deleteDocument(scheduleId); console.log(`[Meilisearch] 일정 삭제: ${scheduleId}`); } catch (error) { console.error("[Meilisearch] 문서 삭제 오류:", error.message); } } /** * 일정 검색 */ export async function searchSchedules(query, options = {}) { try { const index = client.index(SCHEDULE_INDEX); const searchOptions = { limit: options.limit || 50, attributesToRetrieve: ["*"], }; // 카테고리 필터 if (options.categoryId) { searchOptions.filter = `category_id = ${options.categoryId}`; } // 정렬 (기본: 날짜 내림차순) searchOptions.sort = options.sort || ["date:desc", "time:desc"]; const results = await index.search(query, searchOptions); return results.hits; } catch (error) { console.error("[Meilisearch] 검색 오류:", error.message); return []; } } /** * 모든 일정 동기화 (초기 데이터 로드용) */ export async function syncAllSchedules(schedules) { try { const index = client.index(SCHEDULE_INDEX); // 기존 문서 모두 삭제 await index.deleteAllDocuments(); // 문서 변환 const documents = schedules.map((schedule) => ({ id: schedule.id, title: schedule.title, description: schedule.description || "", date: schedule.date, time: schedule.time || "", category_id: schedule.category_id, category_name: schedule.category_name || "", category_color: schedule.category_color || "", source_name: schedule.source_name || "", source_url: schedule.source_url || "", member_names: schedule.member_names || "", })); // 일괄 추가 await index.addDocuments(documents); console.log(`[Meilisearch] ${documents.length}개 일정 동기화 완료`); return documents.length; } catch (error) { console.error("[Meilisearch] 동기화 오류:", error.message); return 0; } } export { client };