Skip to content

Commit acb6656

Browse files
authored
Merge pull request #1616 from future-architect/feature
PostgreSQL全文検索
2 parents 59fa722 + a025b2a commit acb6656

4 files changed

Lines changed: 165 additions & 1 deletion

File tree

source/_posts/20250825a_夏の自由研究連載_2025.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ lede: "夏の自由研究2025のブログリレーのインデックス記事で
3333
| 8/25(月) | 二宮佑斗 | [Grafana Alloyを使って、EKSクラスタ外部のサーバからメトリクス取得を試してみた](/articles/20250825b/) |
3434
| 8/27(水) | 松本朝香 | [リスクアセスメント ーリスクの可視化から意思決定までー](/articles/20250827a/) |
3535
| 8/28(木) | 大前七奈 | [dbt fusion engine・dbt profiler](/articles/20250828a/) |
36-
| 8/29(金) | 澁川喜規 | PostgreSQLの標準の全文検索 |
36+
| 8/29(金) | 澁川喜規 | [PostgreSQLの標準の全文検索](/articles/20250829a/) |
3737
| 🌻 | - | - |
3838
| 9/1(月) | 清水雄一郎 | Cursor+GitHub Actionsで、自分でソースコード書かずにiOSアプリをApp Storeに公開できるか |
3939
| 9/2(火) | 永井優斗 | クロスプラットフォームフレームワークLynX |
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
---
2+
title: "PostgreSQLの全文検索機能を試してみる"
3+
date: 2025/08/29 00:00:00
4+
postid: a
5+
tag:
6+
- PostgreSQL
7+
- 全文検索
8+
- Go
9+
- Prisma
10+
- kagome
11+
category:
12+
- Programming
13+
thumbnail: /images/20250829a/thumbnail.png
14+
author: 澁川喜規
15+
lede: "全文検索機能がPrismaにも標準で用意されているということを知りました。PostgreSQLで全文検索はというと、PGroongaとか、pg_bigmを使うとかがトップ出てくるし、そもそも検索をしたくなったらElasticSearch使う、みたいに思っていました。標準で全文検索もできるなら運用コストもだいぶ下げられそうです。"
16+
---
17+
18+
<img src="/images/20250829a/top.png" alt="" width="600" height="600">
19+
20+
[夏の自由研究2025](/articles/20250825a/)ブログ連載の4日目です。
21+
22+
技術コンサルをしているお客さんとPrismaのドキュメントの読書会をしていて、全文検索機能が[Prismaにも](https://www.prisma.io/dataguide/managing-databases/intro-to-full-text-search)[PostgreSQLにも](https://www.postgresql.org/docs/current/textsearch.html)標準で用意されているということを知りました。PostgreSQLで全文検索はというと、PGroongaとか、pg_bigmを使うとかがトップ出てくるし、そもそも検索をしたくなったらElasticSearch使う、みたいに思っていました。
23+
24+
標準で全文検索もできるなら運用コストもだいぶ下げられそうです。かつて、Python製ドキュメントツールの、ブラウザで動く全文検索エンジンの日本語対応をやってみたり、[転置インデックスをS3に置く検索エンジン](/articles/20200327/)を作ってみたり~~貧乏~~低コスト検索エンジンの第一人者(自称)としては試してみたいところです。
25+
26+
ものは試しでやってみました。
27+
28+
# PostgreSQLの全文検索機能
29+
30+
PostgreSQLの全文検索では、`LIKE`とか`ILIKE`で検索するみたいに、 `@@`で転置インデックスを検索する演算子が提供されており、それを呼び出します。検索するフィールドは`to_tsvector()`、検索ワードは`to_tsquery()`関数に渡して前処理をするところがポイントですかね。
31+
32+
```sql
33+
SELECT title
34+
FROM pgweb
35+
WHERE to_tsvector(title || ' ' || body) @@ to_tsquery('create & table')
36+
ORDER BY last_mod_date DESC
37+
LIMIT 10;
38+
```
39+
40+
テーブルの中にもtsvectorを作ってあげます。
41+
42+
```sql
43+
CREATE TABLE articles (
44+
id SERIAL PRIMARY KEY,
45+
title TEXT NOT NULL,
46+
content TEXT NOT NULL,
47+
file_path TEXT NOT NULL UNIQUE,
48+
author TEXT NOT NULL DEFAULT '',
49+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
50+
processed_text TEXT NOT NULL,
51+
search_vector tsvector GENERATED ALWAYS AS (
52+
to_tsvector('simple', coalesce(title, '') || ' ' || coalesce(processed_text, ''))
53+
) STORED
54+
);
55+
```
56+
57+
なお、全文検索エンジンあるあるテーマが日本語対応で、大抵は英語のようにスペース区切りの単語で分割し、転置インデックスという、単語→ドキュメントのインデックスを作り、それを元に検索をするという仕組みです。その過程で、英語やドイツ語などの言語ごとに正規化(ステミング)、冠詞などのノイズになる単語(stop word)をフィルタリングなど、自然言語ごとの前処理を行います。そのあたりの設定は[ここ](https://www.postgresql.org/docs/current/textsearch-dictionaries.html)に書かれています。
58+
59+
標準でサポートしている言語は17.5で以下のような感じです。
60+
61+
```sh
62+
/usr/local/share/postgresql/tsearch_data # ls *.stop
63+
danish.stop finnish.stop hungarian.stop norwegian.stop spanish.stop
64+
dutch.stop french.stop italian.stop portuguese.stop swedish.stop
65+
english.stop german.stop nepali.stop russian.stop turkish.stop
66+
```
67+
68+
デフォルトでは日本語のようなスペース区切りではない言語は対応できません。当然日本語の文法ルールもなく、stop wordの辞書もありません。この機能も、昔は日本語対応のtextsearch_jaというモジュールがあったようですが、今はメンテナンスされていないようです。仮にされていたとしても、DB運用はクラウドにお任せ時代なので最初から導入されている方が運用は楽でしょう。
69+
70+
なんとかして使ってみる方法として、事前に、DBの外でスペース区切りにしてからテーブルに入れてみるという前処理をする方法を試してみます。
71+
72+
# GoでPostgreSQLの全文検索で日本語検索してみる
73+
74+
Goでは分かち書きのライブラリとして定評のある[github.com/ikawaha/kagome/v2](https://pkg.go.dev/github.com/ikawaha/kagome/v2)を使います。
75+
76+
kagomeではこんな感じではこんな感じに
77+
78+
```sh
79+
$ kagome
80+
シグナルを送信した
81+
シグナル 名詞,一般,*,*,*,*,シグナル,シグナル,シグナル
82+
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
83+
送信 名詞,サ変接続,*,*,*,*,送信,ソウシン,ソーシン
84+
し 動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
85+
た 助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
86+
```
87+
88+
助詞とか助動詞はひかっかりまくるので削除し、動詞などは原形にすることで、「送信する」「送信した」などの表記揺れでもひっかかるようになります。分かち書きでは品詞もわかるのでこれを使って、助詞、助動詞、副詞などを除外すれば良いでしょう。簡単ですね。
89+
90+
# 実装
91+
92+
ソースコードは[github.com/shibukawa/pgtfs](https://github.com/shibukawa/pgtfs)です。生成AIでサッと作りました。ライセンスはUnlicenseです。full text searchだとftsのはずですが、スペルを間違ったのでtfsになってます。CLIツールとなっています。実証実験のコードなので、インデックスのメンテナンスとか考えずに、一発投入のみ。
93+
94+
```sh
95+
# PostgreSQLの起動
96+
$ docker compose up
97+
98+
# DBを初期化して./articles以下のテキストファイルをDBに投入
99+
$ ./pgtfs init
100+
101+
# 検索
102+
$ ./pgtfs search "検索用語"
103+
```
104+
105+
サンプルドキュメントも生成AIが用意してくれました。
106+
107+
```md golang.md
108+
# Goプログラミング言語
109+
110+
Goは、Googleが開発したプログラミング言語です。
111+
112+
## 特徴
113+
- シンプルな文法
114+
- 高速なコンパイル
115+
- 優れた並行処理機能
116+
- ガベージコレクション
117+
118+
## 活用分野
119+
Goは以下の分野で広く使用されています:
120+
- Webサービス開発
121+
- クラウドインフラストラクチャ
122+
- コマンドラインツール
123+
- マイクロサービス
124+
125+
ゴルーチンとチャネルを使った並行プログラミングが魅力的です。
126+
```
127+
128+
内部では次のように区切られてスペース区切りにされて保存されます。
129+
130+
```text
131+
#|Go|プログラミング|言語|Go|Google|開発|し|プログラミング|言語|
132+
##|特徴|-|シンプル|文法|-|高速|コンパイル|-|優れ|並行|処理|機能|
133+
-|ガベージコレクション|##|活用|分野|Go|以下|分野|広く|使用|さ|れ|い|
134+
-|Web|サービス|開発|-|クラウドインフラストラクチャ|-|コマンドラインツール|
135+
-|マイクロ|サービス|ゴルーチン|チャネル|使っ|並行|プログラミング|魅力|的
136+
```
137+
138+
「Web開発」で検索します。この単語は文中にはありません。「Webサービス開発」ならあります。うまく分かち書きされると単語がヒットしてくれるはず!!!
139+
140+
```shell
141+
$ ./pgtfs search "Web開発"
142+
Searching for: "Web開発"
143+
Found 1 articles:
144+
145+
=== Result 1 (Rank: 0.0989) ===
146+
Title: golang
147+
File: articles/golang.md
148+
```
149+
150+
きた!
151+
152+
# より使いやすい検索にするには
153+
154+
分かち書きとstop wordは最低限です。実際にElastichsearchで使われるKuromojiではそれ以外に漢数字を算用数字にしたりさまざまなフィルタを提供しています。
155+
156+
* [ZOZO TECH BLOG: Elasticsearchで日本語検索を扱うためのマッピング定義](https://techblog.zozo.com/entry/elasticsearch-mapping-config-for-japanese-search#kuromojiプラグイン機能)
157+
158+
これにより、表記揺れでもヒットしやすいようになります。PostgreSQLの検索機能も類義語辞書が持てるようになっているため、日本語でも頑張って集めることでさらに精度を上げる余地があります。最終的にはベクトル検索を実装して類似文書検索ですかね。いつかはやってみたい。
159+
160+
# まとめ
161+
162+
かんたんな前処理でPostgreSQLの標準の検索機能が使えました。これでコストを増やさずに全文検索機能が簡単に組み込めるでしょう。bigmなんかは正規の単語の区切りではないところでもひっかかってしまったりというのがあり、やはりきちんと分かち書きをした検索エンジンが使いたいですよね?どのような言語でもたいてい分かち書きのライブラリはあると思うので言語問わず利用できると思います。
163+
164+
[PostgreSQLにはPub/Sub機能もある](/articles/20240628a/)ので、DBさえあれば他のマネージドサービスは要らない、という時代がそのうち来るんじゃないかと思っているところです。
168 KB
Loading

source/images/20250829a/top.png

597 KB
Loading

0 commit comments

Comments
 (0)