我正在尝试构建一个用于使用 NextJs 和 Flexsearch 进行搜索的 API。
有一个索引器任务可以扫描整个站点并将索引存储在磁盘上。当 NextJs 启动时,会读取这些文件以在内存中保留文档索引。我们公开了一个
/api/search
端点,我们可以在其中搜索上一个文档索引。
这是每个部分的相关代码,不包括目前不感兴趣的索引器。
此代码在内存上创建文档索引:
const documentIndex = new Document({
tokenize: 'full',
document: {
id: 'id',
index: ['title', 'section', 'content'],
store: true
}
});
function buildIndex() {
const keys = fs
.readdirSync('search/data/', { withFileTypes: true })
.filter((item) => !item.isDirectory())
.map((item) => item.name.split('.').slice(0, -1).join('.'));
for (let i = 0, key; i < keys.length; i += 1) {
key = keys[i];
const data = fs.readFileSync(`search/data/${key}.json`, 'utf8');
documentIndex.import(key, data ?? null);
}
return documentIndex;
}
然后 API 端点就是这样实现的:
import { NextApiRequest, NextApiResponse } from 'next';
const { serverRuntimeConfig } = getConfig();
import { EnrichedDocumentSearchResultSetUnit } from 'flexsearch';
import getConfig from 'next/config';
const maxContentResultsPerPage = 3;
const maxAgeSeconds = 86400; // 1 day
export interface Section {
id: number;
path: string;
sectionPath: string;
title: string;
section: string;
content: string;
}
export interface Page {
id: number;
path: string;
title: string;
content: string;
sections: Section[];
}
function getResults(query: string, fieldNames: string[]) {
const results = serverRuntimeConfig.documentIndex.search(query, {
index: fieldNames,
enrich: true
}) as EnrichedDocumentSearchResultSetUnit<Section>[];
return results.pop()?.result.map((value) => {
return {
id: value.doc.id,
title: value.doc.title,
section: value.doc.section,
sectionPath: value.doc.sectionPath,
path: value.doc.path,
content: value.doc.content
};
});
}
const filterSections = (sections: Section[]): Section[] => {
const visitedSections: Record<string, Section> = {};
const sectionsOrder: string[] = [];
const finalSections: Section[] = [];
sections.forEach((entry) => {
if (entry.section in visitedSections) {
if (entry.id < visitedSections[entry.section].id) {
visitedSections[entry.section] = entry;
}
} else {
visitedSections[entry.section] = entry;
sectionsOrder.push(entry.section);
}
});
sectionsOrder.forEach((section) => {
if (finalSections.length < maxContentResultsPerPage) {
const entry = visitedSections[section];
finalSections.push(entry);
}
});
return finalSections;
};
const searchHandler = async (req: NextApiRequest, res: NextApiResponse): Promise<void> => {
const titleResults = getResults(req.query.q as string, ['title']);
const sectionResults = getResults(req.query.q as string, ['section', 'content']);
const visitedTitles: Record<string, Section> = {};
const titleOrder: string[] = [];
const results: Page[] = [];
const added: number[] = [];
const pages: Record<string, Page> = {};
const pageOrder: string[] = [];
if (titleResults !== undefined) {
titleResults.forEach((entry) => {
if (visitedTitles[entry.title] !== undefined) {
if (entry.id < visitedTitles[entry.title].id) {
visitedTitles[entry.title] = entry;
}
} else {
visitedTitles[entry.title] = entry;
titleOrder.push(entry.title);
}
});
titleOrder.forEach((title) => {
const entry = visitedTitles[title];
if (added.indexOf(entry.id) === -1) {
pageOrder.push(title);
pages[title] = {
id: entry.id,
title: entry.title,
content: entry.content,
path: entry.path,
sections: []
};
added.push(entry.id);
}
});
}
if (sectionResults !== undefined) {
sectionResults.forEach((entry) => {
if (added.indexOf(entry.id) === -1) {
if (entry.title in pages) {
pages[entry.title].sections.push(entry);
} else {
pages[entry.title] = {
id: entry.id,
title: entry.title,
content: entry.content,
path: entry.path,
sections: []
};
pageOrder.push(entry.title);
}
}
});
}
pageOrder.forEach((title) => {
const page = pages[title];
page.sections = filterSections(page.sections);
results.push(pages[title]);
});
res.setHeader('Cache-Control', `max-age: ${maxAgeSeconds}`);
res.status(200).json(results);
};
export default searchHandler;
代码可以工作,但 Typescript 抱怨
EnrichedDocumentSearchResultSetUnit
。
将
flexsearch
升级到 0.7.3 后,就开始出现这种情况。
我做错了什么?