Chapter to_tsvector dan to_tsquery selalu menyebut satu argumen tambahan sebelum teksnya: nama konfigurasi seperti 'simple' atau 'english'. Argumen itu bukan sekadar label — konfigurasi menentukan bagaimana teks diubah menjadi lexeme: kata mana yang dibuang sebagai stop word, bagaimana imbuh dipangkas, dan kamus apa yang dipakai untuk tiap jenis token. Pilihan yang salah membuat pencarian gagal secara diam-diam: query run tidak menemukan dokumen berisi running bila konfigurasinya simple, dan sebaliknya dokumen Indonesia yang diolah config english tidak mendapat stemming sama sekali. Chapter ini membedah isi sebuah konfigurasi, membandingkan perilaku english lawan simple, lalu menyoroti kabar baik untuk pembaca Indonesia: PostgreSQL memang menyertakan konfigurasi indonesian — sejak PostgreSQL 12.

Apa yang Dibundel Sebuah Text Search Config #

Sebuah text search configuration memaketkan dua komponen: sebuah parser yang memecah teks menjadi token berjenis (kata, bilangan, URL, host), dan pemetaan kamus — untuk setiap jenis token, daftar kamus yang berurutan mencoba mengenalinya. Kamus bisa berupa stop word list, stemmer seperti Snowball yang memangkas imbuhan, atau kamus sinonim. Token yang tidak dikenal kamus mana pun akan dibuang dari tsvector. Tabel contoh chapter ini sengaja mencampur artikel Indonesia dan Inggris:

sql buat-tabel.sql
-- Enam artikel: empat berbahasa Indonesia, dua berbahasa Inggris.
-- Campuran bahasa ini sengaja dipakai untuk membandingkan konfigurasi text search.
CREATE TABLE artikel (
    id      integer PRIMARY KEY,
    judul   text NOT NULL,
    isi     text NOT NULL
);

INSERT INTO artikel VALUES
 (1, 'Penjualan Kopi Naik',   'penjualan kopi naik karena pembeli membaca ulasan'),
 (2, 'Kopi Dijual Grosir',    'kopi dijual per kilogram untuk warung kopi'),
 (3, 'Kiat Membaca Pasar',    'membaca tren penjualan butuh waktu sebulan'),
 (4, 'Buku Harian Baru',      'pembaca setia menunggu edisi baru terbit'),
 (5, 'Database Tuning Guide', 'running databases perform better after careful tuning'),
 (6, 'Backup Strategy',       'nightly backups verify the cluster before shipping');

SELECT id, judul, isi
FROM artikel
ORDER BY id;

Output yang Dihasilkan:

text
CREATE TABLE
INSERT 0 6
 id |         judul         |                          isi                          
----+-----------------------+-------------------------------------------------------
  1 | Penjualan Kopi Naik   | penjualan kopi naik karena pembeli membaca ulasan
  2 | Kopi Dijual Grosir    | kopi dijual per kilogram untuk warung kopi
  3 | Kiat Membaca Pasar    | membaca tren penjualan butuh waktu sebulan
  4 | Buku Harian Baru      | pembaca setia menunggu edisi baru terbit
  5 | Database Tuning Guide | running databases perform better after careful tuning
  6 | Backup Strategy       | nightly backups verify the cluster before shipping
(6 rows)

Daftar konfigurasi bawaan tersimpan di katalog pg_ts_config dan bisa dilihat dua cara: perintah meta \dF di psql, atau query SQL langsung. Query SQL lebih cocok untuk materi ini karena hasilnya bisa diurutkan penuh:

-- Katalog konfigurasi text search bawaan tersimpan di pg_ts_config.
-- Query ini deterministik: daftarnya tetap untuk satu versi PostgreSQL.
SELECT count(*) AS total_config
FROM pg_ts_config;

-- Sebelas nama yang relevan untuk pemeriksaan: bahasa besar + kandidat Asia Tenggara.
SELECT cfgname
FROM pg_ts_config
WHERE cfgname IN ('simple', 'english', 'german', 'french', 'spanish',
                  'russian', 'arabic', 'hindi', 'tamil',
                  'indonesian', 'malay')
