将 flexsearch 与 NextJs 结合使用来构建搜索 API

问题描述 投票:0回答:1

我正在尝试构建一个用于使用 NextJsFlexsearch 进行搜索的 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 后,就开始出现这种情况。

我做错了什么?

next.js13 flexsearch
1个回答
© www.soinside.com 2019 - 2024. All rights reserved.