Menu

Full-Text Search di SQLite: Virtual Table FTS5 dan MATCH

Cara menambahkan full-text search ke SQLite dengan FTS5: membuat virtual table, operator MATCH, peringkat BM25, dan menjaga index tetap sinkron dengan datamu.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

LIKE Tidak Bisa Diandalkan untuk Skala Besar

Kalau kamu pernah mencari teks di SQLite, kemungkinan kamu memakai LIKE '%word%'. Cara itu berhasil pada tabel kecil dan berantakan pada tabel besar. Tidak ada index yang bisa membantu: SQLite harus memindai setiap baris, mengubahnya ke huruf kecil, lalu memeriksa substring-nya. Batas kata, peringkat, query banyak kata, dan pencocokan prefix semuanya harus kamu implementasikan sendiri.

FTS5 adalah jawaban bawaannya. FTS5 adalah jenis virtual table yang memelihara inverted index atas kolom-kolom teksmu, memahami bahasa query kecil, dan memberi peringkat hasil dengan BM25. FTS5 sudah disertakan di SQLite secara bawaan, tanpa ekstensi yang perlu dipasang.

Membuat Tabel FTS5

Kamu membuat tabel FTS5 dengan CREATE VIRTUAL TABLE ... USING fts5(...), sambil menuliskan kolom teks yang ingin di-index:

Ada tiga hal yang layak diperhatikan. Kolomnya tidak punya tipe, karena FTS5 memperlakukan semuanya sebagai teks. Operator MATCH dipakai terhadap nama tabel (posts MATCH ...), bukan terhadap kolom. Dan query-nya tidak membedakan huruf besar kecil serta sudah dipecah menjadi token, jadi 'sqlite' menemukan SQLite di baris mana pun.

Bahasa Query MATCH

MATCH menerima lebih dari satu kata. String query-nya punya tata bahasa kecil sendiri:

Apa yang dilakukan masing-masing:

  • 'fts5 AND prefix': kedua token harus muncul (dalam urutan apa pun, di mana pun dalam baris).
  • '"keep fts"': frasa persis, dengan urutan itu.
  • 'trig*': prefix search, cocok dengan trigger, triggers, trigonometry...
  • 'index NOT trigger': mengandung index, tidak mengandung trigger.

Kamu juga bisa menargetkan satu kolom dengan column:term, misalnya 'title:sqlite'. Tata bahasa lengkapnya mencakup tanda kurung untuk pengelompokan dan OR untuk alternatif, bentuk yang sama seperti yang kamu harapkan dari mesin pencari.

Peringkat dengan BM25

Secara bawaan, FTS5 menyertakan kolom tersembunyi rank pada setiap baris. Kolom itu berisi skor relevansi BM25: makin rendah angkanya, makin cocok hasilnya. Urutkan berdasarkan kolom itu untuk mendapatkan hasil paling relevan lebih dulu:

Ingin memberi bobot lebih pada kolom tertentu? Panggil bm25() dengan bobot, satu untuk setiap kolom sesuai urutan deklarasinya:

Post pertama menang karena sqlite muncul di title (berbobot 10×), bukan hanya di body (berbobot 1×). Pilih bobot yang sesuai dengan cara aplikasimu sebenarnya ingin memberi peringkat.

Menjaga Index Tetap Sinkron

Tabel FTS5 paling sederhana menyimpan salinan teksnya sendiri. Itu tidak masalah untuk data bergaya log yang hanya kamu tambahkan, tapi sebagian besar aplikasi sudah punya tabel asli dan ingin FTS mengikutinya. Pola yang rapi adalah tabel FTS external content ditambah tiga trigger.

content='articles' menyuruh FTS5 tidak menyimpan teksnya sendiri: ia akan mengambilnya dari tabel articles saat dibutuhkan. Trigger-trigger itu mencerminkan setiap penulisan ke index FTS. Sekarang articles adalah sumber kebenaran, dan articles_fts hanyalah struktur pencarian di sampingnya.

