fromis_9/backend/services/meilisearch.js

188 lines
5.2 KiB
JavaScript
Raw Normal View History

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",
"member_names",
"description",
"source_name",
"category_name",
]);
// 필터링 가능한 필드 설정
await index.updateFilterableAttributes(["category_id", "date"]);
// 정렬 가능한 필드 설정
await index.updateSortableAttributes(["date", "time"]);
// 랭킹 규칙 설정 (동일 유사도일 때 최신 날짜 우선)
await index.updateRankingRules([
"words", // 검색어 포함 개수
"typo", // 오타 수
"proximity", // 검색어 간 거리
"attribute", // 필드 우선순위
"exactness", // 정확도
"date:desc", // 동일 유사도 시 최신 날짜 우선
]);
// 오타 허용 설정 (typo tolerance)
await index.updateTypoTolerance({
enabled: true,
minWordSizeForTypos: {
oneTypo: 2,
twoTypos: 4,
},
});
// 페이징 설정 (기본 1000개 제한 해제)
await index.updatePagination({
maxTotalHits: 10000, // 최대 10000개까지 조회 가능
});
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 || 1000, // 기본 1000개 (Meilisearch 최대)
offset: options.offset || 0, // 페이징용 offset
attributesToRetrieve: ["*"],
};
// 카테고리 필터
if (options.categoryId) {
searchOptions.filter = `category_id = ${options.categoryId}`;
}
// 정렬 지정 시에만 적용 (기본은 유사도순)
if (options.sort) {
searchOptions.sort = options.sort;
}
const results = await index.search(query, searchOptions);
// 페이징 정보 포함 반환
return {
hits: results.hits,
total: results.estimatedTotalHits, // 전체 결과 수
offset: searchOptions.offset,
limit: searchOptions.limit,
};
} catch (error) {
console.error("[Meilisearch] 검색 오류:", error.message);
return { hits: [], total: 0, offset: 0, limit: 0 };
}
}
/**
* 모든 일정 동기화 (초기 데이터 로드용)
*/
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 };