Semantic Search dengan pgvector: Lewati Vector Database Khusus

Foto oleh Google DeepMind

Foto oleh Google DeepMind
Setiap proyek semantic search yang saya scoping dua tahun terakhir dimulai dengan asumsi sama: kita akan butuh vector database. Dan di hampir semuanya — pencarian dokumen internal, pencocokan record ERP, retrieval artikel support — jawaban yang benar ternyata instance PostgreSQL yang memang sudah berjalan, dengan ekstensi pgvector diaktifkan. Satu CREATE EXTENSION menuju vector search, nol layanan baru untuk dioperasikan.
Saya sudah membandingkan pgvector lawan Pinecone secara langsung di tulisan terdahulu; yang ini panduan membangunnya. Skema, pilihan index, query hybrid-search yang saya pakai ulang di mana-mana, dan angka operasional yang memberi tahu kapan Anda benar-benar melampaui Postgres.
Argumennya bukan bahwa pgvector mengalahkan engine khusus di semua benchmark — melainkan bahwa untuk workload yang sungguh dimiliki kebanyakan tim, matematika operasionalnya yang dominan:
Ini skema lengkap yang saya pakai, diukur untuk text-embedding-3-small dari OpenAI pada 1.536 dimensi default-nya — pilihan hemat biaya untuk kebanyakan workload retrieval, sekitar 62.500 halaman per dolar:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE documents (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
source TEXT NOT NULL,
chunk TEXT NOT NULL,
-- text-embedding-3-small: 1536 dims by default
embedding VECTOR(1536) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- HNSW: best query speed/recall tradeoff; build after bulk-loading
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops);
-- Top-8 nearest chunks by cosine distance
SELECT id, source, chunk, embedding <=> $1 AS distance
FROM documents
ORDER BY embedding <=> $1
LIMIT 8;Operatornya penting: pgvector mengirim enam operator jarak, dan untuk embedding teks ternormalisasi Anda mau cosine distance — operator mirip spaceship di query atas — dengan index vector_cosine_ops yang cocok. Mencampur operator dan opclass index adalah kesalahan senyap klasik: query tetap jalan, hanya saja tidak pernah memakai index.
pgvector memberi dua tipe index approximate-nearest-neighbor. Panduan resminya jelas dan cocok dengan pengalaman saya:
| Faktor | HNSW | IVFFlat |
|---|---|---|
| Kecepatan query dan recall | Tradeoff speed-recall lebih baik — pilihan default | Lebih rendah, bisa dituning lewat probes saat query |
| Waktu build dan memori | Build lebih lambat, memori lebih besar | Build lebih cepat, lebih ringan memori |
| Butuh data training | Tidak — bisa dibuat di tabel kosong | Butuh baris eksisting untuk training k-means |
| Kapan saya memakainya | Pencarian produksi, apa pun yang menghadap pengguna | Korpus raksasa hasil batch-load di mana biaya build dominan |
Vector search murni melewatkan identifier eksak — nomor invoice, SKU, kode error — karena embedding mengaburkan string presisi. Postgres punya solusinya bawaan: gabungkan pgvector dengan full-text search native dan campurkan skornya. Tanpa infrastruktur ekstra, hanya SQL:
-- Hybrid search: combine vector similarity with full-text rank
WITH semantic AS (
SELECT id, 1 - (embedding <=> $1) AS sim
FROM documents ORDER BY embedding <=> $1 LIMIT 30
),
keyword AS (
SELECT id, ts_rank(to_tsvector('english', chunk),
plainto_tsquery('english', $2)) AS rank
FROM documents
WHERE to_tsvector('english', chunk) @@ plainto_tsquery('english', $2)
LIMIT 30
)
SELECT d.id, d.chunk,
COALESCE(s.sim, 0) * 0.7 + COALESCE(kw.rank, 0) * 0.3 AS score
FROM documents d
LEFT JOIN semantic s ON s.id = d.id
LEFT JOIN keyword kw ON kw.id = d.id
WHERE s.id IS NOT NULL OR kw.id IS NOT NULL
ORDER BY score DESC LIMIT 8;Bobot 70/30 itu titik awal, bukan kitab suci — tuning terhadap set kecil query nyata yang dilabeli. Untuk use case dokumen ERP saya, hybrid menaikkan kualitas jawaban lebih dari pergantian model embedding mana pun, karena pengguna mencari kode dan nama sesering konsep.
Kekuatan super yang kurang dihargai adalah JOIN. Isolasi tenant, filter berbasis role, soft-delete, dan menyambungkan chunk hasil retrieval kembali ke record sumbernya terjadi dalam satu query plan yang sama dengan vector scan. Di vector store khusus, masing-masing menjadi akrobat metadata-filter dan round trip kedua ke database utama Anda.
Batas jujurnya: kalau Anda melewati kira-kira lima puluh juta vektor, butuh replikasi multi-region untuk index-nya sendiri, menahan ribuan query vektor per detik, atau kebutuhan recall Anda memaksa tuning ekstrem yang berkelahi dengan workload database lainnya, engine khusus layak biaya operasionalnya. Angka-angka itu menggambarkan minoritas kecil produk — dan migrasi belakangan itu lurus saja, karena pipeline embedding Anda tidak berubah, hanya target penyimpanannya.
Semantic search adalah fitur, bukan keputusan platform. Mulai dari database yang sudah Anda jalankan: satu ekstensi, satu migrasi, satu query hybrid, dan Anda punya retrieval kelas produksi dengan backup, auth, dan monitoring yang sudah ada. Beli vector database khusus saat pengukuran Anda yang bilang begitu, bukan saat diagram arsitektur vendor yang bilang.
Sumber dan bacaan lanjutan