【ITニュース解説】Skip Elasticsearch: Build Blazing-Fast Full-Text Search Right in Supabase
2025年10月03日に「Dev.to」が公開したITニュース「Skip Elasticsearch: Build Blazing-Fast Full-Text Search Right in Supabase」について初心者にもわかりやすく解説しています。
ITニュース概要
SupabaseのPostgres組み込み機能を使えば、Elasticsearchのような外部ツールなしで高速な全文検索を実装できる。テキストを正規化しtsvectorで最適化、GINインデックスを活用することで、データと完全に同期した検索を効率的に実現し、運用コストも削減する。
ITニュース解説
全文検索とは、データベースに保存された大量のテキストデータの中から、特定のキーワードやフレーズを含む情報を効率的に探し出すための機能だ。通常の検索(SQLのLIKE句)では「Python REST」といった完全一致の文字列しか見つけられないが、全文検索を使えば、「PythonとFastAPIを使ってRESTful APIを設計・開発する」といった文章からも、「Python」と「REST」というキーワードを見つけ出し、関連性の高い順に結果を表示できる。これは、私たちがGoogleなどの検索エンジンでキーワードを入力して情報を探すのと同じような体験を、自分のアプリケーションでも実現できることを意味する。
現代のアプリケーションでは、膨大な量のテキストデータを扱い、ユーザーは瞬時に目的の情報にたどり着くことを期待する。このような要件を満たすために、外部の検索エンジンツールを使うことも多いが、Postgresには強力な全文検索機能が組み込まれており、これを利用することで余分なインフラを導入することなく、高性能な検索システムを構築できる。データベース内に検索用のインデックスを保持するため、通常のデータと常に同期が取れており、管理の複雑さや運用コストを削減できるのが大きなメリットだ。Supabaseは、このPostgresの強力な機能をマネージドサービスとして提供しているため、特別な設定なしで利用できる。
Postgresの全文検索は、検索対象のテキストデータを「tsvector(ティーエスベクター)」という特殊な形式に変換するところから始まる。このtsvectorは、元の文章から句読点を取り除き、全て小文字にし、「and」や「the」といった一般的な単語(ストップワード)を無視し、さらに単語をその「語根(レクセム)」に変換(ステミング)したものだ。例えば、「running」「ran」「runs」といった単語は、全て「run」というレクセムに変換される。これにより、単語の形が少し違っても同じ意味として検索できるようになる。ユーザーが入力した検索キーワードも「tsquery(ティーエスクエリ)」という形式に変換され、tsqueryとtsvectorが比較されて一致するデータが探し出される。Postgresは、これらの変換や比較の際に、単語の出現頻度や位置などに基づいて関連性を評価し、結果をランキングする機能も持ち、英語だけでなく、フランス語やドイツ語など多言語に対応した設定も利用できる。
具体的な実装では、まず検索対象となるテキストフィールド(例えば、記事のタイトル、本文、タグなど)を持つテーブルを作成する。次に、これらのフィールドを組み合わせてtsvectorを作成するための特別な関数と、その関数を使ってtsvectorを自動的に更新する「生成列(Generated Column)」をテーブルに追加する。生成列は、元のテキストフィールドが更新されるたびに、自動的にtsvectorを再生成してくれるため、検索インデックスとデータの一貫性を常に保てる。そして、この生成列に対して「GIN(ジェネラルイズド・インバーテッド・インデックス)」インデックスを作成すれば、高速な全文検索の準備は完了する。GINインデックスは、各レクセムがどの文書に登場するかを記録する特殊なデータ構造であり、数百万件ものデータの中からでも瞬時に目的の文書を見つけ出せるようにする。これにより、Postgresは全ての行をスキャンする代わりに、インデックスを使って直接該当する文書へとアクセスできるのだ。
検索は、「to_tsquery()」関数で検索キーワードをtsqueryに変換し、「@@」演算子を使ってtsvectorと比較することで行う。複数のキーワードを「&(AND)」や「|(OR)」で組み合わせたり、「!(NOT)」で除外したりすることも可能だ。さらに、単に一致するだけでなく、検索結果の「関連度」に基づいてランキングすることも重要だ。Postgresの「ts_rank()」関数や「ts_rank_cd()」関数を使えば、キーワードの出現頻度や、キーワード同士がどのくらい近くに現れるかといった要素に基づいて、関連度スコアを計算できる。このスコアが高い順に結果を表示することで、ユーザーは最も関連性の高い情報から見つけられる。また、「setweight()」関数を使えば、例えばタイトルでの一致は本文での一致よりも関連度が高い、といったように、検索対象の各フィールドに「重み付け」をして、よりユーザーの意図に合ったランキングを実現できる。
最後に、効率的な全文検索を維持するためのいくつかのヒントがある。まず、必ずtsvector列にGINインデックスを設定すること。次に、検索対象のテキストが英語以外の言語であれば、適切な言語設定を「to_tsvector()」関数に指定すること。そして、生成列を使うことで、検索インデックスを常に最新の状態に保つことが重要だ。頻繁に検索されるが更新頻度の低いデータに対しては、「マテリアライズドビュー」で検索結果を事前に計算しておくことも有効だ。また、非常に大規模なテーブルは、「パーティショニング」で分割することで、インデックスのサイズを小さく保ち、クエリ速度を向上させられる。Supabaseのパフォーマンスアドバイザーを利用して、遅いクエリを特定し、最適化のヒントを得ることも忘れてはならない。
このように、SupabaseとPostgresを組み合わせることで、外部の検索エンジンツールに頼ることなく、強力で高速な全文検索システムを構築し、運用できる。