ys memos

Blog

個人ブログへの静的検索エンジンの実装とその変遷の話


blog

2024/05/08


個人ブログである本サイトへの検索エンジンの実装および、その発展の変遷についての話の備忘録。

post数もだいぶ増えていて、簡易なものでも検索エンジンを導入したかった。

完璧なものである必要はなく、キーワードを前提とした(ほぼ)全文検索を実現したかった。


  • ブログはNext.js (SSG)で構築されている
    • SSGで生成した静的ファイルをGitHub Pagesでホスティングしているため、ビルド時に自由な処理を実行できるものとする
  • 個人ブログなので、継続性のためにランニングコストを抑えたく、バックエンドサーバは用いない

まずはSSGであることを活かして、 next buildのタイミングでインデックスを生成し、そのインデックスをフロント側で用いて、ローカルで検索するというアプローチを目指すことにした。


全文検索エンジンをイメージしていたので、転置インデックスか n-gramのどちらか(もしくは両方)を用いるのが頭に浮かんだ。

それらの取捨選択についてだが、ここでの実装ではフロントがインデックスを取得することになるので、完全な全文検索よりもインデックスの軽さを重視し、n-gramは用いずに、形態素解析を用いた転置インデックスを採用することにした。

この決定により、検索できるワードは限定され、細かいニュアンスや接頭辞検索などはできず、単語検索エンジンを目指すことになった。



形態素解析は kuromojiを用い、その結果から stop-wordsの除去と名詞の抽出を行い、それを Record<string, {slug:string; count:number}>のような形式でJSONに保存し、これをインデックスとして用いることにした。

これをフロントエンド側で importし、これを利用して単語検索およびその結果表示を行うコンポーネントでそれを利用した。

動作例は以下の通り。

  • rustという単語を検索した時は、その countで降順ソートし表示
  • react typescriptで検索したときは reacttypescriptの両方が含まれる記事を、それらの countの和で降順ソートし表示

この時点で静的解析エンジンの全容は完成している。


next buildする毎にすべてのファイルのインデックスを生成しており、ビルド時間がいたずらに増えていった。

そこで、形態素解析の結果をキャッシュし、変更がないファイルについては再解析を行わないようにすることで、ビルド時間の短縮を実現した。


ここまで読むと想像がつくと思うが、かなり大きいJSONを初期ロード時に必ず読み込むことになってしまっている。

そしてこれは、postが増えていくにつれて増えていくことが考えられる。

初期表示時間短縮のための手法として、

  • {slug:string; count:number}{id:number, count:number}にし、別途 {id:number, slug:string}を用意する
  • ② 単語ごとにインデックスを分割する

という手法が考えられた。両方とも必要な対策ではあるものの、①はより最適化を目指す場合に有効ではあるがこれだけでは記事数の増加に対処できず、②は初期ロードを抜本的に抑えつつ記事の増加による影響も少ないと考察した。

そこで、将来①をやる可能性はあるものの、まずは②を実現した。

各単語のインデックスを( rustであれば rust.jsonとして)準備し、それを public/の配下に配置し、SSGの結果とともに静的ホスティングにアップロードした。

フロントでは初期では空のインデックスを持ち、単語を入力する毎に、静的ホスティングからインデックスをFetchし、それをフロントでマージし、実装済みの検索につなげた。一度Fetchしたインデックスはフロントでキャッシュし、再度Fetchすることはない。


インデックスを分割したことにより、初期ロード時の負荷は減ったが、それに伴い、入力文字すべてをFetchしてしまうと、入力毎に存在しないインデックスもリクエストするようになった。

これでは、無駄なFetchが発生して嬉しくなかった。

そこで、初期ロードを遅くしてしまうが、妥協案として、「インデックスが存在する単語リスト」を初期にロードし、それを元にFetchの有無を判断することで、Fetchの回数を必要最低限に抑えた。


通常の全文検索エンジンとは経路が違うものの、それに近い実装を経験することができた上、フロントエンドにおけるレンダリング時間の改善を意識した開発ができたので、非常に楽しかった。

より完璧な検索エンジンを用いる場合は、バックエンドサーバを用意したほうが良さそうではあるが、フロントでできる中ではきちんとした検索エンジンが完成したので満足した。


関連タグを探す