SQLite FTS 同步触发器
定义
SQLite FTS 同步触发器 指的是在 SQLite 中,围绕业务主表与 FTS 全文检索虚拟表建立的一组同步触发器。它的核心作用不是执行业务规则,而是把主表中的可检索文本字段持续同步到 FTS 索引中,使全文检索状态跟随主表的增删改变化。
在本文档语境里,这种模式以 observations 作为主表、observations_fts 作为全文检索虚拟表为例,展示了最典型的三段式维护方式:AFTER INSERT 新增索引、AFTER UPDATE 重建索引、AFTER DELETE 移除索引。
本文档中的语境
来源给出的并不是抽象说明,而是实际的触发器 SQL 示例。示例明确把主表中的多个文本列同步到 FTS 表,而不是把整行记录原样复制过去。被同步的列包括:
titlesubtitlenarrativetextfactsconcepts
这些字段与 rowid 一起写入 observations_fts,构成全文检索索引内容。
关键机制
1. 插入:新增索引
插入触发器示例为 CREATE TRIGGER observations_ai AFTER INSERT ON observations。其行为是在主表插入新记录后,立即向 observations_fts 插入对应索引行:
INSERT INTO observations_fts(rowid, title, subtitle, narrative, text, facts, concepts)
VALUES (new.id, new.title, new.subtitle, new.narrative, new.text, new.facts, new.concepts);
这里最关键的点有两个:
- 使用
new.id作为 FTS 表的rowid。 - 只同步用于全文检索的文本字段。
这意味着每新增一条业务记录,就会新增一条对应的全文索引记录。
2. 更新:先删旧索引,再建新索引
更新触发器示例为 CREATE TRIGGER observations_au AFTER UPDATE ON observations。它不是直接覆盖 FTS 索引,而是分两步执行:
- 先删除旧索引。
- 再插入新索引。
对应 SQL 为:
INSERT INTO observations_fts(observations_fts, rowid, title, subtitle, narrative, text, facts, concepts)
VALUES('delete', old.id, old.title, old.subtitle, old.narrative, old.text, old.facts, old.concepts);
然后再执行:
INSERT INTO observations_fts(rowid, title, subtitle, narrative, text, facts, concepts)
VALUES (new.id, new.title, new.subtitle, new.narrative, new.text, new.facts, new.concepts);
这里必须特别注意:删除旧索引时,并不是对 observations_fts 执行常规 DELETE FROM ... SQL,而是向 FTS 表插入一条特殊命令,其中 observations_fts 列的值为 'delete'。这属于 FTS 维护模式的一部分,用来告诉虚拟表移除旧索引内容。
因此,UPDATE 的本质不是“改一改索引行”,而是“撤销旧文本对应的索引,再按新文本重新建立索引”。这能避免旧内容残留在全文检索结果中。
3. 删除:移除索引
删除触发器示例为 CREATE TRIGGER observations_ad AFTER DELETE ON observations。其行为是在主表删除记录后,向 FTS 表写入 'delete' 命令来清除对应索引:
INSERT INTO observations_fts(observations_fts, rowid, title, subtitle, narrative, text, facts, concepts)
VALUES('delete', old.id, old.title, old.subtitle, old.narrative, old.text, old.facts, old.concepts);
这同样不是普通的 SQL DELETE,而是通过 FTS 虚拟表约定的删除命令移除旧索引项。
依赖条件与边界
rowid 必须与业务主键 id 对齐
这种同步模式依赖一个非常具体的前提:FTS 表中的 rowid 要和业务表主键 id 对齐。示例里无论插入、更新还是删除,都使用 new.id 或 old.id 写入 rowid。
如果这两个标识不对齐,触发器就无法稳定地定位“主表中的这一行对应 FTS 里的哪一条索引记录”,更新和删除阶段尤其容易失配。
同步的是索引字段,不是整行镜像
示例同步的不是主表的所有列,也不是为了做一份普通副本表。它只挑选参与全文检索的字段:title、subtitle、narrative、text、facts、concepts。
因此,SQLite FTS 同步触发器 的目标是维护搜索索引,而不是维护业务数据镜像、审计日志或缓存表。
UPDATE/DELETE 的删除动作有特殊语义
很多数据库触发器中的“删除同步”会写成普通 DELETE FROM 索引表 WHERE ...。但这里不是这样。示例清楚显示,删除旧索引依赖的是:
- 向
observations_fts插入一条记录; - 该记录的
observations_fts列值为'delete'; - 同时带上旧版本的
rowid和旧文本字段值。
这说明该机制是面向 FTS 虚拟表维护协议的,不应按普通关系表的写法理解。
目的与意义
这种模式的直接目的,是让全文检索结果始终与主表数据保持一致。
具体来说:
- 主表新增后,搜索能立即搜到新内容。
- 主表更新后,搜索结果不再保留旧文本,而是反映新文本。
- 主表删除后,搜索结果中对应内容也会被移除。
如果没有这组同步触发器,主表与 FTS 索引就可能逐渐偏离,出现“数据已经改了但搜索结果还是旧的”或“数据已删但搜索还能命中”的问题。
相关条目
- Database Architecture 摘要
- observations_fts
- SQLite
- 全文检索