Chapter sebelumnya menutup dengan satu fakta yang mengganjal: cast literal 'running'::tsvector @@ 'run'::tsquery menghasilkan false karena cast tidak menormalkan apa pun. Dokumen nyata berupa kalimat utuh, dan kueri nyata berupa teks yang diketik pengguna — keduanya harus melewati fungsi konversi agar jatuh ke lexeme yang sama. Fungsi itu adalah to_tsvector untuk dokumen dan to_tsquery untuk kueri, dan keduanya sama-sama menerima satu argumen tambahan yang menentukan nasib normalisasi: konfigurasi bahasa. Pada chapter ini kita membedah kerja kedua fungsi tersebut, memilih konfigurasi secara jujur untuk teks Indonesia, dan mengenal tiga fungsi pembantu yang mengubah teks polos menjadi kueri yang valid tanpa operator manual.
to_tsvector: Tokenisasi dan Normalisasi #
to_tsvector(konfigurasi, teks) menjalankan tiga tahap berurutan pada teks mentah. Parser memecah teks menjadi token, lalu kamus yang dipilih konfigurasi menormalkan setiap token — menyeragamkan huruf kecil, membuang stopword, dan mereduksi ke akar kata — dan hasilnya disimpan sebagai tsvector berikut posisi token aslinya. Perbandingan dua konfigurasi pada teks yang sama memperlihatkan betapa besar pengaruh pilihan konfigurasi:
-- 'english': stemming + pembuangan stopword. 'simple': hanya lowercase dan urut.
SELECT to_tsvector('english', 'The cats are running') AS konfigurasi_english,
to_tsvector('simple', 'The cats are running') AS konfigurasi_simple;
-- Semua bentuk infleksi dari akar yang sama jatuh menjadi satu lexeme.
SELECT to_tsvector('english', 'cat cats run runs running') AS bentuk_kata_runtuh;Output yang Dihasilkan:
konfigurasi_english | konfigurasi_simple
---------------------+--------------------------------------
'cat':2 'run':4 | 'are':3 'cats':2 'running':4 'the':1
(1 row)
bentuk_kata_runtuh
-----------------------
'cat':1,2 'run':3,4,5
(1 row)Dua hasil ini layak dibedah per baris:
- Baris pertama, kolom
konfigurasi_english:'cat':2 'run':4. KataThedanaredibuang sebagai stopword bahasa Inggris,catsmenjadicat, danrunningmenjadirun. Posisi tetap mengacu pada token asli —catberasal dari kata kedua,rundari kata keempat — sehingga informasi urutan kalimat tidak hilang meski kata di tengah dibuang. - Baris pertama, kolom
konfigurasi_simple:'are':3 'cats':2 'running':4 'the':1. Konfigurasisimplehanya menyeragamkan huruf kecil lalu menyimpan setiap token apa adanya; tidak ada stopword yang dibuang dan tidak ada stemming. Untuk pencarian kata kunci yang presisi, perilaku seminim ini justru bisa menjadi nilai plus. - Baris kedua: lima kata berbeda (
cat,cats,run,runs,running) runtuh menjadi dua lexeme —'cat':1,2 'run':3,4,5. Inilah mekanisme yang membuat pencarian satu kata mampu menangkap semua bentuk infleksinya sekaligus.
Konfigurasi Bahasa: simple, english, indonesian #
Konfigurasi adalah paket bernama yang berisi parser plus rangkaian kamus per jenis token. PostgreSQL 17 mengirimkan 29 konfigurasi siap pakai yang daftarnya bisa diperiksa langsung di catalog pg_ts_config:
-- Daftar konfigurasi yang terpasang bisa diperiksa di catalog pg_ts_config.
SELECT cfgname AS konfigurasi FROM pg_ts_config
WHERE cfgname IN ('simple', 'english', 'indonesian', 'malaysia')
ORDER BY cfgname;
-- 'indonesian' melakukan stemming bahasa Indonesia; 'simple' membiarkan kata utuh.
SELECT to_tsvector('indonesian', 'Kami sedang belajar PostgreSQL di kelas') AS konfigurasi_indonesian,
to_tsvector('simple', 'Kami sedang belajar PostgreSQL di kelas') AS konfigurasi_simple;
-- Bentuk-bentuk kata berkerabat pada akar yang sama: semuanya menjadi 'ajar'.
SELECT to_tsvector('indonesian', 'belajar mengajar pelajaran') AS satu_akar;
-- Stemming Indonesia juga punya sisi kasar: kata tidak berkerabat ikut terpotong.
SELECT to_tsvector('indonesian', 'sekolah menekankan mengolah') AS potongan_berlebih;Output yang Dihasilkan:
konfigurasi
-------------
english
indonesian
simple
(3 rows)
konfigurasi_indonesian | konfigurasi_simple
--------------------------------------------------------------+-----------------------------------------------------------------
'ajar':3 'di':5 'kami':1 'kelas':6 'postgresql':4 'sedang':2 | 'belajar':3 'di':5 'kami':1 'kelas':6 'postgresql':4 'sedang':2
(1 row)
satu_akar
--------------
'ajar':1,2,3
(1 row)
potongan_berlebih
-----------------------------
'ekan':2 'mengo':3 'seko':1
(1 row)Hasilnya memberi dasar pemilihan yang jujur untuk korpus kita:
- Baris pertama: dari empat nama yang diuji, tiga benar-benar terpasang —
english,indonesian, dansimple— sementaramalaysiatidak ada. PostgreSQL tidak menebak-nebak nama konfigurasi; nama yang tidak dikenal langsung ditolak (kita lihat error-nya sebentar lagi). - Baris kedua: konfigurasi
indonesianmereduksibelajarmenjadiajar, sementarasimplemenyimpanbelajarutuh. Perhatikan juga bahwadi,kami, dansedangtetap disimpan olehindonesian— PostgreSQL mengirimkan stemmer bahasa Indonesia, tetapi tanpa daftar stopword bahasa Indonesia, sehingga kata-kata umum itu ikut tersimpan sebagai lexeme. - Baris ketiga:
belajar,mengajar, danpelajaranruntuh menjadi satu lexeme'ajar':1,2,3— janji utama full-text search untuk bahasa Indonesia. - Baris keempat: sisi kasarnya.
sekolahmenjadiseko,menekankanmenjadiekan, danmengolahmenjadimengo. Stemmer bahasa Indonesia bawaan kadang memotong berlebihan sehingga kata yang tidak berkerabat bisa berakhir pada lexeme yang sama.
Untuk korpus artikel kita — bahasa Indonesia yang dicampur istilah Inggris — chapter berikutnya akan konsisten memakai konfigurasi simple: tanpa stemming, perilakunya mudah ditebak, dan kata campuran seperti JSONB atau running tersimpan apa adanya. Konfigurasi indonesian layak diuji kalau recall bentuk kata lebih penting daripada presisi, dan pembahasan cara mengendalikan konfigurasi secara lebih dalam — termasuk stopword sendiri — menempati chapter Konfigurasi Bahasa.
Mencoba konfigurasi yang tidak ada tidak berakhir diam-diam, melainkan dengan error yang menyebut nama konfigurasi yang dicari:
-- Nama konfigurasi yang tidak ada ditolak dengan error.
SELECT to_tsvector('malaysia', 'selamat datang');Output yang Dihasilkan:
psql:konfigurasi-error.sql:2: ERROR: text search configuration "malaysia" does not exist
LINE 1: SELECT to_tsvector('malaysia', 'selamat datang');
^to_tsquery: Kueri dengan Operator Manual #
to_tsquery(konfigurasi, teks) menormalkan setiap token input dengan konfigurasi yang sama seperti to_tsvector, lalu merangkainya mengikuti operator yang Anda tulis sendiri:
-- Sisi kueri ikut dinormalkan: "running" dan "cats" menjadi lexeme akarnya.
SELECT to_tsquery('english', 'running') AS satu_kata,
to_tsquery('english', 'cats | running') AS dua_kata;
-- Operator AND, OR, dan NOT tetap ditulis manual di input.
SELECT to_tsquery('english', 'cat & !dog') AS logika;
-- Operator frasa dan jarak juga dikenal oleh to_tsquery.
SELECT to_tsquery('simple', 'belajar <-> php') AS frasa,
to_tsquery('simple', 'belajar <2> php') AS jarak;Output yang Dihasilkan:
satu_kata | dua_kata
-----------+---------------
'run' | 'cat' | 'run'
(1 row)
logika
----------------
'cat' & !'dog'
(1 row)
frasa | jarak
---------------------+---------------------
'belajar' <-> 'php' | 'belajar' <2> 'php'
(1 row)Perhatikan kolom dua_kata: input cats | running keluar sebagai 'cat' | 'run' — normalisasi berjalan dulu, operator merangkai hasilnya. Karena operator wajib ditulis manual, teks pengguna yang polos langsung ditolak parser:
-- to_tsquery menuntut operator di antara setiap token.
SELECT to_tsquery('simple', 'belajar php');Output yang Dihasilkan:
psql:to-tsquery-error.sql:2: ERROR: syntax error in tsquery: "belajar php"Error yang sama dengan chapter sebelumnya — dan inilah alasan tiga fungsi pembantu berikutnya ada.
Tiga Pembantu untuk Teks Polos #
| Fungsi | Input yang diterima | Hubungan antar kata | Kekhususan |
|---|---|---|---|
to_tsquery | token + operator manual | sesuai operator yang ditulis | kendali penuh, teks polos ditolak |
plainto_tsquery | teks polos | semua kata di-AND-kan | paling toleran terhadap simbol |
phraseto_tsquery | teks polos | kata dirangkai sebagai frasa berurutan | urutan dan jarak dihitung |
websearch_to_tsquery | gaya kotak pencarian | kata di-AND-kan | mendukung "frasa", OR, dan -pengecualian |
Kita uji ketiganya pada input yang sama supaya perbedaannya terlihat bersebelahan:
-- plainto_tsquery: semua kata di-AND-kan, karakter operator diabaikan.
SELECT plainto_tsquery('simple', 'belajar php') AS plain_biasa,
plainto_tsquery('simple', 'belajar & php') AS plain_simbol;
-- phraseto_tsquery: kata dirangkai sebagai frasa berurutan.
SELECT phraseto_tsquery('simple', 'belajar php pemula') AS frasa_panjang;
-- Stopword yang dibuang tetap dihitung sebagai jarak antar kata.
SELECT phraseto_tsquery('english', 'The cat and the rats') AS frasa_stopword;
-- websearch_to_tsquery: sintaks ala kotak pencarian mesin pencari.
SELECT websearch_to_tsquery('simple', 'belajar php') AS kata_biasa,
websearch_to_tsquery('simple', '"belajar php"') AS frasa_kutip,
websearch_to_tsquery('simple', 'belajar OR php') AS operator_atau,
websearch_to_tsquery('simple', 'belajar -php') AS pengecualian;Output yang Dihasilkan:
plain_biasa | plain_simbol
-------------------+-------------------
'belajar' & 'php' | 'belajar' & 'php'
(1 row)
frasa_panjang
----------------------------------
'belajar' <-> 'php' <-> 'pemula'
(1 row)
frasa_stopword
-----------------
'cat' <3> 'rat'
(1 row)
kata_biasa | frasa_kutip | operator_atau | pengecualian
-------------------+---------------------+-------------------+--------------------
'belajar' & 'php' | 'belajar' <-> 'php' | 'belajar' | 'php' | 'belajar' & !'php'
(1 row)Empat baris hasil ini merangkum karakter masing-masing fungsi:
plain_biasadanplain_simbol: keduanya menghasilkan'belajar' & 'php'.plainto_tsquerymemecah input berdasarkan katanya; simbol&pada input tidak dibaca sebagai operator, jadi teks pengguna yang kebetulan berisi karakter istimewa tidak merusak kueri.frasa_panjang: tiga kata dirangkai'belajar' <-> 'php' <-> 'pemula'. Frasa hanya cocok bila ketiga kata muncul berurutan persis seperti ditulis.frasa_stopword:'cat' <3> 'rat'. Katathedananddibuang dari kueri, tetapi posisinya tetap dihitung sebagai jarak —ratmemang berjarak tepat tiga posisi daricatdalam kalimat aslinya. Tanpa kehati-hatian ini, frasa yang dipisah stopword tidak akan pernah cocok.- Baris
websearch: satu fungsi, empat perilaku. Kata biasa di-AND-kan, kutip ganda menjadi frasa,ORmenjadi alternatif, dan tanda minus menjadi pengecualian — persis kebiasaan pengguna mesin pencari.
Dokumen dan kueri kini sama-sama bisa dibangun dari teks mentah. Chapter berikutnya, Pencarian Teks, merangkai semuanya menjadi alur kerja lengkap pada tabel artikel: pencarian di kolom gabungan, pengurutan hasil berdasarkan skor ts_rank, cuplikan hasil dengan ts_headline, hingga pola kolom tsvector yang tersimpan otomatis.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
konfigurasi-error.sql
Unduh berkas-- Nama konfigurasi yang tidak ada ditolak dengan error.
SELECT to_tsvector('malaysia', 'selamat datang');
konfigurasi.sql
Unduh berkas-- Daftar konfigurasi yang terpasang bisa diperiksa di catalog pg_ts_config.
SELECT cfgname AS konfigurasi FROM pg_ts_config
WHERE cfgname IN ('simple', 'english', 'indonesian', 'malaysia')
ORDER BY cfgname;
-- 'indonesian' melakukan stemming bahasa Indonesia; 'simple' membiarkan kata utuh.
SELECT to_tsvector('indonesian', 'Kami sedang belajar PostgreSQL di kelas') AS konfigurasi_indonesian,
to_tsvector('simple', 'Kami sedang belajar PostgreSQL di kelas') AS konfigurasi_simple;
-- Bentuk-bentuk kata berkerabat pada akar yang sama: semuanya menjadi 'ajar'.
SELECT to_tsvector('indonesian', 'belajar mengajar pelajaran') AS satu_akar;
-- Stemming Indonesia juga punya sisi kasar: kata tidak berkerabat ikut terpotong.
SELECT to_tsvector('indonesian', 'sekolah menekankan mengolah') AS potongan_berlebih;
query-pembantu.sql
Unduh berkas-- plainto_tsquery: semua kata di-AND-kan, karakter operator diabaikan.
SELECT plainto_tsquery('simple', 'belajar php') AS plain_biasa,
plainto_tsquery('simple', 'belajar & php') AS plain_simbol;
-- phraseto_tsquery: kata dirangkai sebagai frasa berurutan.
SELECT phraseto_tsquery('simple', 'belajar php pemula') AS frasa_panjang;
-- Stopword yang dibuang tetap dihitung sebagai jarak antar kata.
SELECT phraseto_tsquery('english', 'The cat and the rats') AS frasa_stopword;
-- websearch_to_tsquery: sintaks ala kotak pencarian mesin pencari.
SELECT websearch_to_tsquery('simple', 'belajar php') AS kata_biasa,
websearch_to_tsquery('simple', '"belajar php"') AS frasa_kutip,
websearch_to_tsquery('simple', 'belajar OR php') AS operator_atau,
websearch_to_tsquery('simple', 'belajar -php') AS pengecualian;
to-tsquery-error.sql
Unduh berkas-- to_tsquery menuntut operator di antara setiap token.
SELECT to_tsquery('simple', 'belajar php');
to-tsquery.sql
Unduh berkas-- Sisi kueri ikut dinormalkan: "running" dan "cats" menjadi lexeme akarnya.
SELECT to_tsquery('english', 'running') AS satu_kata,
to_tsquery('english', 'cats | running') AS dua_kata;
-- Operator AND, OR, dan NOT tetap ditulis manual di input.
SELECT to_tsquery('english', 'cat & !dog') AS logika;
-- Operator frasa dan jarak juga dikenal oleh to_tsquery.
SELECT to_tsquery('simple', 'belajar <-> php') AS frasa,
to_tsquery('simple', 'belajar <2> php') AS jarak;
to-tsvector.sql
Unduh berkas-- 'english': stemming + pembuangan stopword. 'simple': hanya lowercase dan urut.
SELECT to_tsvector('english', 'The cats are running') AS konfigurasi_english,
to_tsvector('simple', 'The cats are running') AS konfigurasi_simple;
-- Semua bentuk infleksi dari akar yang sama jatuh menjadi satu lexeme.
SELECT to_tsvector('english', 'cat cats run runs running') AS bentuk_kata_runtuh;