
夏の自由研究2025ブログ連載の4日目です。
技術コンサルをしているお客さんとPrismaのドキュメントの読書会をしていて、全文検索機能がPrismaにも、PostgreSQLにも標準で用意されているということを知りました。PostgreSQLで全文検索はというと、PGroongaとか、pg_bigmを使うとかがトップ出てくるし、そもそも検索をしたくなったらElasticSearch使う、みたいに思っていました。
標準で全文検索もできるなら運用コストもだいぶ下げられそうです。かつて、Python製ドキュメントツールの、ブラウザで動く全文検索エンジンの日本語対応をやってみたり、FM-indexという高速文字列解析の世界という書籍で紹介されていたアルゴリズムを使ったブラウザで動く検索エンジンを作ったり、転置インデックスをS3に置く検索エンジンを作ってみたり貧乏低コスト検索エンジンの第一人者(自称)としては試してみたいところです。
ものは試しでやってみました。
PostgreSQLの全文検索機能
PostgreSQLの全文検索では、LIKE
とかILIKE
で検索するみたいに、 @@
で転置インデックスを検索する演算子が提供されており、それを呼び出します。検索するフィールドはto_tsvector()
、検索ワードはto_tsquery()
関数に渡して前処理をするところがポイントですかね。
SELECT title |
テーブルの中にもtsvectorを作ってあげます。
CREATE TABLE articles ( |
なお、全文検索エンジンあるあるテーマが日本語対応で、大抵は英語のようにスペース区切りの単語で分割し、転置インデックスという、単語→ドキュメントのインデックスを作り、それを元に検索をするという仕組みです。その過程で、英語やドイツ語などの言語ごとに正規化(ステミング)、冠詞などのノイズになる単語(stop word)をフィルタリングなど、自然言語ごとの前処理を行います。そのあたりの設定はここに書かれています。
標準でサポートしている言語は17.5で以下のような感じです。
/usr/local/share/postgresql/tsearch_data # ls *.stop |
デフォルトでは日本語のようなスペース区切りではない言語は対応できません。当然日本語の文法ルールもなく、stop wordの辞書もありません。この機能も、昔は日本語対応のtextsearch_jaというモジュールがあったようですが、今はメンテナンスされていないようです。仮にされていたとしても、DB運用はクラウドにお任せ時代なので最初から導入されている方が運用は楽でしょう。
なんとかして使ってみる方法として、事前に、DBの外でスペース区切りにしてからテーブルに入れてみるという前処理をする方法を試してみます。
GoでPostgreSQLの全文検索で日本語検索してみる
Goでは分かち書きのライブラリとして定評のあるgithub.com/ikawaha/kagome/v2を使います。
kagomeではこんな感じではこんな感じに
$ kagome |
助詞とか助動詞はひかっかりまくるので削除し、動詞などは原形にすることで、「送信する」「送信した」などの表記揺れでもひっかかるようになります。分かち書きでは品詞もわかるのでこれを使って、助詞、助動詞、副詞などを除外すれば良いでしょう。簡単ですね。
実装
ソースコードはgithub.com/shibukawa/pgtfsです。生成AIでサッと作りました。ライセンスはUnlicenseです。full text searchだとftsのはずですが、スペルを間違ったのでtfsになってます。CLIツールとなっています。実証実験のコードなので、インデックスのメンテナンスとか考えずに、一発投入のみ。
# PostgreSQLの起動 |
サンプルドキュメントも生成AIが用意してくれました。
# Goプログラミング言語 |
内部では次のように区切られてスペース区切りにされて保存されます。
#|Go|プログラミング|言語|Go|Google|開発|し|プログラミング|言語| |
「Web開発」で検索します。この単語は文中にはありません。「Webサービス開発」ならあります。うまく分かち書きされると単語がヒットしてくれるはず!!!
./pgtfs search "Web開発" |
きた!
より使いやすい検索にするには
分かち書きとstop wordは最低限です。実際にElastichsearchで使われるKuromojiではそれ以外に漢数字を算用数字にしたりさまざまなフィルタを提供しています。
これにより、表記揺れでもヒットしやすいようになります。PostgreSQLの検索機能も類義語辞書が持てるようになっているため、日本語でも頑張って集めることでさらに精度を上げる余地があります。最終的にはベクトル検索を実装して類似文書検索ですかね。いつかはやってみたい。
まとめ
かんたんな前処理でPostgreSQLの標準の検索機能が使えました。これでコストを増やさずに全文検索機能が簡単に組み込めるでしょう。bigmなんかは正規の単語の区切りではないところでもひっかかってしまったりというのがあり、やはりきちんと分かち書きをした検索エンジンが使いたいですよね?どのような言語でもたいてい分かち書きのライブラリはあると思うので言語問わず利用できると思います。
PostgreSQLにはPub/Sub機能もあるので、DBさえあれば他のマネージドサービスは要らない、という時代がそのうち来るんじゃないかと思っているところです。