跳至內容
Fumadocs

Meilisearch

將 Meilisearch 與 Fumadocs 整合

Meilisearch 是一款開源搜尋引擎,支援錯字容忍度與篩選功能。您可以自行架設,也可以使用 Meilisearch Cloud。

設定

安裝相依套件:

npm install meilisearch

API 金鑰

請透過自行架設或使用 Meilisearch Cloud 啟動 Meilisearch 執行個體,並將其認證資訊儲存在 .env.local 中 (在 Vite 框架中請將 NEXT_PUBLIC_ 改為 VITE_,在 Astro 中則改為 PUBLIC_) :

NEXT_PUBLIC_MEILISEARCH_HOST="URL of your instance"
NEXT_PUBLIC_MEILISEARCH_SEARCH_KEY="search API key"
MEILISEARCH_ADMIN_KEY="Default Admin API Key"

搜尋金鑰會暴露在瀏覽器中,因此請建立一組只能搜尋 docs 索引的金鑰。管理員金鑰則請妥善保管,切勿公開。

同步文件

預先算繪 static.json 路由,在建置時匯出搜尋文件,並於每次建置後透過指令碼將其同步至 Meilisearch:

app/static.json/route.ts
import { source } from '@/lib/source';
import { toDocuments } from 'fumadocs-core/search/meilisearch';

export const revalidate = false;

export async function GET() {
  return Response.json(await toDocuments(source));
}
scripts/sync-content.ts
import * as fs from 'node:fs/promises';
import { type DocumentRecord, sync } from 'fumadocs-core/search/meilisearch';
import { Meilisearch } from 'meilisearch';

// Next.js writes it under `route-cache` when built with an adapter, like on Vercel
const [file] = await Array.fromAsync(fs.glob('.next/server/{app,route-cache/*/*/$}/static.json.body'));
const records = JSON.parse(await fs.readFile(file, 'utf-8')) as DocumentRecord[];

const client = new Meilisearch({
  host: process.env.NEXT_PUBLIC_MEILISEARCH_HOST!,
  apiKey: process.env.MEILISEARCH_ADMIN_KEY!,
});

await sync(client, {
  indexName: 'docs',
  documents: records,
});

console.log(`search updated: ${records.length} records`);
package.json
{
  "scripts": {
    "build": "next build && node --env-file-if-exists=.env.local scripts/sync-content.ts"
  }
}

sync() 會設定索引並取代其中的文件;在新文件完成索引前,舊文件仍可供搜尋。build 指令碼會在每次建置時同步,請在您的部署平台上設定相同的變數。

搜尋 UI

實作 UI 時,您可以考慮以下幾種方式:

  • 使用 Fumadocs UI 搜尋對話方塊。

  • 使用內建的搜尋用戶端 hook 自行打造:

    import {  } from 'meilisearch';
    import {  } from 'fumadocs-core/search/client';
    
    const  = new ({
      : 'host url',
      : 'search api key',
    });
    
    const { , , ,  } = ({
      ,
      : 'docs',
    });

    屬性

    型別

  • 直接使用 Meilisearch 的官方用戶端。

選項

標籤篩選

若要設定標籤篩選,請在 static.json 路由中將 tag 傳入 toDocuments():

toDocuments(source, { tag: (page) => page.slugs[0] });

接著更新您的搜尋用戶端:

  • Fumadocs UI:在搜尋 UI 中啟用標籤篩選。

  • 搜尋用戶端:將標籤傳入 useMeilisearch()。

    import { useMeilisearch } from 'fumadocs-core/search/client';
    
    const { search, onSearchChange, isLoading, data } = useMeilisearch({
      tag: '<your tag value>', 
      // ...
    });

國際化

文件會包含所屬頁面的語系,傳入 locale 即可搜尋特定語言:

import { useI18n } from 'fumadocs-ui/contexts/i18n';
import { useMeilisearch } from 'fumadocs-core/search/client';

const { locale } = useI18n();
const { search, onSearchChange, isLoading, data } = useMeilisearch({
  locale, 
  // ...
});

索引設定

sync() 只會設定 searchableAttributes 和 filterableAttributes,其他設定 (例如同義詞與錯字容忍度) 則會維持不變。您可以透過 Meilisearch 儀表板或 meilisearch 用戶端調整這些設定。

運作原理

每個標題與段落都是一份文件,並附帶頁面標題、URL 與 Breadcrumb。只有 content 可供搜尋,搜尋用戶端會依頁面將結果分組。

屬性說明
typepage、heading 或 text
content頁面標題、標題或段落
heading所屬標題的 ID (選用)
tag用於篩選結果的標籤 (選用)
locale頁面的語系 (選用)

How is this guide?

最後更新於