Skip to main content
🗃️

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