ORDER BY cfgname;

Output yang Dihasilkan:

text
 total_config 
--------------
           29
(1 row)

  cfgname   
------------
 arabic
 english
 french
 german
 hindi
 indonesian
 russian
 simple
 spanish
 tamil
(10 rows)

PostgreSQL 17 yang dipakai course ini membawa 29 konfigurasi bawaan. Dari sebelas nama yang diuji, sepuluh ditemukan — termasuk indonesian. Nama malay tidak ada, dan memang tidak pernah disediakan; kabar yang sering keliru dikutip adalah bahwa bahasa Indonesia juga tidak didukung. Catatan rilis PostgreSQL 12 mencatat penambahan stemmer Snowball untuk enam bahasa — Arab, Indonesia, Irlandia, Lituania, Nepal, dan Tamil — sehingga konfigurasi indonesian sudah tersedia sejak versi itu.

english vs simple: Stemming dan Stop Word #

Konfigurasi simple adalah perilaku minimum: semua token dijadikan lexeme apa adanya (hanya di-lowercase), tidak ada stop word, tidak ada pemangkasan imbuhan. Konfigurasi english mengaktifkan dua perlakuan ekstra — stemming bahasa Inggris dan pembuangan stop word Inggris. Kedua vektor berikut berasal dari teks yang persis sama:

-- Perilaku config 'english' dan 'simple' pada teks yang sama (isi baris 5).
SELECT to_tsvector('english', isi) AS vektor_english,
       to_tsvector('simple', isi)  AS vektor_simple
FROM artikel
WHERE id = 5;

-- Stop word: config 'english' membuang 'the' dan 'before' (isi baris 6).
SELECT to_tsvector('english', isi) AS vektor_english,
       to_tsvector('simple', isi)  AS vektor_simple
FROM artikel
WHERE id = 6;

-- Konsekuensi pencarian: query 'run' hanya cocok bila stemming aktif.
SELECT id, judul
FROM artikel
WHERE to_tsvector('english', judul || ' ' || isi) @@ plainto_tsquery('english', 'run')
ORDER BY id;

SELECT id, judul
FROM artikel
WHERE to_tsvector('simple', judul || ' ' || isi) @@ plainto_tsquery('simple', 'run')
ORDER BY id;

Output yang Dihasilkan:

text
                        vektor_english                        |                                   vektor_simple                                   
--------------------------------------------------------------+-----------------------------------------------------------------------------------
 'better':4 'care':6 'databas':2 'perform':3 'run':1 'tune':7 | 'after':5 'better':4 'careful':6 'databases':2 'perform':3 'running':1 'tuning':7
(1 row)

                    vektor_english                    |                                 vektor_simple                                  
------------------------------------------------------+--------------------------------------------------------------------------------
 'backup':2 'cluster':5 'night':1 'ship':7 'verifi':3 | 'backups':2 'before':6 'cluster':5 'nightly':1 'shipping':7 'the':4 'verify':3
(1 row)

 id |         judul         
----+-----------------------
  5 | Database Tuning Guide
(1 row)

 id | judul 
----+-------
(0 rows)

Mari kita bedah perbedaan yang tercetak:

  1. Stemming: running menjadi run, databases menjadi databas, careful menjadi care, tuning menjadi tune — konfigurasi english merapatkan kata berimbuhan ke bentuk dasarnya. Konfigurasi simple menyimpan running, databases, careful, tuning apa adanya.
  2. Stop word: after, the, dan before hilang sepenuhnya dari vektor english; konfigurasi simple menyimpan ketiganya sebagai lexeme biasa.
  3. Konsekuensi pencarian: query run di bawah english menemukan baris 5 karena running dirapatkan ke run. Query yang sama di bawah simple menemukan kosong — tabel tidak memuat kata run secara harfiah, hanya running.

Konfigurasi indonesian untuk Teks Indonesia #

