EmDash で日本語検索が 0 件になるので、プラグインで直した
WordPress + Astro で作っていた自分のサイトを、Cloudflare の CMS「EmDash」に移しています。その途中で、日本語の検索がまったく効かないことに気づきました。
「検索」というタイトルの記事を「検索」で探しても、結果は 0 件です。
記事タイトル: 日本語検索について
検索ワード: 検索
結果: No results for "検索"原因を調べて、辞書なしで動くプラグインを作って直したので、その記録です。
EmDash の検索の仕組み
EmDash は Astro ベースのオープンソースの CMS で、2026 年 9 月に 1.0 が出ました。記事の検索には SQLite の全文検索機能(FTS5)を使っています。
全文検索は、保存するときに文章を「単語」に切り分けて索引を作り、検索ワードと同じ単語があるかで探す仕組みです。EmDash は、この切り分け方の初期設定が porter unicode61 になっています。
// emdash/src/search/fts-manager.ts
const DEFAULT_SEARCH_TOKENIZER: SearchTokenizer = "porter unicode61";0 件になる理由
unicode61 は、スペースと句読点で文章を区切ります。英語ならそれで単語になりますが、日本語にはスペースがありません。なので、句読点までの一続きがまるごと 1 つの「単語」になります。
元の文: EmDash の日本語全文検索の挙動を検証します。
切り分け後: ["emdash", "の日本語全文検索の挙動を検証します"]この状態で「検索」を探しても、「検索」という単語は索引のどこにもありません。だから 0 件になります。
本家にも同じ報告が上がっていました(emdash-cms/emdash#2775)。この記事を書いている時点では、まだ直っていません。
trigram に切り替えるだけではダメだった
EmDash では、切り分け方を trigram にも変えられます。文章を 3 文字ずつずらして切る方式で、日本語ではよく使われる回避策です。
ただ、3 文字ずつなので、2 文字以下の検索ワードは引っかかりません。
料金 (2文字) → 0件
の料金 (3文字) → 1件
従量課金 (4文字) → 1件日本語の検索ワードは「料金」「予約」「会社」「採用」のような 2 文字がとても多いので、これだと実用になりません。それに、英語の単語の語形をそろえる処理(search と searching を同じ扱いにするなど)も効かなくなります。
直し方: 2 文字ずつの索引を別に作る
考え方は単純で、2 文字ずつずらして切ったものを、記事とは別の欄に入れておきます。
元の文: 日本語検索について
索引: 日本 本語 語検 検索 索に につ つい いて検索するときも、検索ワードを同じように 2 文字ずつに切ります。そのうえで「この順番で並んでいること」という条件で探します。
検索ワード: 日本語 → 「日本」「本語」がこの順で並んでいるこれで「この文字列が含まれているか」と同じ意味の検索になります。2 文字の「料金」も、索引の中にそのままあるので見つかります。
切り分けた結果は記事の中の検索用の欄に入れるだけなので、検索そのものは EmDash 本体の仕組みをそのまま使えます。並び順(関連度順)や、公開・非公開の扱いは、本体がやってくれます。
落とし穴: 区切りをまたいで当たってしまう
このままだと、困ったケースが出ます。
本文: 本社は東京、京都、大阪の三拠点です。
索引: ... 東京 京都 大阪 ...「東京」と「京都」が、索引の中で隣り合っています。そのため、本文に「東京都」という言葉はないのに、「東京都」で検索すると当たってしまいます。
これを防ぐために、区切りのところに目印の文字を 1 つ挟みました。目印が間に入れば、「東京」と「京都」は隣り合わなくなります。
索引: ... ǁ東京 ǁ ǁ京都 ǁ ǁ大阪 ...目印には ǁ という、普通の文章にはまず出てこない文字を使っています。索引の各トークンの頭にも付けているのは、本文の欄の切り分け結果と混ざらないようにするためです。
結果
検索ワード | 直す前 | 直した後 |
|---|---|---|
検索 | 0 件 | 当たる |
料金 | 0 件 | 当たる |
挙動(文の途中の語) | 0 件 | 当たる |
東京都(本文は「東京、京都」) | ― | 当たらない(正しい) |
テスト(半角)で「テスト」 | 0 件 | 当たる |
英語だけの検索ワードは、これまでどおり本体の検索に任せています。
できないこと
- 「京都」で探すと「東京都」にも当たります。文字列が含まれているかどうかで探しているので、単語の区切りまでは分かりません。
- 「出来る」と「できる」、「解約する」と「解約します」は別物として扱います。ここをそろえるには辞書が必要です。
- 索引の分だけ、データは元の文章の 3〜4 倍くらいに増えます。
ちゃんとした日本語検索がほしいなら、辞書を使う形態素解析や、専用の検索サービスを使うことになります。今回のは、個人ブログくらいの規模で、追加のサービスなしに「ちゃんと引ける」状態にするための方法です。
Claude Code と作って分かったこと
このプラグインは Claude Code と一緒に作りました。途中で何度か「できました」と言われたあとに、壊れているところが見つかっています。
- 自分で用意した都合のいいテストデータだけで確認していて、本番の動きと違っていた
- テストが EmDash 本体の処理の一部を飛ばしていて、複数の語で探すと全部 0 件になっていた
- 記事が 5 件しかない環境で試していたので、件数が増えたときだけ起きる不具合が見えていなかった
どれも、テストは全部通っている状態で見つかりました。テストが通っていることと、正しく動いていることは別だというのは、今回あらためて実感したところです。
---
本家の issue(#2775)はまだオープンです。同じところで困っている人の参考になればうれしいです。