94 lines
2.6 KiB
JavaScript
94 lines
2.6 KiB
JavaScript
// ═══════════════════════════════════════════════════════════════════════════
|
||
// 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<string, number>} - 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();
|
||
}
|