用 SQLite FTS5 给静态博客做全文搜索
起因
博客文章上了二十篇之后,自己翻起来都费劲。想加搜索,但为了一个静态博客装 Elasticsearch 实在小题大做。
试了一下 SQLite 内置的 FTS5,发现完全够用。
建表
FTS5 需要一张「虚表」,内容和主表分开存:
CREATE VIRTUAL TABLE posts_fts USING fts5(
title,
summary,
content,
content='posts',
content_rowid='id',
tokenize='unicode61'
);
content='posts' 表示内容和主表共用,不额外占空间。
中文分词
默认的 unicode61 对中文是逐字切分的,效果一般但能用。想要更准就上 jieba 或者 simple 分词器,不过编译麻烦。
unicode61 配合「加空格 = 切词」的策略,在博客这种场景下召回率其实不差。
查询
SELECT p.id, p.title,
snippet(posts_fts, 2, '<mark>', '</mark>', '…', 24) AS hit
FROM posts_fts
JOIN posts p ON p.id = posts_fts.rowid
WHERE posts_fts MATCH ?
ORDER BY rank
LIMIT 10;
snippet() 直接帮你把命中片段摘出来,省得自己写高亮逻辑。
维护
主表改动后要同步虚表,用触发器最简单:
CREATE TRIGGER posts_ai AFTER INSERT ON posts BEGIN
INSERT INTO posts_fts(rowid, title, summary, content)
VALUES (new.id, new.title, new.summary, new.content);
END;
删除和更新同理写两个。加上之后基本不用管了。
小结
全文搜索这件事,需求不大的话真没必要上重型组件。SQLite 的 FTS5 大概是最低成本的方案。