Konfigurasi indonesian memakai stemmer Snowball bahasa Indonesia: imbuhan awalan seperti me-, ber-, pe- dan akhiran seperti -kan, -an, -nya dipangkas menuju akar kata. Efeknya langsung terasa pada data contoh:

-- Config 'indonesian': stemmer Snowball untuk bahasa Indonesia.
-- Bentuk turunan dirapatkan ke akar kata yang sama.
SELECT to_tsvector('indonesian', judul || ' ' || isi) AS vektor_indonesian
FROM artikel
WHERE id = 1;

-- Query 'jual' menemukan penjualan dan dijual sekaligus.
SELECT id, judul
FROM artikel
WHERE to_tsvector('indonesian', judul || ' ' || isi) @@ plainto_tsquery('indonesian', 'jual')
ORDER BY id;

-- Query 'baca' menemukan membaca dan pembaca.
SELECT id, judul
FROM artikel
WHERE to_tsvector('indonesian', judul || ' ' || isi) @@ plainto_tsquery('indonesian', 'baca')
ORDER BY id;

-- Batas stemmer: tiga bentuk kata 'masak' justru terpecah menjadi tiga lexeme berbeda.
SELECT to_tsvector('indonesian', 'memasak masak masakan') AS vektor_masak;

-- Batas kedua: tidak ada file stop word Indonesia, kata fungsi tetap diindeks.
SELECT to_tsvector('indonesian', 'yang dan di ke dari') AS vektor_stopword;

Output yang Dihasilkan:

text
                            vektor_indonesian                            
-------------------------------------------------------------------------
 'baca':9 'beli':8 'jual':1,4 'karena':7 'kopi':2,5 'naik':3,6 'ulas':10
(1 row)

 id |        judul        
----+---------------------
  1 | Penjualan Kopi Naik
  2 | Kopi Dijual Grosir
  3 | Kiat Membaca Pasar
(3 rows)

 id |        judul        
----+---------------------
  1 | Penjualan Kopi Naik
  3 | Kiat Membaca Pasar
  4 | Buku Harian Baru
(3 rows)

         vektor_masak         
------------------------------
 'masa':3 'masak':2 'pasak':1
(1 row)

             vektor_stopword             
-----------------------------------------
 'dan':2 'dari':5 'di':3 'ke':4 'yang':1
(1 row)

Vektor baris 1 memperlihatkan perapatan yang berhasil: penjualan dan dijual sama-sama menjadi jual, membaca dan pembaca menjadi baca, bahkan pembeli menjadi beli dan ulasan menjadi ulas. Query satu kata jual lalu menemukan tiga artikel sekaligus — hasil yang mustahil dicapai config simple. Namun dua query terakhir adalah pengingat bahwa stemmer ini murni algoritma tanpa kamus: memasak, masak, dan masakan justru terpecah menjadi pasak, masak, dan masa — tiga lexeme berbeda untuk satu akar kata. Selain itu PostgreSQL tidak menyertakan file stop word bahasa Indonesia, sehingga yang, dan, di, ke, dari tetap menempati posisi di tsvector dan ikut dicocokkan.

Praktik yang aman untuk teks Indonesia bisa dirangkum begini:

StrategiCara kerjaKapan dipilih
Config indonesian bawaanStemming Snowball, tanpa stop wordCukup pencocokan akar kata; setup tercepat
Config simpleTanpa stemming dan stop wordPencocokan kata persis, mis. nama produk
Config kustom + stop word sendiriCREATE TEXT SEARCH CONFIGURATION + file stop word di serverButuh membuang kata fungsi; punya akses filesystem server
Praproses di sisi aplikasiAplikasi membuang stop word sebelum mengirim teksTanpa akses file server, mis. PostgreSQL terkelola (managed)
Mesin pencari eksternalMeilisearch, Elasticsearch di samping PostgreSQLSkala besar, toleransi salah ketik, ranking canggih

Membuat Konfigurasi Kustom #

