サイト内検索で同義語検索できるようにする

前回に引き続き検索機能の改善をしたよ。
最初はKuromojiで検索インデックスを作ろうと思ってたんだけど、
- インデックス作るタイミングどうしようかなぁ。Githubアクションで組み込むのか、でもコンテンツはignoreされてていつもvercel --prodで本番更新するスタイルだから悩ましい
- データを加工せず検索ワードを加工しまくる戦略でどこまでいけるのかちょっと興味わいてきた
というわけで、当面検索インデックスは作らずに、今まで通り単純なファイル検索でいくことにしたよ。
検索キーワードを加工しまくる戦略
前提
まず、前提条件はこんな感じだよ
検索対象のファイル
- ブログ記事そのままの、ただのマークダウンファイルだよ
- 食べ物っていう同じ概念でも、記事により「Food」とか「food」とか「フード」とか「食べ物」とか書かれているよ
検索キーワード
- 「フード おすすめ」とかスペース区切り(全角・半角可)でなんでも入力できるよ
- 「食べ物」と入力することもあれば「フード」を入力することもあるよ
- 「Next.js」と入力することもあれば「nextjs」と入力することもあるよ
- いろんなパターンに対応できるようにする必要があるよ
検索キーワードの加工
検索キーワードを入力したら、こんな処理をするよ
検索時に全角「食べ物」と入力されたら、手作りの同義語辞典を使って同義語を検索するよ
- 食べ物
- フード
- food
上記の検索文字を
$queries = '食べ物|フード|food';
になるようにパイプで繋いで、
jsのRegExpオブジェクトのCase insensitive(大文字小文字無視モード)で検索するよ
const posts = await getAllPosts();
const filteredPosts = posts.filter((post) => {
return new RegExp(queries, "i").test(post.title + post.content + post.tags.join(''));
});
(実際には全角英数字を半角にしたり記号をエスケープする処理も入れてるよ)
検索結果
すると、「食べ物」と検索しただけなのに、
- 食べ物
- フード
- food
- FOOD
- Food
- fOOd
が一個でも含まれてる記事は全部ヒットするよ
「nextjs」で検索してもNext.jsの記事が出るし、「ts」と入れてもTypeScriptの記事が出るようになったよ
同義語検索機能
このブログの規模だったら、よく検索されそうな単語だけ同義語を取得できればいいかと思って自前実装にしたよ。 ざっくりこんなふうに実装したよ
同義語辞典を作成
こんな風に、ただのテキストファイルを作ってサーバー上に設置したよ
食べ物,フード,food
ポエム,poem
レシピ,recipe
検索,search
next.js,nextjs
typescrypt,ts
javascript,js
APIルートを作成
同義語辞典にアクセスするのはサーバーサイドの処理だよ。なので、こんな処理をするAPIルートを作ったよ。
- クエリパラメーターでキーワードを取得する(食べ物)
- 類語ファイルにキーワードが含まれている行があったら、その類語を返す(フード、food)
自前実装のいいところ
外部APIを使わないので、アカウント管理が不要だし、サーバー内でしか通信が発生しないので軽いよ。
Kuromijiどうする?
というわけで、インデックスを使わずにまぁまぁいい感じの精度で検索できるようになったよ。
KuromojiもせっかくAPIルート作ったから何かに使いたいな?と思って考え中...