PostgreSQLの全文検索、`tsquery`を使いこなして「検索精度」を一段引き上げよう
やあ。最近、PostgreSQLで「とりあえず`LIKE`検索」でお茶を濁して、データが増えてからパフォーマンスに頭を抱えているエンジニアをよく見かけるんだよね。
「`LIKE ‘%hoge%’`だとインデックスが効かないし、全文検索エンジンを導入するほどでもない…」
そんな時に思い出してほしいのが、PostgreSQL標準の全文検索機能だ。特に、クエリを操るための型である`tsquery`を理解すると、ただのキーワード検索が「ユーザーが本当に欲しい情報をピンポイントで当てる検索」に化ける。
今日は、実務で明日から使える`tsquery`の勘所を、少しだけ深掘りしてみよう。
—
tsqueryとは何者か?
簡単に言うと、`tsquery`は「検索したい条件を、PostgreSQLが解釈できる形式に変換したもの」だ。
例えば、ユーザーが「PostgreSQL」と「パフォーマンス」という単語で検索したい時、これを単なる文字列として渡すのではなく、`tsquery`という特別な型に変換してやる必要がある。
一番シンプルな使い方はこれだ。
— ‘postgresql’ と ‘performance’ の両方を含むレコードを探す
SELECT FROM articles
WHERE to_tsvector(‘english’, body) @@ to_tsquery(‘english’, ‘postgresql & performance’);
ここで使っている `@@` 演算子が「マッチング演算子」。左側の`tsvector`(インデックス化されたテキスト)と、右側の`tsquery`(検索条件)を照合する。これだけで、PostgreSQLは内部のインデックス(GINなど)を使って爆速で結果を返してくれるんだ。
—
実務で必須の演算子をマスターする
`tsquery`が面白いのは、ただのAND検索だけじゃないところにある。現場でよく使う演算子を整理しておくよ。
- `&` (AND): 両方含まれる。
- `|` (OR): どちらか一方でも含まれる。
- `!` (NOT): 特定の単語を除外する(これ、意外と忘れがちだけどめちゃくちゃ重要)。
- `<->` (FOLLOWED BY): 「Aの直後にBがくる」という順序付き検索。
例えば、「PostgreSQLのパフォーマンスは知りたいけど、MySQLの話は除外したい」なんて時はこう書く。
SELECT FROM articles
WHERE to_tsvector(‘english’, body) @@ to_tsquery(‘english’, ‘postgresql & performance & !mysql’);
これ、ユーザーにUIで「除外キーワード」を入力させたい時には必須のテクニックだよね。
—
プレフィックスマッチングで「先読み」を実現する
ここからが少し実践的な話だ。ユーザーが検索窓に「Postgre」と打った時、当然「PostgreSQL」もヒットしてほしいよね。
`tsquery`では、単語の後ろに `: ` をつけることで、プレフィックスマッチングができる。
— ‘postgre’ で始まる単語を含むレコードを検索
SELECT FROM articles
WHERE to_tsvector(‘english’, body) @@ to_tsquery(‘english’, ‘postgre:’);
ただし注意点がある。この `:` を使うと、通常のB-treeインデックスや単純なGINインデックスでは対応できない場合があるんだ。運用環境で使うなら、`pg_trgm`(trigram)拡張を組み合わせるか、インデックスの設計をしっかり見直す必要がある。このあたりは、「検索の速さ」と「利便性」のトレードオフだね。
—
実装時の「落とし穴」とアドバイス
最後に、現場で設計する時に注意してほしいポイントを3つだけ伝えておくよ。
1. ユーザー入力を直接投げない:
ユーザーが入力した文字列には `&` や `|` が含まれているかもしれない。そのまま `to_tsquery` に渡すとエラーになることが多い。必ず `plainto_tsquery` や `websearch_to_tsquery` を使おう。これらは、ユーザーの入力文字列を「いい感じに」安全な`tsquery`に変換してくれる便利な関数だ。
2. 言語設定(辞書)を合わせる:
`to_tsvector(‘english’, …)` としているなら、`to_tsquery` も `’english’` を指定すること。ここがズレると、ステミング(単語の語幹抽出)がうまくいかず、検索結果がスカスカになる。
3. インデックスはGINを使う:
全文検索の結果を高速化したいなら、対象カラムに `GIN` インデックスを貼るのが鉄板だ。
CREATE INDEX idx_articles_body ON articles USING GIN (to_tsvector(‘english’, body));
—
最後に
全文検索は奥が深い。最初は `LIKE` 検索で十分だと思っていても、データが100万件を超えたあたりで、PostgreSQLのこの機能が「最強の味方」に変わる瞬間が来るはずだ。
まずは、自分のプロジェクトの検索クエリを `to_tsquery` に置き換えるところから始めてみてほしい。「あれ、こんなに速くなるの?」という感動が、きっと君のエンジニアとしての武器を一つ増やしてくれるはずだよ。
また何か詰まったら、いつでも聞きに来てくれ。応援しているよ!
コメント