Full Text Searches
Di PostgreSQL, full-text search (FTS) berbeda dengan pencarian string biasa. FTS fokus pada kata / lexeme (unit makna setelah preprocess seperti stemming & stopword removal) — bukan pada pencocokan substring literal. Dengan FTS kita mencari apakah dokumen mengandung kata/kumpulan kata tertentu, dapat menggabungkan operasi boolean (AND/OR/NOT), memberi peringkat (ranking), dan mengekstrak snippet yang menonjolkan hasil (highlighting). Berikut penjelasan lengkap, langkah-langkah praktis, contoh kode, dan best practice.
1) Konsep dasar: tsvector, tsquery, lexeme, stemming, stop words
to_tsvector(config, text) → mengubah teks menjadi
tsvector(kumpulan lexeme + posisi).Proses: tokenisasi → lowercase → menghapus stop words → stemming (mengubah kata ke bentuk dasar/akar).
Contoh:
SELECT to_tsvector('english', 'A car, I want a car. I would not even mind having many cars');Hasil (contoh):
'car':2,6,14 'even':10 'mani':13 'mind':11 'want':4 'would':8carsdancardistem menjadicar.manymenjadimani(contoh stemming bahasa Inggris).
to_tsquery(config, 'some & query') → membuat
tsquerydari sintaks tsquery (membutuhkan operator & | ! <-> dsb.).plainto_tsquery(config, 'plain text') → mengubah teks biasa menjadi tsquery dengan operator
&antar kata (lebih mudah untuk input user yang bukan operator tsquery).phraseto_tsquery(config, 'phrase') → untuk pencarian frase (kata berurutan).
@@→ operator BOOLEAN yang memeriksa apakahtsvector @@ tsquery(apakah dokumen cocok dengan query).Lexeme = token yang distandarisasi (hasil stemming/dictionary). Contoh:
wantdanwanted→ sama menjadi lexemewant.
Catatan: konfigurasi bahasa (
'english','dutch','simple', dll.) mempengaruhi tokenisasi, stop words, dan stemming. Lihatpg_ts_configuntuk daftar konfigurasi tersedia:
2) Contoh perilaku stemming & perbandingan bahasa
Inggris:
Dutch (konfigurasi berbeda → tokenisasi/stopword/stemming berbeda):
3) Mencari & operator tsquery
Basic boolean dalam
to_tsquery:&: AND|: OR!: NOT<->: FOLLOWED BY (adjacency / kata bersebelahan) — berguna untuk frase/proximity
Contoh:
plainto_tsquerysangat cocok untuk input user biasa:
4) Indexing untuk Full-Text Search — dua pendekatan
Ada dua pola umum untuk mengindeks teks:
A. Functional index (lebih hemat ruang, lebih sederhana)
Buat index langsung dari ekspresi to_tsvector(...):
Kelebihan: tidak menambah kolom, lebih ringkas dari segi schema.
Kekurangan: setiap query yang memanfaatkan index harus mengeksekusi ekspresi; update index saat row berubah tetap terjadi; kadang sedikit lebih lambat dibanding materialized column pada runtime.
B. Materialized tsvector column + trigger + index (lebih cepat pada query baca)
Tambah kolom
tsbertipetsvector:
Isi kolom untuk baris existing:
Buat index pada kolom tersebut:
Buat trigger supaya
tsselalu ter-sync saat INSERT/UPDATE:
Pada versi PostgreSQL modern gunakan
EXECUTE FUNCTION, pada versi sangat lamaEXECUTE PROCEDURE.
tsvector_update_trigger(target_column, config, column1, column2, ...)Kelebihan: query baca lebih cepat (index langsung pada kolom).
Kekurangan: butuh ruang tambahan untuk kolom
ts, trigger menambah overhead saat write.
5) Contoh query FTS lengkap (dengan ranking & snippet)
Cari & urutkan hasil berdasarkan relevansi (
ts_rank) — contohplainto_tsquery:
Menggunakan weighting (beri bobot lebih besar ke kolom title vs body):
Ambil snippet / highlight hasil dengan
ts_headline:
6) Debugging & Statistik
ts_debug(config, text) → menampilkan tokenisation/dictionary/lexeme untuk tiap token — berguna untuk mengetahui mengapa query match/tidak match.
Output menunjukkan token, jenis token, dictionary yang dipakai, dan lexeme hasil stemming.
ts_stat(query_sql) → kumpulan statistik kata/lexeme (berapa dokumen mengandung kata, frekuensi total):
7) Pilihan operator & sintaks pencarian lanjutan
to_tsquery('english', 'foo & bar')→ match dokumen yang mengandung foo AND bar (lexeme setelah stemming).plainto_tsquery('english', 'foo bar')→ mempermudah: akan jadifoo & bar.phraseto_tsquery('english', 'exact phrase')→ membantu pencarian frase bersebelahan.<->dalamto_tsquery→ adjacency (kata bersebelahan, urutan memakai lexeme): contoh'foo <-> bar'.Ada juga fungsi modern seperti
websearch_to_tsquery(meniru sintaks search engines — dukungan pada versi PostgreSQL tertentu); ini memudahkan pengguna memakai kutipan/+, - dsb. (cek versi PostgreSQL Anda).
8) Index type: GIN vs GiST untuk FTS
GIN (Generalized Inverted Index) adalah yang paling umum dipakai untuk full-text search (
USING gin(ts)).Kelebihan: sangat cepat untuk pencarian, efisien mencari kata/lexeme di dokumen besar.
Kekurangan: update index bisa lebih berat (INSERT/UPDATE lebih mahal dibanding GiST), dan index bisa relatif besar.
GiST juga bisa digunakan (lebih fleksibel), tetapi untuk FTS GIN biasanya pilihan utama di production kecuali ada kebutuhan khusus.
Untuk trigram (
pg_trgm), GIN sering lebih baik untuk dataset besar; GiST kadang lebih cepat saat update, trade-off tergantung workload.
9) Maintenance, konsistensi & hal yang harus diperhatikan
Jika Anda mengubah konfigurasi bahasa, aturan stemming, atau dictionary → Anda harus rebuild tsvector dan reindex (
REINDEX) karena hasil stemming/dictionary berubah.Pilihan antara functional index vs materialized column:
Functional index: lebih hemat struktur, berguna jika update jarang.
Materialized
tsvector+ trigger: lebih cepat untuk query baca intensif, tapi menambah overhead pada penulisan.
Autovacuum & VACUUM: GIN index maintenance bisa memerlukan perhatian (bloating). Monitoring & reindexing berkala mungkin perlu.
Untuk performa ranking, gunakan
ts_rankatauts_rank_cddengan parameter normalisasi jika perlu.
10) Contoh alur kerja FTS (praktis, step-by-step)
Aktifkan extension (sekali per database):
Tambah tabel / data:
Tambah kolom
ts(materialized approach):Buat index:
Buat trigger untuk keep-in-sync:
Query pencarian:
11) Tips & best practice
Pilih konfigurasi bahasa sesuai teks (search dalam bahasa Indonesia? gunakan config yang sesuai atau
simplejika tidak ada konfig bahasa lokal).Gunakan plainto_tsquery untuk input raw user.
to_tsquerymengharuskan sintaks tsquery.Beratkan (weight) kolom penting (title > subtitle > body) dengan
setweightagar hasil relevansi lebih alami.Gunakan ts_headline untuk menampilkan potongan teks (highlight).
Monitoring: cek ukuran index, frekuensi update; GIN dapat bloat setelah banyak update.
Rebuild tsvector & reindex jika stop words / dictionaries diubah.
Gabungkan pg_trgm untuk fuzzy matching pada nama/kolom yang bukan cocok dengan FTS (mis. nama orang, alamat).
pg_trgmmempercepatLIKE '%...%', regex, dan similarity search.
12) Debug & analisis kenapa suatu query tidak match
ts_debug→ lihat tokenization & mapping dictionary untuk kata bermasalah.ts_stat→ lihat frekuensi lexeme untuk memahami kata umum vs jarang.Jika query tidak match: kemungkinan stopword dihapus, stemming mengubah bentuk, atau config bahasa salah.
13) Singkat: Perbandingan pg_trgm vs Full-Text Search
pg_trgm (trigram)
Tujuan: fuzzy matching / similarity / mempercepat
LIKE '%...%'dan regex.Cocok untuk: nama, alamat, autocomplete, typo tolerance.
Index: GiST (
gist_trgm_ops) atau GIN (gin_trgm_ops) — GIN sering lebih baik di dataset besar.
Full-Text Search (FTS)
Tujuan: pencarian dokumen/isi berbasis kata; mendukung stemming, stop words, ranking, phrase.
Cocok untuk: artikel, komentar, dokumen panjang, konten umum.
Index: GIN pada
tsvector(pola umum).
Keduanya saling melengkapi: untuk search bar modern Anda mungkin menggunakan FTS untuk dokumen + pg_trgm untuk nama/slug/typo tolerance.
14) (Tambahan) Exclusion operators — contoh singkat (terkait indexing GiST)
Walaupun bukan bagian inti FTS, buku juga menyebut exclusion constraints yang memanfaatkan GiST. Contoh: mencegah double booking ruangan.
EXCLUDE + GiST memberikan cara kuat untuk menjaga integritas yang bergantung pada operator seperti overlap (&&) atau geometric ops.
Penutup
Full-text search di PostgreSQL adalah fitur sangat kaya: preprocessing (tokenize, stopword, stemming), berbagai jenis query, ranking & headline, debugging tools, serta index (GIN/GiST) yang membuatnya skalabel di produksi. Untuk aplikasi nyata, pertimbangkan trade-off storage vs query speed (functional index vs materialized tsvector) dan pilih konfigurasi bahasa & opclass yang sesuai.
Last updated