Pencarian natural language dan boolean mode sama-sama mengembalikan dokumen yang memuat kata-kata dari query kita. Masalahnya, pengunjung toko buku mungkin mengetik “jawa” ketika yang dicari buku tentang “pulau”, atau pembaca artikel kopi mengetik istilah sempit tanpa menyebut kata “kopi” itu sendiri. MySQL menyediakan mode ketiga bernama query expansion yang mencoba menjembatani celah tersebut: pencarian dijalankan dua kali, dan kata-kata yang paling sering muncul di hasil putaran pertama ikut dipakai sebagai kata kunci putaran kedua. Pada chapter ini kita mengamati cara kerja dua fase tersebut lewat contoh nyata, melihat kapan hasilnya memperluas penemuan yang berguna, dan kapan justru mengundang artikel tak relevan masuk ke daftar.

Menyiapkan Tabel Latihan #

Kita pakai kembali tabel artikel dengan sepuluh artikel bertema teknologi, kopi, dan masakan, lengkap dengan full-text index multi kolom idx_ft_artikel:

sql
DROP DATABASE IF EXISTS belajar_fts;
CREATE DATABASE belajar_fts;
USE belajar_fts;

Output yang Dihasilkan:

text
Query OK, 0 rows affected, 1 warning (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Database changed
sql
CREATE TABLE artikel (
  id INT AUTO_INCREMENT PRIMARY KEY,
  judul VARCHAR(100) NOT NULL,
  isi TEXT NOT NULL,
  FULLTEXT idx_ft_artikel (judul, isi)
) ENGINE=InnoDB;

INSERT INTO artikel (judul, isi) VALUES
  ('Belajar Database Relasional', 'Database relasional menyimpan data dalam tabel yang saling terhubung. MySQL adalah database relasional populer untuk aplikasi web. Indeks mempercepat pencarian data pada kolom tertentu.'),
  ('Panduan Menyeduh Espresso', 'Espresso adalah basis minuman kopi modern. Seduh espresso memakai mesin kopi bertekanan tinggi dan biji kopi gelap. Grinder yang baik menghasilkan rasa espresso seimbang.'),
  ('Memasak Rendang Daging', 'Rendang daging adalah masakan khas Padang. Memasak rendang membutuhkan santan, cabai, dan rempah. Proses memasak rendang memakan waktu beberapa jam hingga kuah mengering.'),
  ('Cloud Computing untuk Pemula', 'Cloud computing menyediakan server dan penyimpanan lewat internet. Perusahaan menyewa cloud agar tidak membeli server fisik. Layanan cloud populer contohnya penyimpanan berkas dan basis data terkelola.'),
  ('Asal Biji Kopi Gayo', 'Kopi Gayo tumbuh di dataran tinggi Aceh. Petani kopi Gayo memanen biji secara manual. Citarasa kopi Gayo khas dengan aroma rempah dan keasaman seimbang.'),
  ('Nasi Goreng Kampung', 'Nasi goreng kampung adalah masakan rumahan favorit. Bumbu nasi goreng terdiri dari bawang, cabai, dan kecap. Memasak nasi goreng butuh api besar agar wangi.'),
  ('JavaScript di Browser', 'JavaScript membuat halaman web menjadi interaktif. Browser menjalankan JavaScript pada sisi klien. Framework JavaScript mempercepat pembuatan antarmuka aplikasi modern.'),
  ('Cold Brew vs Kopi Panas', 'Cold brew diseduh dengan air dingin selama belasan jam. Rasa cold brew lebih halus dibanding kopi panas. Penikmat kopi sering menyimpan cold brew di kulkas.'),
  ('Tempe Sumber Protein', 'Tempe adalah makanan fermentasi kedelai khas Indonesia. Tempe mengandung protein tinggi dan harganya murah. Olahan tempe contohnya tempe goreng, tempe bacem, dan tempe crispy.'),
  ('Keamanan Aplikasi Web', 'Keamanan aplikasi web wajib dijaga sejak awal pengembangan. Pengembang memvalidasi input pengguna dan menyimpan sandi terenkripsi. Serangan umum pada aplikasi web contohnya SQL injection.');

OPTIMIZE TABLE artikel;

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

Query OK, 10 rows affected (0.00 sec)
Records: 10  Duplicates: 0  Warnings: 0

+---------------------+----------+----------+-------------------------------------------------------------------+
| Table               | Op       | Msg_type | Msg_text                                                          |
+---------------------+----------+----------+-------------------------------------------------------------------+
| belajar_fts.artikel | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| belajar_fts.artikel | optimize | status   | OK                                                                |
+---------------------+----------+----------+-------------------------------------------------------------------+
2 rows in set (0.00 sec)

Pencarian Biasa Sebagai Titik Awal #

Sebelum melihat expansion bekerja, kita ukur dulu hasil pencarian biasa dengan kata kunci santan — bahan yang hanya disebut satu artikel:

sql
SELECT id, judul
FROM artikel
WHERE MATCH (judul, isi) AGAINST ('santan');

Output yang Dihasilkan:

text
+----+------------------------+
| id | judul                  |
+----+------------------------+
|  3 | Memasak Rendang Daging |
+----+------------------------+
1 row in set (0.00 sec)

Hanya satu artikel yang memuat kata santan, dan itu memang satu-satunya artikel masakan Padang. Pencarian biasa sudah tepat di sini. Sekarang kita naikkan satu tingkat: kata kunci yang sama dijalankan dengan WITH QUERY EXPANSION:

sql
SELECT id, judul
FROM artikel
WHERE MATCH (judul, isi) AGAINST ('santan' WITH QUERY EXPANSION);

Output yang Dihasilkan:

text
+----+------------------------------+
| id | judul                        |
+----+------------------------------+
|  3 | Memasak Rendang Daging       |
|  6 | Nasi Goreng Kampung          |
|  5 | Asal Biji Kopi Gayo          |
|  8 | Cold Brew vs Kopi Panas      |
|  9 | Tempe Sumber Protein         |
|  2 | Panduan Menyeduh Espresso    |
|  1 | Belajar Database Relasional  |
|  4 | Cloud Computing untuk Pemula |
| 10 | Keamanan Aplikasi Web        |
+----+------------------------------+
9 rows in set (0.00 sec)

Daftar hasil kini memuat lebih dari satu artikel. Mesin pencari tidak sekadar mencari santan lagi: hasil putaran pertama — artikel rendang — ikut disumbangkan kata-katanya sebagai kunci tambahan. Kata seperti masakan, memasak, padang, atau cabai yang frekuensinya tinggi di hasil awal menjadi kata kunci putaran kedua, sehingga artikel masakan lain ikut terangkat. Hasilnya adalah penemuan yang lebih luas dari kata yang diketik pengguna.

Mekanisme Dua Fase #

Query expansion menjalankan pencarian dua kali secara internal:

  1. Fase pertama: MySQL menjalankan pencarian natural language biasa terhadap kata kunci pengguna dan mengumpulkan dokumen yang relevan.
  2. Fase kedua: kata-kata yang paling sering muncul pada dokumen hasil fase pertama ditambahkan ke kata kunci, lalu pencarian dijalankan ulang terhadap gabungan kata kunci lama dan baru.

Skor relevansi lalu dihitung ulang atas kata-kata gabungan tersebut. Perhatikan bahwa fase kedua sepenuhnya otomatis — kita tidak melihat dan tidak bisa mengatur kata tambahan mana yang dipilih. Berikut tampilan skor relevansi pada pencarian dengan expansion:

sql
SELECT id, judul,
  MATCH (judul, isi) AGAINST ('santen' WITH QUERY EXPANSION) AS skor
FROM artikel
WHERE MATCH (judul, isi) AGAINST ('santen' WITH QUERY EXPANSION)
ORDER BY skor DESC, id;

Output yang Dihasilkan:

text
Empty set (0.00 sec)

Perlu dibaca dengan teliti: query di atas memakai kata santen (ejaan keliru) dan hasilnya kosong. Inilah bukti bahwa query expansion tidak mengoreksi ejaan — fase pertama mencari santen dan tidak menemukan dokumen apa pun, sehingga tidak ada hasil yang bisa disumbangkan kata-katanya ke fase kedua. Expansion memperluas kata kunci dari dokumen yang ditemukan; bila fase pertama gagal total, fase kedua tidak punya bahan. Kesalahan ketik satu huruf tetap menghasilkan nol baris, sama seperti mode biasa.

Kapan Expansion Membantu #

Mode ini paling bernilai ketika pengguna mengetik istilah yang sangat sempit atau teknis, sementara koleksi dokumen menyebut topik yang sama dengan kosakata lain. Contoh klasiknya dalam koleksi kita: mencari “grinder” — alat yang hanya disebut di artikel espresso:

sql
SELECT id, judul
FROM artikel
WHERE MATCH (judul, isi) AGAINST ('grinder');

Output yang Dihasilkan:

text
+----+---------------------------+
| id | judul                     |
+----+---------------------------+
|  2 | Panduan Menyeduh Espresso |
+----+---------------------------+
1 row in set (0.00 sec)
sql
SELECT id, judul
FROM artikel
WHERE MATCH (judul, isi) AGAINST ('grinder' WITH QUERY EXPANSION);

Output yang Dihasilkan:

text
+----+------------------------------+
| id | judul                        |
+----+------------------------------+
|  2 | Panduan Menyeduh Espresso    |
|  5 | Asal Biji Kopi Gayo          |
|  8 | Cold Brew vs Kopi Panas      |
|  1 | Belajar Database Relasional  |
|  4 | Cloud Computing untuk Pemula |
|  7 | JavaScript di Browser        |
|  9 | Tempe Sumber Protein         |
|  3 | Memasak Rendang Daging       |
|  6 | Nasi Goreng Kampung          |
| 10 | Keamanan Aplikasi Web        |
+----+------------------------------+
10 rows in set (0.00 sec)

Pada contoh ini fase pertama menemukan artikel espresso, yang lalu menyumbangkan kata kopi, seduh, dan biji ke fase kedua — sehingga artikel kopi Gayo dan cold brew yang tidak menyebut grinder sama sekali ikut terangkat di posisi atas. Sekaligus kita melihat sisi gelapnya: kata-kata umum dari dokumen sumber (yang, adalah, topic word lain yang lolos stopword) ikut memperluas fase kedua sampai seluruh sepuluh artikel masuk daftar. Pengguna yang mencari “grinder” sambil menelusuri topik kopi mendapat penemuan yang berguna di lima besar; pengguna yang hanya ingin definisi grinder harus menggulir melewati lima artikel yang tidak relevan. Keputusan memakai expansion atau tidak karena itu sebaiknya diambil per kasus penggunaan dengan mempertimbangkan tingkat kebisingan yang bisa diterima, bukan diaktifkan global untuk semua pencarian.

Referensi #

Mode query expansion melengkapi tiga mode pencarian full-text yang kita kuasai. Chapter penutup section ini merangkum seluruh batasan full-text MySQL — dari stopword, panjang kata minimum, dukungan bahasa, hingga titik jera kapan sebaiknya beralih ke mesin pencarian khusus.