W
AI-Wiki
CONCEPT

SQLite FTS 同步触发器

定义

SQLite FTS 同步触发器 指的是在 SQLite 中,围绕业务主表与 FTS 全文检索虚拟表建立的一组同步触发器。它的核心作用不是执行业务规则,而是把主表中的可检索文本字段持续同步到 FTS 索引中,使全文检索状态跟随主表的增删改变化。

在本文档语境里,这种模式以 observations 作为主表、observations_fts 作为全文检索虚拟表为例,展示了最典型的三段式维护方式:AFTER INSERT 新增索引、AFTER UPDATE 重建索引、AFTER DELETE 移除索引。

本文档中的语境

来源给出的并不是抽象说明,而是实际的触发器 SQL 示例。示例明确把主表中的多个文本列同步到 FTS 表,而不是把整行记录原样复制过去。被同步的列包括:

  • title
  • subtitle
  • narrative
  • text
  • facts
  • concepts

这些字段与 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 索引,而是分两步执行:

  1. 先删除旧索引。
  2. 再插入新索引。

对应 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.idold.id 写入 rowid

如果这两个标识不对齐,触发器就无法稳定地定位“主表中的这一行对应 FTS 里的哪一条索引记录”,更新和删除阶段尤其容易失配。

同步的是索引字段,不是整行镜像

示例同步的不是主表的所有列,也不是为了做一份普通副本表。它只挑选参与全文检索的字段:titlesubtitlenarrativetextfactsconcepts

因此,SQLite FTS 同步触发器 的目标是维护搜索索引,而不是维护业务数据镜像、审计日志或缓存表。

UPDATE/DELETE 的删除动作有特殊语义

很多数据库触发器中的“删除同步”会写成普通 DELETE FROM 索引表 WHERE ...。但这里不是这样。示例清楚显示,删除旧索引依赖的是:

  • observations_fts 插入一条记录;
  • 该记录的 observations_fts 列值为 'delete'
  • 同时带上旧版本的 rowid 和旧文本字段值。

这说明该机制是面向 FTS 虚拟表维护协议的,不应按普通关系表的写法理解。

目的与意义

这种模式的直接目的,是让全文检索结果始终与主表数据保持一致。

具体来说:

  • 主表新增后,搜索能立即搜到新内容。
  • 主表更新后,搜索结果不再保留旧文本,而是反映新文本。
  • 主表删除后,搜索结果中对应内容也会被移除。

如果没有这组同步触发器,主表与 FTS 索引就可能逐渐偏离,出现“数据已经改了但搜索结果还是旧的”或“数据已删但搜索还能命中”的问题。

相关条目