INSERT INTO articles_fts(articles_fts, ...) VALUES ('delete', ...) yang tampak aneh itu adalah sintaks perintah FTS5 untuk menyuruh index menghapus sebuah baris.

Snippet dan Highlight

Hasil pencarian biasanya butuh pratinjau dengan kata yang cocok ditonjolkan. FTS5 punya dua fungsi untuk itu:

  • highlight(table, column_index, open, close) mengembalikan teks lengkap kolom dengan token yang cocok dibungkus penanda.
  • snippet(table, column_index, open, close, ellipsis, token_count) mengembalikan potongan pendek yang berpusat pada kecocokan.

Indeks kolom dimulai dari nol sesuai urutan deklarasi. Inilah bahan dasar untuk perilaku "kata yang cocok berwarna kuning" yang dibutuhkan setiap UI pencarian.

Jebakan yang Perlu Diketahui

Beberapa hal yang sering menjebak orang:

  • MATCH hanya bekerja pada tabel FTS. Kamu tidak bisa memakai MATCH pada kolom biasa. Kalau kamu butuh pencarian di tabel yang sudah ada, pakai pola external-content di atas.
  • Jangan lupa mengurutkan berdasarkan rank. Tanpa itu, FTS5 mengembalikan baris sesuai urutan penyimpanan, yang tidak ada hubungannya dengan relevansi.
  • Tokenizer itu penting. Tokenizer bawaan (unicode61) memecah teks di batas kata Unicode dan mengubahnya ke huruf kecil. Untuk stemming (run cocok dengan running), pakai tokenizer porter: USING fts5(body, tokenize='porter').
  • FTS5 bukan engine yang toleran terhadap salah ketik. Ia melakukan pencocokan prefix, bukan fuzzy matching. Kalau kamu butuh perilaku "mungkin maksudmu...", itu lapisan di atas FTS5.
  • Tabel contentless (content='') lebih kecil tapi kehilangan data. Kamu bisa mencari di dalamnya, tapi tidak bisa mengambil teks aslinya, hanya rowid-nya. Berguna kalau teksnya kamu simpan di tempat lain.

Berikutnya: Window Function

FTS5 menangani pencarian teks. Halaman berikutnya membahas bentuk query tingkat lanjut yang berbeda, yaitu window function, yang memungkinkanmu menghitung total berjalan, peringkat, dan analitik per kelompok tanpa meleburkan baris-barismu menjadi agregat.

Pertanyaan yang sering diajukan

Apa itu FTS5 di SQLite?

FTS5 adalah ekstensi full-text search bawaan SQLite. Kamu membuat virtual table khusus dengan CREATE VIRTUAL TABLE ... USING fts5(...) dan menjalankan query dengan operator MATCH. FTS5 memecah teks menjadi token saat insert, menyimpan inverted index, dan secara bawaan memberi peringkat hasil dengan BM25.

Apa bedanya MATCH dan LIKE di SQLite?

LIKE memindai substring secara linear dan mengabaikan batas kata. MATCH memakai inverted index FTS5, jadi cepat pada tabel besar dan memahami token, query prefix (term*), operator boolean (AND, OR, NOT), dan pencarian frasa ("exact phrase"). MATCH hanya bekerja pada virtual table FTS.

Bagaimana cara menjaga index FTS5 tetap sinkron dengan tabel biasa?

Pakai tabel FTS5 contentless atau external-content yang menunjuk ke tabel aslimu, atau buat trigger AFTER INSERT, AFTER UPDATE, dan AFTER DELETE yang mencerminkan perubahan ke tabel FTS. Pola external-content (content='posts') menghindari penyimpanan teks dua kali.

Bagaimana cara memberi peringkat hasil full-text search di SQLite?

FTS5 menyediakan kolom tersembunyi rank yang mengembalikan skor BM25 (makin rendah makin baik). Urutkan langsung berdasarkan kolom itu: ORDER BY rank. Kamu juga bisa memanggil bm25(table) untuk mendapatkan skornya secara eksplisit, atau memberikan bobot kolom seperti bm25(posts, 10.0, 1.0) agar judul diberi bobot lebih tinggi daripada isi.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai