Text
Page: 1
PostgreSQLでの
セマンティックサーチへの挑戦
株式会社クリアコード
堀本 泰弘
PostgreSQL Conference Japan 2025
阿部 智晃
2025-11-21
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 2
本日のゴール
PostgreSQLで簡単に
セマンティックサーチ
が使えることを
実感してもらう
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 3
本日の内容
1. キーワード検索とセマンティックサーチの
比較
2. セマンティックサーチを実現するために
3. PostgreSQLでセマンティクサーチ
4. 性能とユースケース
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 4
本日の内容
1. キーワード検索とセマンティックサーチの
比較
2. セマンティックサーチを実現するために
3. PostgreSQLでセマンティクサーチ
4. 性能とユースケース
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 5
キーワード検索の利点と課題
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 6
キーワード検索の利点と課題
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 7
キーワード検索の利点と課題
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 8
キーワード検索の利点と課題
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 9
キーワード検索の利点と課題
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 10
セマンティックサーチの利点と課題
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 11
本日の内容
1. キーワード検索とセマンティックサーチの
比較
2. セマンティックサーチを実現するために
3. PostgreSQLでセマンティクサーチ
4. 性能とユースケース
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 12
セマンティックサーチに必要な技術
1. テキスト -> ベクトルデータへの変換
2. ベクトルデータの類似度の計算
3. ベクトルデータの効率的な検索
4. ベクトルデータの圧縮
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 13
テキスト -> ベクトルデータへの変換
意味で検索するとは?
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 14
テキスト -> ベクトルデータへの変換
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 15
テキスト -> ベクトルデータへの変換
意味をベクトル
として表現できる
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 16
テキスト -> ベクトルデータへの変換
ベクトルは
距離を計算できる
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 17
ベクトルデータの類似度の計算
どれだけ似ているか
- ベクトル間の距離が近い = 似ている
- ベクトル間の距離が遠い = 似ていない
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 18
ベクトルデータの類似度の計算
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 19
ベクトルデータの類似度の計算
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 20
ベクトルデータの効率的な検索
全てのベクトルと類似
度を計算するのか?
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 21
ベクトルデータの効率的な検索
線形探索は
現実的ではない
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 22
ベクトルデータの効率的な検索
一部のベクトルデータ
だけを検索
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 23
ベクトルデータの効率的な検索(イメージ)
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 24
ベクトルデータの効率的な検索(イメージ)
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 25
ベクトルデータの効率的な検索(イメージ)
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 26
ベクトルデータの効率的な検索(イメージ)
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 27
ベクトルデータの圧縮
自然言語を変換した
ベクトルデータは
大きい
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 28
ベクトルデータの圧縮
次元数:大
▶データサイズ:大
▶メモリーに乗らない
▶ 性能劣化
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 29
ベクトルデータの圧縮
データ量の削減が必要
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 30
圧縮方法
主な圧縮方法
1. ベクトルの次元を削減
2. ベクトルの量子化
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 31
圧縮方法
どちらの方法も
圧縮前のベクトルの
特徴をなるべく維持
して圧縮
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 32
本日の内容
1. キーワード検索とセマンティックサーチの
比較
2. セマンティックサーチを実現するために
3. PostgreSQLでセマンティクサーチ
4. 性能とユースケース
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 33
PostgreSQLのセマンティクサーチ
セマンティックサーチを使うには?
pgvectorを使う
PGroongaを使う
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 34
pgvectorとは?
類似したベクトルデータを検索する機能を提
供するPostgreSQLの拡張機能
類似度の計算方法が多数提供されている
インデックスを使用して検索を高速化できる
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 35
PGroonga(ぴーじーるんが)とは?
全言語対応の超高速全文検索機能を提供する
拡張
PostgreSQLのインデックスとして使える
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 36
PGroonga(ぴーじーるんが)とは?
PostgreSQLのデータを使って全文検索する
= ゼロETLで利用できる
PostgreSQLの構文をほぼそのまま使える
= 学習コストが低い
4.0.5からセマンティックサーチが使える
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 37
pgvectorとPGroongaの違い
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 38
pgvectorとPGroongaの違い
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 39
テキスト -> ベクトルデータへの変換
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 40
pgvectorのメリット
ベクトルデータへ変換するサービスは複数あ
るので、用途に合わせた組み合わせができる
ベクトルデータ変換と検索でリソースを分割
できる
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 41
pgvectorのデメリット
変換したベクトルデータをシステム間でやり
とりする必要がある
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 42
PGroongaのメリット
ベクトルデータをシステム間でやりとりする
必要がない
RaBitQというアルゴリズムを使って、デー
タサイズを少なくできる
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 43
PGroongaのデメリット
ベクトルデータへの変換は固定的
ベクトルデータ変換と検索でリソースが同一
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 44
RaBitQとは?
ベクトルデータの
量子化手法の一つ
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 45
RaBitQを採用した理由
データ量を大幅に
少なく出来る
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 46
データ量を大幅に少なく出来る
各32bit浮動少数点数を
1ビットで表現
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 47
データ量を大幅に少なく出来る
単純にデータサイズは
1/32になる
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 48
pgvectorとPGroongaでデータ登録と検索
Page: 49
pgvectorのデータ登録と検索
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE contents_for_pgvector (
content text,
content_embedding vector(384)
);
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 50
pgvectorのデータ登録と検索
INSERT INTO contents_for_pgvector (content, content_embedding)
SELECT 'I am a boy', (pgroonga_language_model_vectorize(
'hf:///groonga/all-MiniLM-L6-v2-Q4_K_M-GGUF',
'I am a boy'));
INSERT INTO contents_for_pgvector (content, content_embedding)
SELECT 'I am a dog', (pgroonga_language_model_vectorize(
'hf:///groonga/all-MiniLM-L6-v2-Q4_K_M-GGUF',
'I am a dog'));
INSERT INTO contents_for_pgvector (content, content_embedding)
SELECT 'I am a king', (pgroonga_language_model_vectorize(
'hf:///groonga/all-MiniLM-L6-v2-Q4_K_M-GGUF',
'I am a king'));
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 51
pgvectorのデータ登録と検索
SELECT * FROM contents_for_pgvector;
-- I am a boy | [-0.05031514,0.10813845,...,-0.12843993]
-- I am a dog | [-0.03515085,-0.0059523215,...,-0.024966048]
-- I am a king | [-0.030026972,0.057919234,...,-0.09476562]
-- (3 行)
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 52
pgvectorのデータ登録と検索
SELECT contents_for_pgvector.content,
(contents_for_pgvector.content_embedding <#> query_embedding.query) * -1 AS inner_product
FROM contents_for_pgvector,
(
SELECT CAST(pgroonga_language_model_vectorize(
'hf:///groonga/all-MiniLM-L6-v2-Q4_K_M-GGUF',
'boy') AS vector) AS query
) query_embedding
ORDER BY inner_product DESC;
--
content
|
inner_product
-- -------------+--------------------
-- I am a boy | 0.5909400582313538
-- I am a dog | 0.2753480076789856
-- I am a king | 0.1733587086200714
-- (3 行)
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 53
PGroongaのインデックス登録
DROP INDEX IF EXISTS pgroonga_content_semantic_search_index;
CREATE INDEX pgroonga_content_semantic_search_index ON contents_for_pgroonga
USING pgroonga (content pgroonga_text_semantic_search_ops_v2)
WITH (plugins = 'language_model/knn',
model = 'hf:///groonga/multilingual-e5-large-Q4_K_M-GGUF');
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 54
PGroongaのデータ登録と検索
CREATE EXTENSION IF NOT EXISTS pgroonga;
CREATE TABLE contents_for_pgroonga (
content text
);
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 55
PGroongaのデータ登録と検索
INSERT INTO contents_for_pgroonga VALUES ('I am a boy');
INSERT INTO contents_for_pgroonga VALUES ('I am a dog');
INSERT INTO contents_for_pgroonga VALUES ('I am a king');
SELECT * FROM contents_for_pgroonga;
--
content
-- -------------
-- I am a boy
-- I am a dog
-- I am a king
-- (3 行)
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 56
PGroongaのデータ登録と検索
SELECT content, pgroonga_score(tableoid, ctid)
FROM contents_for_pgroonga
WHERE content &@* pgroonga_condition('boy');
--
content
|
pgroonga_score
-- -------------+--------------------
-- I am a boy | 0.8671597838401794
-- I am a king | 0.5496522784233093
-- I am a dog | 0.5450347065925598
-- (3 行)
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 57
PGroongaのデータ登録と検索
PGroongaが内部で保持している
ベクトルデータ
-- [1,1,"I am a boy","mjctvB1JDj5pur67C/ ... ICPMFQi7wSL8q9"],
-- [2,2,"I am a dog","BwGXu4YNUj2Yi5k9j+ ... nDPPL4ijySlqy7"],
-- [3,3,"I am a king","1lklOxiXvj2rSB69VG ... tbvVHmDr1wSFK9"]
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 58
本日の内容
1. キーワード検索とセマンティックサーチの
比較
2. PostgreSQLでセマンティクサーチ
3. セマンティックサーチを実現するために
4. 性能とユースケース
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 59
測定環境
GPUなし
PostgreSQL 18
PGroonga mainブランチ
pgvector v0.8.1
IVFFlatインデックス
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 60
測定に使ったデータ
Wikipediaのタイトル
1,477,194 件
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 61
測定項目
検索速度
ベクトルデータのサイズ
インデックス作成時間
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 62
検索速度: PGroonga
検証用のクエリ:
SET enable_seqscan = off;
SELECT COUNT(title) FROM wikipedia
ORDER BY title <&@*> pgroonga_condition('異世界転生して無双したよ')
LIMIT 5;
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 63
検索速度: pgvector
検証用のクエリ:
SET enable_seqscan = off;
SELECT COUNT(title) FROM wikipedia
ORDER BY embedding <-> '['異世界転生して無双したよ'のベクトル]'
LIMIT 5;
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 64
検索速度: 結果: PGroonga
5回実行した中央値: 108.868 ms
ベクトルの生成も含む
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 65
検索速度: 結果: pgvector
5回実行した中央値: 12.166 ms
ベクトルの生成は含まず
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 66
ベクトルデータサイズ
float4の384次元のベクトルデータ:
4バイト * 384 = 1,536バイト
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 67
PGroongaのベクトルデータサイズ
PGroongaではfloat4の384次元のベクトル
データを量子化
「iJDX5WFlJ7wwvR ...
+sHiH0KxDv1PoHT8=」という64バイトの
バイナリーで保持
64文字 = 64バイト
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 68
PGroongaのインデックス作成時間
PGroongaの場合インデックスの作成時間は
「ベクトル化 + インデックスの作成時間」
ベクトル化がすごく遅い
ただ、GPUの有無や、使う言語モデルによっ
ても大きく速度が変わる
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 69
ユースケース:
PostgreSQLのドキュメントを検索
テーブルを消す
クエリを検索
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 70
キーワード検索:結果
SELECT title, SUBSTRING(content FROM 0 FOR 10)
FROM jpug_doc_contents
WHERE content LIKE '%テーブル%消す%'
LIMIT 5;
--
title
|
substring
-- -------------------+------------------
-- contrib
| 付録F 追加で提供
-- ddl-priv
| 5.8. 権限
-- ddl-schemas
| 5.10. スキー
-- explicit-locking | 13.3. 明示的
-- extend-extensions | 36.17. 関連
-- (5 rows)
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 71
セマンティックサーチ:結果
PGroongaだと直接テキストで検索できる
SELECT title, SUBSTRING(content FROM 0 FOR 10)
FROM jpug_doc_contents
ORDER BY content <&@*> pgroonga_condition('テーブルを消す')
LIMIT 5;
--
title
|
substring
-- --------------------+---------------
-- tutorial-table
| 2.3. 新しいテ
-- ddl-basics
| 5.1. テーブル
-- ddl-alter
| 5.7. テーブル
-- sql-delete
| DELETEDEL
-- sql-droptablespace | DROP TABL
-- (5 rows)
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1
Page: 72
まとめ
PostgreSQLから簡単に
セマンティックサーチ
を使えるようになりま
した!
PostgreSQLでの セマンティックサーチへの挑戦
Powered by Rabbit 4.0.1