CREATE TEXT SEARCH CONFIGURATION membuat konfigurasi baru; klausa COPY menyalin pemetaan konfigurasi lain sekaligus, sedangkan klausa parser memilih parser tanpa pemetaan apa pun. Pemetaan lalu diisi satu per satu dengan ALTER TEXT SEARCH CONFIGURATION ... ADD MAPPING FOR <jenis token> WITH <kamus>:

-- Konfigurasi kustom 'idringan': dimulai dari parser default tanpa pemetaan apa pun.
CREATE TEXT SEARCH CONFIGURATION public.idringan (parser = default);

-- Tanpa MAPPING, semua token dibuang: vektor kosong.
SELECT to_tsvector('idringan', 'Kopi Toraja 250 gram yang enak') AS vektor_kosong;

-- Petakan hanya jenis token kata (asciiword, word) ke kamus simple.
ALTER TEXT SEARCH CONFIGURATION public.idringan
    ADD MAPPING FOR asciiword, word WITH pg_catalog.simple;

-- Angka '250' masih dibuang karena jenis token uint belum dipetakan.
SELECT to_tsvector('idringan', 'Kopi Toraja 250 gram yang enak') AS vektor_tanpa_angka;

-- Petakan juga bilangan bulat, lalu angka ikut terindeks.
ALTER TEXT SEARCH CONFIGURATION public.idringan
    ADD MAPPING FOR uint WITH pg_catalog.simple;

SELECT to_tsvector('idringan', 'Kopi Toraja 250 gram yang enak') AS vektor_lengkap;

-- Daftar kamus yang dipakai config kustom, dilihat dari pg_ts_config_map.
SELECT t.alias, m.mapdict::regdictionary AS kamus
FROM pg_ts_config_map m
JOIN pg_ts_config c ON c.oid = m.mapcfg,
     ts_token_type('default') t
WHERE c.cfgname = 'idringan' AND t.tokid = m.maptokentype
ORDER BY t.alias;

Output yang Dihasilkan:

text
CREATE TEXT SEARCH CONFIGURATION
 vektor_kosong 
---------------
 
(1 row)

ALTER TEXT SEARCH CONFIGURATION
               vektor_tanpa_angka               
------------------------------------------------
 'enak':5 'gram':3 'kopi':1 'toraja':2 'yang':4
(1 row)

ALTER TEXT SEARCH CONFIGURATION
                     vektor_lengkap                     
--------------------------------------------------------
 '250':3 'enak':6 'gram':4 'kopi':1 'toraja':2 'yang':5
(1 row)

   alias   | kamus  
-----------+--------
 asciiword | simple
 uint      | simple
 word      | simple
(3 rows)

Empat tahap output memperlihatkan mekanisme pemetaan secara bertahap. Konfigurasi baru idringan belum punya pemetaan, sehingga seluruh token dibuang dan vektor yang dihasilkan kosong. Setelah asciiword dan word dipetakan ke kamus simple, kata-kata terindeks — tetapi 250 masih hilang karena angka berjenis token uint dan jenis itu belum dipetakan. Pemetaan uint menambahkannya, dan query katalog terakhir mengonfirmasi ketiga pemetaan yang terpasang. Titik lanjutan yang realistis: buat kamus stop word Indonesia (CREATE TEXT SEARCH DICTIONARY id_stop (TEMPLATE = pg_catalog.simple, STOPWORDS = ...)) lalu petakan token kata ke kamus itu — namun file stop word harus berada di direktori tsearch_data pada filesystem server, sehingga demo ini tidak dijalankan di lingkungan kursus.

default_text_search_config #

Bentuk satu argumen to_tsvector(teks) dan plainto_tsquery(teks) memakai konfigurasi yang ditunjuk setting default_text_search_config. Nilainya terlihat dengan SHOW, bisa diubah per sesi dengan SET, dan di seluruh server lewat postgresql.conf:

-- Setting default_text_search_config dipakai oleh to_tsvector satu argumen.
SHOW default_text_search_config;

