こんにちは。ソフトウェアエンジニアの id:masutaka26 です。
ROUTE06 では全社ワークスペースに GitHub を利用しています。
ストック情報としての社内ドキュメントは GitHub Pages で社内限定で公開されており、静的サイトジェネレーターとして Docusaurus を利用しています。
検索精度への課題
今までは Docusaurus の Community plugins でも紹介されている @easyops-cn/docusaurus-search-local を使用していました。
フロントエンドだけで動作し、設定もお手軽なのは良いのですが、検索精度に難があり、一部ではリポジトリを clone して手元で grep することが常態化していました。
参考までに docusaurus.config.js の設定内容です。
themes: [ [ require.resolve("@easyops-cn/docusaurus-search-local"), /** @type {Partial<import("@easyops-cn/docusaurus-search-local").PluginOptions>} */ ({ hashed: true, language: ["en", "ja"], }), ], ],
リポジトリをざっと眺めた限りでは、検索精度が上がりそうなオプションは見当たりません。日本語だと removeDefaultStopWordFilter
, zhUserDict
, zhUserDictPath
は関係なさそうです。
DuckDB Wasm で検索フォームを実装した
今回 DuckDB をリサーチする機会があったため、成果物として DuckDB Wasm で検索フォームを実装しました。以下がサンプルコードです。デモサイトもあります。
正式な Docusaurus の plugin として作ったわけではないため、あくまでサンプルコードとして参考になれば幸いです。
DuckDB とは
DuckDB は OSS であり、軽量で高速な列指向のデータベースです。SQLite と似ていますが、SQLite は行指向データベースです。
DuckDB では Wasm も提供されており、フロントエンドのみで DuckDB を動作させることが出来ます1。
Core Extension も FTS (Full-Text Search) や VSS (Vector Similarity Search) などを利用可能です。
検索フォームの実装概要
実装は素朴です。
デプロイ前に static/docs.json を生成する
npm run collect-docs
デプロイされると GitHub Pages の
/docs.json
として配置される/search/
を訪問すると、CDN 上の DuckDB Wasm を読み込み、DuckDB を初期化する- DuckDB fts 拡張のインストール&ロード
/docs.json
を読み込み、DuckDB 内のdocs.json
として登録- documents テーブルの作成
- DuckDB 内の
docs.json
を documents テーブルに一括挿入 - documents テーブルのインデックス作成
- 作成したインデックスに対して、検索 (SELECT) を実行する
後述する PoC でわかち書きも検討しましたが、社内ドキュメント 500 記事程度だと、そこまで検索精度が変わらなかったので、現在は導入していません。
PoC で検討したこと
検索フォーム実装前に PoC (Proof of Concept) として、以下の検索を検討しました。前述のリポジトリ poc/ に、実際に動作するコードがあります。
- 全文検索
- 日本語全文検索(わかち書き版)
- 検索対象のドキュメントは予め Lindera CLI でわかち書き
- 検索クエリは Lindera Wasm でわかち書き
- ベクトル検索
- 要 OpenAI API キー
最終的に、わかち書きなしの「全文検索」を採用しました。CLI 版と Wasm 版の Lindera を Docusaurus に組み込む手間の割に、そこまで検索精度が変わらなかったからです。
まとめ
Docusaurus v3 の全文検索を DuckDB Wasm + FTS 拡張で実装し、検索精度の課題を解決することが出来ました。
DuckDB も Wasm も初見で、初めは全く分からない状態でしたが、Claude Code のおかげでなんとか形になりました。
DuckDB Wasm を使うと、フロントエンドのみでデータベースを構築できます。MySQL や BigQuery と違って、権限を気にしなくて済むため、今回のような小規模のデータには最適だと感じました。
検索対象が増えると、わかち書きを含めた検索精度のチューニングが必要になりそうですが、今は必要なさそうです。