// ═══════════════════════════════════════════════════════════════════════════ // MiniSearch Index Manager // 全文搜索索引管理 // ═══════════════════════════════════════════════════════════════════════════ import MiniSearch from '../../../libs/minisearch.mjs'; // 索引缓存:chatId -> { index, updatedAt } const indexCache = new Map(); /** * 获取或创建搜索索引 * @param {string} chatId * @param {Array} events - L2 事件 * @param {number} storeUpdatedAt - store.updatedAt 时间戳 * @returns {MiniSearch} */ export function getSearchIndex(chatId, events, storeUpdatedAt) { const cached = indexCache.get(chatId); // 缓存有效 if (cached && cached.updatedAt >= storeUpdatedAt) { return cached.index; } // 重建索引 const index = new MiniSearch({ fields: ['title', 'summary', 'participants'], storeFields: ['id'], searchOptions: { boost: { title: 2, participants: 1.5, summary: 1 }, fuzzy: 0.2, prefix: true, }, }); // 索引事件 const docs = events.map(e => ({ id: e.id, title: e.title || '', summary: e.summary || '', participants: (e.participants || []).join(' '), })); index.addAll(docs); // 缓存 indexCache.set(chatId, { index, updatedAt: storeUpdatedAt }); // 限制缓存数量 if (indexCache.size > 5) { const firstKey = indexCache.keys().next().value; indexCache.delete(firstKey); } return index; } /** * 关键词搜索 * @returns {Map} - eventId -> 归一化分数 */ export function searchByKeywords(index, queryText, limit = 20) { if (!queryText?.trim()) return new Map(); const results = index.search(queryText, { limit }); if (results.length === 0) return new Map(); // 归一化分数到 0-1 const maxScore = results[0].score; const scores = new Map(); for (const r of results) { scores.set(r.id, r.score / maxScore); } return scores; } /** * 清除指定聊天的索引缓存 */ export function invalidateIndex(chatId) { indexCache.delete(chatId); } /** * 清除所有索引缓存 */ export function clearAllIndexes() { indexCache.clear(); }