Rabbit Slide Show

PostgreSQLでのセマンティックサーチへの挑戦

2025-11-21

Description

従来のキーワード検索は同義語や曖昧表現に弱く、必要な情報を見落としがちです。 本講演ではこの課題に対し、意味的な近さで探せるセマンティックサーチに挑戦します。 量子化と補正で軽量・高速な近似距離計算を実現するRaBitQをGroongaに実装し、PGroongaを通してPostgreSQLから利用可能にしました。 その仕組みと導入手順、FAQ/ナレッジ検索での活用例を交え、検索が使えるようになるまでの流れを紹介します。

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

Other slides