🗃️
SQLite FTS5
kham-sqlite เป็น loadable extension สำหรับ SQLite ที่ให้ custom FTS5 tokenizer ช่วยตัดคำภาษาไทยในการทำ FTS5 MATCH query ให้ถูกต้อง
1
สิ่งที่ต้องมีก่อน
SQLite ที่เปิดใช้งาน FTS5 และรองรับ load_extension บน macOS ต้องติดตั้งจาก Homebrew เพราะ system sqlite3 ปิด load_extension
brew install sqlite 2
Build extension
cargo build -p kham-sqlite --release
# ไฟล์อยู่ที่:
# Linux: target/release/libkham_sqlite.so
# macOS: target/release/libkham_sqlite.dylib 3
Load และสร้างตาราง FTS5
.load ./target/release/libkham_sqlite
CREATE VIRTUAL TABLE docs USING fts5(
title, body,
tokenize = 'kham'
); 4
Insert และค้นหา
INSERT INTO docs VALUES
('อาหารไทย', 'กินข้าวกับปลาและผัก'),
('เมืองหลวง', 'กรุงเทพมหานครเป็นเมืองหลวงของประเทศไทย');
SELECT * FROM docs WHERE docs MATCH 'ปลา';
SELECT snippet(docs, 1, '<b>', '</b>', '…', 10)
FROM docs WHERE docs MATCH 'กรุงเทพ'; 5
argument ของ tokenize
ทุก argument เป็น optional และไม่ขึ้นกับลำดับ path ของไฟล์ต้องใส่ single quote ภายใน tokenize directive (FTS5 bareword รองรับเฉพาะ A–Z, 0–9 และ _) และ escape สำหรับ SQL ด้วย '' (single quote สองตัว)
-- lk82 soundex (default) — คำที่ออกเสียงใกล้เคียงจะมีรหัสเดียวกัน
CREATE VIRTUAL TABLE t1 USING fts5(body, tokenize='kham');
-- udom83 soundex (แยกแยะ sibilant/liquid ละเอียดกว่า)
CREATE VIRTUAL TABLE t2 USING fts5(body, tokenize='kham soundex udom83');
-- ปิด soundex
CREATE VIRTUAL TABLE t3 USING fts5(body, tokenize='kham soundex none');
-- ไม่ index stopword
CREATE VIRTUAL TABLE t4 USING fts5(body, tokenize='kham stopwords on');
-- bigram สำหรับ OOV/unknown token (default: trigram; 0 = ปิด)
CREATE VIRTUAL TABLE t5 USING fts5(body, tokenize='kham ngram_size 2');
-- custom synonym map (path ต้องใส่ single quote)
CREATE VIRTUAL TABLE t6 USING fts5(body,
tokenize='kham synonyms ''/etc/kham/synonyms.tsv''');
-- custom word list (overlay บน built-in dictionary ไม่ rebuild trie)
CREATE VIRTUAL TABLE t7 USING fts5(body,
tokenize='kham dict ''/etc/kham/domain_words.txt''');
-- รวมทุก option
CREATE VIRTUAL TABLE t8 USING fts5(body,
tokenize='kham soundex lk82 stopwords on ngram_size 2
dict ''/words.txt'' synonyms ''/syns.tsv'''); 6
การจัดอันดับและ field boosting
FTS5 มี BM25 built-in ค่าต่ำกว่า (ติดลบมากกว่า) = match ดีกว่า ส่งน้ำหนัก column เพื่อให้ title มีผลมากกว่า body การ match แบบ phonetic/soundex ได้น้ำหนัก BM25 เท่ากับ exact match — หากต้องการลด ให้ rerank ใน application code
-- BM25 พื้นฐาน (ค่าน้อยกว่า = match ดีกว่า)
SELECT title, body, bm25(docs) AS score
FROM docs
WHERE docs MATCH 'ปลา'
ORDER BY score;
-- Field boosting: title 10×, body 1×
-- argument ตามลำดับ column ใน CREATE VIRTUAL TABLE
CREATE VIRTUAL TABLE docs USING fts5(title, body, tokenize='kham');
SELECT title, bm25(docs, 10.0, 1.0) AS score
FROM docs
WHERE docs MATCH 'ปลา'
ORDER BY score;
-- Exact match vs phonetic match: FTS5 ให้น้ำหนักเท่ากัน
-- หากต้องการแยก:
-- 1. ดึง candidate ด้วย FTS5 MATCH (ใช้ index เร็ว)
-- 2. ใน app code ตรวจ query term ว่า exact token หรือไม่ (edit distance 0)
-- 3. คูณ BM25 score × 0.3 สำหรับ phonetic-only match