-- Bentuk satu argumen: config aktif (english) ikut menentukan hasil.
SELECT to_tsvector('the running databases') AS vektor_default;

-- Ubah default untuk sesi ini, lalu bentuk satu argumen berubah perilaku.
SET default_text_search_config = 'pg_catalog.simple';

SHOW default_text_search_config;

SELECT to_tsvector('the running databases') AS vektor_simple;

-- Kembalikan nilai bawaan server.
RESET default_text_search_config;

SHOW default_text_search_config;

Output yang Dihasilkan:

text
 default_text_search_config 
----------------------------
 pg_catalog.english
(1 row)

   vektor_default    
---------------------
 'databas':3 'run':2
(1 row)

SET
 default_text_search_config 
----------------------------
 pg_catalog.simple
(1 row)

           vektor_simple           
-----------------------------------
 'databases':3 'running':2 'the':1
(1 row)

RESET
 default_text_search_config 
----------------------------
 pg_catalog.english
(1 row)

Pada server kursus ini default-nya pg_catalog.english, sehingga bentuk satu argumen membuang the dan merapatkan running menjadi run. Setelah SET default_text_search_config mengarah ke pg_catalog.simple, teks yang sama diproses tanpa stemming dan tanpa stop word. RESET mengembalikan nilai bawaan. Karena kebiasaan bentuk satu argumen mengikuti setting sesi, materi course ini — seperti chapter tsvector dan tsquery — selalu menulis config secara eksplisit; itulah juga syarat agar expression index to_tsvector(...) dianggap immutable. Vektor yang kini dihitung dengan benar per bahasa tinggal disimpan dan diindeks supaya pencarian berskala ribuan baris tetap cepat — tugas chapter GIN Index untuk Pencarian .

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

buat-tabel.sql

Unduh berkas
sql
-- Enam artikel: empat berbahasa Indonesia, dua berbahasa Inggris.
-- Campuran bahasa ini sengaja dipakai untuk membandingkan konfigurasi text search.
CREATE TABLE artikel (
    id      integer PRIMARY KEY,
    judul   text NOT NULL,
    isi     text NOT NULL
);

INSERT INTO artikel VALUES
 (1, 'Penjualan Kopi Naik',   'penjualan kopi naik karena pembeli membaca ulasan'),
 (2, 'Kopi Dijual Grosir',    'kopi dijual per kilogram untuk warung kopi'),
 (3, 'Kiat Membaca Pasar',    'membaca tren penjualan butuh waktu sebulan'),
 (4, 'Buku Harian Baru',      'pembaca setia menunggu edisi baru terbit'),
 (5, 'Database Tuning Guide', 'running databases perform better after careful tuning'),
 (6, 'Backup Strategy',       'nightly backups verify the cluster before shipping');

SELECT id, judul, isi
FROM artikel
ORDER BY id;

config-custom.sql

Unduh berkas
sql
-- Konfigurasi kustom 'idringan': dimulai dari parser default tanpa pemetaan apa pun.
CREATE TEXT SEARCH CONFIGURATION public.idringan (parser = default);

-- Tanpa MAPPING, semua token dibuang: vektor kosong.
SELECT to_tsvector('idringan', 'Kopi Toraja 250 gram yang enak') AS vektor_kosong;

-- Petakan hanya jenis token kata (asciiword, word) ke kamus simple.
ALTER TEXT SEARCH CONFIGURATION public.idringan
    ADD MAPPING FOR asciiword, word WITH pg_catalog.simple;

-- Angka '250' masih dibuang karena jenis token uint belum dipetakan.
SELECT to_tsvector('idringan', 'Kopi Toraja 250 gram yang enak') AS vektor_tanpa_angka;

-- Petakan juga bilangan bulat, lalu angka ikut terindeks.
ALTER TEXT SEARCH CONFIGURATION public.idringan
    ADD MAPPING FOR uint WITH pg_catalog.simple;

SELECT to_tsvector('idringan', 'Kopi Toraja 250 gram yang enak') AS vektor_lengkap;

-- Daftar kamus yang dipakai config kustom, dilihat dari pg_ts_config_map.
SELECT t.alias, m.mapdict::regdictionary AS kamus
FROM pg_ts_config_map m
JOIN pg_ts_config c ON c.oid = m.mapcfg,
     ts_token_type('default') t
WHERE c.cfgname = 'idringan' AND t.tokid = m.maptokentype
ORDER BY t.alias;

config-indonesian.sql

Unduh berkas
sql
-- Config 'indonesian': stemmer Snowball untuk bahasa Indonesia.
-- Bentuk turunan dirapatkan ke akar kata yang sama.
SELECT to_tsvector('indonesian', judul || ' ' || isi) AS vektor_indonesian
FROM artikel
WHERE id = 1;

-- Query 'jual' menemukan penjualan dan dijual sekaligus.
SELECT id, judul
FROM artikel
WHERE to_tsvector('indonesian', judul || ' ' || isi) @@ plainto_tsquery('indonesian', 'jual')
ORDER BY id;

-- Query 'baca' menemukan membaca dan pembaca.
SELECT id, judul
FROM artikel
WHERE to_tsvector('indonesian', judul || ' ' || isi) @@ plainto_tsquery('indonesian', 'baca')
ORDER BY id;

-- Batas stemmer: tiga bentuk kata 'masak' justru terpecah menjadi tiga lexeme berbeda.
SELECT to_tsvector('indonesian', 'memasak masak masakan') AS vektor_masak;

-- Batas kedua: tidak ada file stop word Indonesia, kata fungsi tetap diindeks.
SELECT to_tsvector('indonesian', 'yang dan di ke dari') AS vektor_stopword;

daftar-config.sql

Unduh berkas
sql
-- Katalog konfigurasi text search bawaan tersimpan di pg_ts_config.
-- Query ini deterministik: daftarnya tetap untuk satu versi PostgreSQL.
SELECT count(*) AS total_config
FROM pg_ts_config;

-- Sebelas nama yang relevan untuk pemeriksaan: bahasa besar + kandidat Asia Tenggara.
SELECT cfgname
FROM pg_ts_config
WHERE cfgname IN ('simple', 'english', 'german', 'french', 'spanish',
                  'russian', 'arabic', 'hindi', 'tamil',
                  'indonesian', 'malay')
ORDER BY cfgname;

default-config.sql

Unduh berkas
sql
-- Setting default_text_search_config dipakai oleh to_tsvector satu argumen.
SHOW default_text_search_config;

-- Bentuk satu argumen: config aktif (english) ikut menentukan hasil.
SELECT to_tsvector('the running databases') AS vektor_default;

-- Ubah default untuk sesi ini, lalu bentuk satu argumen berubah perilaku.
SET default_text_search_config = 'pg_catalog.simple';

SHOW default_text_search_config;

SELECT to_tsvector('the running databases') AS vektor_simple;

-- Kembalikan nilai bawaan server.
RESET default_text_search_config;

SHOW default_text_search_config;

simple-vs-english.sql

Unduh berkas
sql
-- Perilaku config 'english' dan 'simple' pada teks yang sama (isi baris 5).
SELECT to_tsvector('english', isi) AS vektor_english,
       to_tsvector('simple', isi)  AS vektor_simple
FROM artikel
WHERE id = 5;

-- Stop word: config 'english' membuang 'the' dan 'before' (isi baris 6).
SELECT to_tsvector('english', isi) AS vektor_english,
       to_tsvector('simple', isi)  AS vektor_simple
FROM artikel
WHERE id = 6;

-- Konsekuensi pencarian: query 'run' hanya cocok bila stemming aktif.
SELECT id, judul
FROM artikel
WHERE to_tsvector('english', judul || ' ' || isi) @@ plainto_tsquery('english', 'run')
ORDER BY id;

SELECT id, judul
FROM artikel
WHERE to_tsvector('simple', judul || ' ' || isi) @@ plainto_tsquery('simple', 'run')
ORDER BY id;