Skip to main content
BlogAI/ML

Semantic Search dengan pgvector: Lewati Vector Database Khusus

Anda mungkin tidak butuh Pinecone. Cara membangun semantic search produksi dengan pgvector di PostgreSQL yang sudah Anda jalankan, lengkap dengan index dan hybrid search.

Matthews Wong

Software Engineer

Mei 202610 menit baca
Semantic Search dengan pgvector: Lewati Vector Database Khusus

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.

Mengapa Postgres Dulu Adalah Default yang Benar

Argumennya bukan bahwa pgvector mengalahkan engine khusus di semua benchmark — melainkan bahwa untuk workload yang sungguh dimiliki kebanyakan tim, matematika operasionalnya yang dominan:

  • Embedding Anda tinggal di sebelah data Anda. Filter per tenant, join ke tabel izin, dan menghapus vektor milik satu pelanggan adalah SQL biasa, bukan protokol sinkronisasi antara dua sistem yang bisa melenceng.
  • Anda sudah mengoperasikan Postgres: backup, replikasi, monitoring, point-in-time recovery. Vector database adalah sistem stateful kedua dengan semua kewajiban itu terduplikasi.
  • Plafon skalanya lebih tinggi dari kesan marketing. pgvector menangani vektor hingga 16.000 dimensi dan jutaan baris dengan nyaman di hardware biasa — kebanyakan korpus internal itu puluhan ribu chunk, bukan miliaran.

Seluruh Setup-nya Satu Migrasi

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.

HNSW atau IVFFlat: Satu-Satunya Keputusan Index

pgvector memberi dua tipe index approximate-nearest-neighbor. Panduan resminya jelas dan cocok dengan pengalaman saya:

FaktorHNSWIVFFlat
Kecepatan query dan recallTradeoff speed-recall lebih baik — pilihan defaultLebih rendah, bisa dituning lewat probes saat query
Waktu build dan memoriBuild lebih lambat, memori lebih besarBuild lebih cepat, lebih ringan memori
Butuh data trainingTidak — bisa dibuat di tabel kosongButuh baris eksisting untuk training k-means
Kapan saya memakainyaPencarian produksi, apa pun yang menghadap penggunaKorpus raksasa hasil batch-load di mana biaya build dominan

Hybrid Search: Upgrade Seharga Satu Query

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.

Catatan Operasional dari Produksi

  1. Bulk-load dulu, index belakangan. Membangun HNSW di tabel terisi jauh lebih cepat daripada insert lewat index yang sudah ada — drop dan buat ulang untuk backfill besar.
  2. Awasi maintenance_work_mem saat build index; build HNSW yang tidak muat akan spill dan melambat parah. Saya set per sesi sebelum build besar.
  3. Kolom embedding itu gemuk. Vektor 1.536 dimensi kira-kira 6 KB per baris, jadi sejuta chunk sekitar 6 GB sebelum index — rencanakan storage dan shared_buffers sesuai itu.
  4. Re-embedding adalah migrasi. Saat ganti model embedding, tulis vektor baru ke kolom kedua, backfill bertahap, tukar index, lalu drop kolom lama — zero-downtime, SQL biasa.

Kapan Anda Benar-Benar Melampauinya

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.

Kesimpulan

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.

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Mengapa memakai pgvector alih-alih vector database khusus seperti Pinecone?

Untuk kebanyakan tim, matematika operasional berpihak pada pgvector: embedding Anda tinggal di sebelah data relasional, sehingga filter tenant, join tabel izin, dan penghapusan cukup dengan SQL biasa tanpa protokol sinkronisasi antara dua sistem. Anda juga sudah mengoperasikan PostgreSQL lengkap dengan backup, replikasi, dan monitoring — vector database khusus menduplikasi semua kewajiban itu sebagai sistem stateful kedua.

Kapan sebaiknya memilih HNSW dibanding IVFFlat untuk index vektor?

HNSW adalah pilihan default untuk pencarian produksi yang menghadap pengguna karena menawarkan tradeoff speed-recall yang lebih baik dan bisa dibangun di tabel kosong tanpa data training. IVFFlat lebih cocok untuk korpus raksasa hasil batch-load di mana biaya build mendominasi, meski recall-nya bisa dituning lewat probes saat query dan build-nya lebih cepat dengan memori lebih sedikit.

Mengapa perlu menggabungkan full-text search dengan vector search?

Vector search murni melewatkan identifier eksak — nomor invoice, SKU, kode error — karena embedding mengaburkan string presisi. Menggabungkan pgvector dengan full-text search native PostgreSQL (hybrid search) memadukan skor semantik dan kata kunci dalam satu query SQL tanpa infrastruktur ekstra, dan penulis menemukan cara ini meningkatkan kualitas jawaban untuk retrieval dokumen ERP lebih dari pergantian model embedding mana pun.

Apa implikasi storage praktis saat menyimpan embedding di PostgreSQL?

Vektor 1.536 dimensi (default untuk text-embedding-3-small dari OpenAI) berukuran sekitar 6 KB per baris, sehingga satu juta chunk menghabiskan sekitar 6 GB sebelum index. Anda harus merencanakan storage dan shared_buffers sesuai itu, dan melakukan bulk-load data sebelum membangun HNSW index — bukan insert lewat index yang sudah ada — jauh lebih cepat untuk backfill besar.

Pada skala berapa sebaiknya migrasi dari pgvector ke engine vektor khusus?

Tulisan ini menyebut ambang batas jujurnya sekitar lima puluh juta vektor, kebutuhan replikasi index multi-region, ribuan query vektor per detik, atau kebutuhan recall yang memaksa tuning ekstrem yang berkelahi dengan workload database lainnya. Penulis mencatat ini hanya menggambarkan minoritas kecil produk, dan migrasi belakangan itu lurus saja karena hanya target penyimpanan yang berubah — pipeline embedding tetap sama.