コンテンツへスキップ
Fumadocs

Mixedbread

Mixedbread SearchをFumadocsに統合する

Mixedbreadはセマンティック検索を提供しています。ユーザーは自然言語で検索でき、キーワードではなく意味に基づいてコンテンツを見つけられます。

セットアップ

依存関係をインストールします。

npm install @mixedbread/sdk

API キー

Mixedbread にサインアップし、API キーとドキュメント用の store を作成したら、それらを .env.local に保存します。

MXBAI_API_KEY="your API key"
MIXEDBREAD_STORE_ID="your store ID"

検索はサーバー上で実行されるため、API キーがブラウザに露出することはありません。

ドキュメントの同期

static.json ルートを事前レンダリングしてビルド時に検索ドキュメントをエクスポートし、ビルドのたびにスクリプトでストアへ同期します。

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

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/mixedbread';
import Mixedbread from '@mixedbread/sdk';

// 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[];

await sync(new Mixedbread(), {
  storeIdentifier: process.env.MIXEDBREAD_STORE_ID!,
  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 スクリプトはビルドのたびに同期を行うため、デプロイ先のプラットフォームにも同じ環境変数を設定してください。

Search API

store を検索するためのルートを作成します。

app/api/search/route.ts
import { createMixedbreadSearchAPI } from 'fumadocs-core/search/mixedbread';
import Mixedbread from '@mixedbread/sdk';

export const { GET } = createMixedbreadSearchAPI({
  client: new Mixedbread(), // `MXBAI_API_KEY` を読み取ります
  storeIdentifier: process.env.MIXEDBREAD_STORE_ID!,
});

検索 UI

UI の実装方法には、いくつかの選択肢があります。

  • Fumadocs UI の検索ダイアログを使用する。

  • 組み込みの検索クライアントフックを使って独自に構築する。

    import {  } from 'fumadocs-core/search/client';
    
    const { , , ,  } = ({
      : '/api/search',
      // 検索のたびに Mixedbread へリクエストが送信されます
      : 300,
    });

オプション

タグフィルター

タグによるフィルタリングを設定するには、static.json ルートで toDocuments() に tag を渡します。

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

あわせて、検索クライアントも更新してください。

  • Fumadocs UI: 検索 UI でタグフィルターを有効にします。

  • 検索クライアント: useFetchSearch() にタグを渡します。

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

国際化

ドキュメントには各ページのロケールが含まれています。特定の言語で検索するには、locale を渡します。

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

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

内部の仕組み

各ページはURLをキーとするファイルで、ページタイトル、URL、パンくずリスト、タグ、ロケールをメタデータとして持ちます。ページのチャンクはタイトル、見出し、段落で構成され、検索結果はすべてチャンク単位で返されます。

チャンクのメタデータ説明
kindpage、heading、または text
heading所属する見出しのID (任意)
tableテーブル行が属するテーブル (任意)

How is this guide?

最終更新日