大型手机网站制作,郑州房产网官网,元气森林的网络营销方式,创意广告视频网站返回#xff1a;SQLite—系列文章目录
上一篇:SQLite的知名用户(二十九)
下一篇#xff1a;SQLite—系列文章目录 1. FTS5概述
FTS5 是一个 SQLite 虚拟表模块#xff0c;它为数据库应用程序提供全文搜索功能。在最基本的形式中#xff0c; 全文搜索引擎允许用户有…返回SQLite—系列文章目录
上一篇:SQLite的知名用户(二十九)
下一篇SQLite—系列文章目录 1. FTS5概述
FTS5 是一个 SQLite 虚拟表模块它为数据库应用程序提供全文搜索功能。在最基本的形式中 全文搜索引擎允许用户有效地搜索大型 包含一个或多个实例的子集的文档集合 搜索词。除其他外谷歌向万维网用户提供的搜索功能是全文搜索 引擎因为它允许用户搜索 Web 上包含 例如术语“FTS5”。
要使用 FTS5用户需要创建一个具有一个或多个 FTS5 虚拟表的 FTS5 虚拟表 列。例如 CREATE VIRTUAL TABLE email USING fts5(sender, title, body); 将类型、约束或 PRIMARY KEY 声明添加到 用于创建 FTS5 表的 CREATE VIRTUAL TABLE 语句。创建后 可以使用 INSERT、UPDATE 或 DELETE 语句填充 FTS5 表 像任何其他桌子一样。与没有 PRIMARY KEY 声明的任何其他表一样一个 FTS5 表具有一个名为 rowid 的隐式 INTEGER PRIMARY KEY 字段。
上面的示例中没有显示的是还可以向 FTS5 提供各种选项如 CREATE VIRTUAL TABLE语句的一部分用于配置 新表。这些可用于修改 FTS5 表提取的方式 来自文档和查询的术语用于在磁盘上创建额外的索引以加快速度 前缀查询或创建充当内容索引的 FTS5 表 存储在别处。
填充后有三种方法可以对 FTS5 表的内容
在 SELECT 语句的 WHERE 子句中使用 MATCH 运算符或者在 SELECT 语句的 WHERE 子句中使用等于 “” 运算符或者使用表值函数语法。
如果使用 MATCH 或 运算符则 MATCH 左侧的表达式 operator 通常是 FTS5 表的名称指定列筛选器时除外。右边的表达式 必须是指定要搜索的字词的文本值。对于表值 函数语法要搜索的术语被指定为第一个表参数。 例如
-- Query for all rows that contain at least once instance of the term
-- fts5 (in any column). The following three queries are equivalent.
SELECT * FROM email WHERE email MATCH fts5;
SELECT * FROM email WHERE email fts5;
SELECT * FROM email(fts5);
默认情况下FTS5 全文搜索与大小写无关。像其他任何其他人一样 不包含 ORDER BY 子句的 SQL 查询上面的示例返回 导致任意顺序。按相关性对结果进行排序从高到低 相关可以将 ORDER BY 添加到全文查询中如下所示 -- Query for all rows that contain at least once instance of the term
-- fts5 (in any column). Return results in order from best to worst
-- match.
SELECT * FROM email WHERE email MATCH fts5 ORDER BY rank; 以及匹配行的列值和 rowid应用程序 可以使用 FTS5 辅助函数来检索有关 匹配的行。例如辅助函数可用于检索 匹配行的列值的副本其中包含匹配行的所有实例 术语被 HTML b/b 标记包围。辅助功能包括 以与 SQLite 标量函数相同的方式调用只是名称 的 FTS5 表被指定为第一个参数。例如
-- Query for rows that match fts5. Return a copy of the body column
-- of each row with the matches surrounded by b/b tags.
SELECT highlight(email, 2, b, /b) FROM email(fts5);
可用辅助功能的说明和更多详细信息 关于特殊“等级”列的配置可在下面找到。自定义辅助函数也可以在 C 语言中实现并注册 FTS5就像自定义 SQL 函数可以注册到 SQLite 核心一样。
除了搜索包含术语的所有行外FTS5 还允许 要搜索包含以下内容的行的用户
任何以指定前缀开头的术语“phrases” - 必须在 文档以匹配查询出现在指定术语中、前缀术语或短语的集合 彼此接近这些查询称为“NEAR 查询”或上述任何一项的布尔组合。
通过提供更复杂的搜索来请求这种高级搜索 FTS5 查询字符串作为 MATCH 运算符右侧的文本或 运算符或作为表值函数语法的第一个参数。这 此处介绍了完整的查询语法。 2. FTS5的编译和使用
2.1. 将FTS5构建为SQLite的一部分
自 3.9.0 版 2015-10-14 起 FTS5 作为 SQLite 合并的一部分包含在内。 如果使用两个 autoconf 构建系统之一则 FTS5 是 通过在运行配置时指定“--enable-fts5”选项来启用 脚本。FTS5 目前默认为 source-tree configure 脚本并默认启用 合并配置脚本但这些默认值可能 未来会发生变化。
或者如果 sqlite3.c 是使用其他构建系统编译的则通过安排 要定义的SQLITE_ENABLE_FTS5预处理器符号。
2.2. 构建可加载扩展
或者FTS5 可以构建为可加载的扩展。
规范的 FTS5 源代码由一系列 *.c 和其他文件组成 在 SQLite 源代码树的“ext/fts5”目录中。构建过程减少了 这只有两个文件 - “fts5.c” 和 “fts5.h” - 可用于构建一个 SQLite 可加载扩展。
从 fossil 获取最新的 SQLite 代码。按照如何编译 SQLite 中所述创建 Makefile。构建“fts5.c”目标。这也创建了 fts5.h。
$ wget -c https://www.sqlite.org/src/tarball/SQLite-trunk.tgz?uuidtrunk -O SQLite-trunk.tgz
.... output ...
$ tar -xzf SQLite-trunk.tgz
$ cd SQLite-trunk
$ ./configure make fts5.c
... lots of output ...
$ ls fts5.[ch]
fts5.c fts5.h
然后“fts5.c”中的代码可以编译成可加载的扩展或 静态链接到应用程序如编译可加载扩展中所述。定义了两个入口点两者都 其中做同样的事情
sqlite3_fts_initsqlite3_fts5_init
另一个文件“fts5.h”不是编译 FTS5 扩展所必需的。 它由实现自定义 FTS5 分词器或辅助函数的应用程序使用。 3. 全文查询语法
以下块包含 BNF 形式的 FTS 查询语法摘要。 详细解释如下。 phrase : string [*]
phrase : phrase phrase
neargroup : NEAR ( phrase phrase ... [, N] )
query : [ [-] colspec :] [^] phrase
query : [ [-] colspec :] neargroup
query : [ [-] colspec :] ( query )
query : query AND query
query : query OR query
query : query NOT query
colspec : colname
colspec : { colname1 colname2 ... } 3.1. FTS5 字符串
在 FTS 表达式中可以通过以下两种方式之一指定字符串 用双引号 “ 括起来。在字符串中任何嵌入 双引号字符可以转义为 SQL 样式 - 通过添加第二个 双引号字符。 As an FTS5 bareword that is not AND, OR or NOT (case sensitive). An FTS5 bareword is a string of one or more consecutive characters that are all either: Non-ASCII range characters (i.e. unicode codepoints greater than 127), orOne of the 52 upper and lower case ASCII characters, orOne of the 10 decimal digit ASCII characters, or下划线字符unicode codepoint 96。替换字符unicode codepoint 26。 包含任何其他字符的字符串必须加引号。字符 目前不允许在简陋的单词中不是引号字符并且 目前在FTS5查询表达式中没有任何特殊用途 在将来的某个时候允许以简陋的词语或用于实现 新的查询功能。这意味着当前 语法错误因为它们在引号之外包含这样的字符 FTS5 的未来版本可能会对字符串进行不同的解释。 3.2. FTS5 短语
fts5 查询中的每个字符串都由分词器和零个或多个令牌的列表解析“标记化”或者 术语提取。例如默认分词器对字符串“alpha beta gamma“到三个独立的标记 - ”alpha“、”beta“和”gamma“ - 在 次序。
FTS 查询由短语组成。短语是 一个或多个令牌。查询中每个字符串的标记都组成了一个 单个短语。两个短语可以连接成一个大短语 使用“”运算符。例如假设正在使用分词器模块 将输入“one.two.three”标记化为三个单独的标记如下所示 四个查询都指定相同的短语 ... MATCH one two three
... MATCH one two three
... MATCH one two three
... MATCH one.two.three 如果文档包含至少一个子序列则短语与文档匹配 与组成短语的标记序列匹配的标记。 3.3. FTS5前缀查询
如果 FTS 表达式中的字符串后面跟有“*”字符则 final 从字符串中提取的令牌被标记为前缀令牌。作为你 可能期望前缀令牌与它所属的任何文档令牌匹配 前缀。例如以下块中的前两个查询将匹配 包含令牌“one”后跟令牌的任何文档 “two”然后是任何以“thr”开头的标记。 ... MATCH one two thr *
... MATCH one two thr*
... MATCH one two thr* -- May not work as expected! 上述块中的最后一个查询可能无法按预期工作。因为 “*”字符在双引号内它将被传递给分词器 这可能会丢弃它或者可能取决于特定的分词器 在使用中将其作为最终令牌的一部分包含在内而不是将其识别为 一个特殊的 FTS 角色。 3.4. FTS5 初始令牌查询
如果“^”字符紧接在不属于 NEAR 查询则该短语仅匹配文档前提是它以 列中的第一个标记。“^”语法可以与列筛选器结合使用但不能插入到 一句话。 ... MATCH ^one -- first token in any column must be one
... MATCH ^ one two -- phrase one two must appear at start of a column
... MATCH ^ one two -- same as previous
... MATCH a : ^two -- first token of column a must be two
... MATCH NEAR(^one, two) -- syntax error!
... MATCH one ^two -- syntax error!
... MATCH ^one two -- May not work as expected! 3.5. FTS5 NEAR查询
两个或多个短语可以分组到一个 NEAR 组中。NEAR组 由令牌“NEAR”区分大小写指定后跟 open 括号字符后跟两个或多个空格分隔的短语可选后跟逗号和数值参数 N后跟 一个紧括号。例如
... MATCH NEAR(one two three four, 10)
... MATCH NEAR(one two thr* four)
如果未提供 N 参数则默认为 10。NEAR组 如果文档包含至少一个标记集则匹配该文档
包含每个短语的至少一个实例并且对于第一个短语末尾之间的标记数 团块中最后一个乐句的开头小于或等于 N。
例如
CREATE VIRTUAL TABLE f USING fts5(x);
INSERT INTO f(rowid, x) VALUES(1, A B C D x x x E F x);... MATCH NEAR(e d, 4); -- Matches!
... MATCH NEAR(e d, 3); -- Matches!
... MATCH NEAR(e d, 2); -- Does not match!... MATCH NEAR(c d e f, 3); -- Matches!
... MATCH NEAR(c e f, 3); -- Does not match!... MATCH NEAR(a d e, 6); -- Matches!
... MATCH NEAR(a d e, 5); -- Does not match!... MATCH NEAR(a b c d b c e f, 4); -- Matches!
... MATCH NEAR(a b c d b c e f, 3); -- Does not match! 3.6. FTS5色谱柱过滤器
单个短语或 NEAR 组可能仅限于匹配 通过在 FTS 表前面加上列名来指定该表的列 后跟冒号字符。或者通过添加前缀到一组列 用括号括起来的空格分隔的列名列表 “大括号”后跟冒号字符。可以指定列名 使用上面为字符串描述的两种形式之一。与字符串不同 是短语的一部分列名不会传递给分词器模块。 列名不区分大小写通常以SQLite列名的方式 - 大写/小写等效性仅适用于 ASCII 范围字符。
... MATCH colname : NEAR(one two three four, 10)
... MATCH colname : one two three... MATCH {col1 col2} : NEAR(one two three four, 10)
... MATCH {col2 col1 col3} : one two three
如果列筛选器规范前面有“-”字符则 它被解释为不匹配的列列表。例如 -- Search for matches in all columns except colname
... MATCH - colname : NEAR(one two three four, 10)-- Search for matches in all columns except col1, col2 and col3
... MATCH - {col2 col1 col3} : one two three 列筛选器规范也可以应用于任意表达式 括号内。在这种情况下列筛选器适用于所有 表达式中的短语。嵌套列筛选器操作只能 进一步限制匹配的列子集它们不能用于 重新启用筛选的列。例如 -- The following are equivalent:
... MATCH {a b} : ( {b c} : hello AND world )
... MATCH (b : hello) AND ({a b} : world) 最后可以使用以下方法指定单个列的列过滤器 列名作为 MATCH 运算符的 LHS而不是通常的 表名。例如 -- Given the following table
CREATE VIRTUAL TABLE ft USING fts5(a, b, c);-- The following are equivalent
SELECT * FROM ft WHERE b MATCH uvw AND xyz;
SELECT * FROM ft WHERE ft MATCH b : (uvw AND xyz);-- This query cannot match any rows (since all columns are filtered out):
SELECT * FROM ft WHERE b MATCH a : xyz; 3.7. FTS5布尔运算符
短语和 NEAR 组可以使用布尔值排列成表达式 运算符。按优先顺序从最高最紧密分组到 最低最松散的分组运算符为
算子功能query1 NOT query2如果 query1 匹配且 query2 不匹配则匹配。query1 AND query2如果 query1 和 query2 都匹配则匹配。query1 OR query2如果 query1 或 query2 匹配则匹配。
括号可用于对表达式进行分组以便修改运算符 以通常的方式优先。例如 -- Because NOT groups more tightly than OR, either of the following may
-- be used to match all documents that contain the token two but not
-- three, or contain the token one.
... MATCH one OR two NOT three
... MATCH one OR (two NOT three)-- Matches documents that contain at least one instance of either one
-- or two, but do not contain any instances of token three.
... MATCH (one OR two) NOT three 短语和 NEAR 组也可以通过隐式 AND 运算符连接。 为简单起见上面的 BNF 语法中没有显示这些内容。从本质上讲任何 短语序列或 NEAR 组包括仅限于匹配的组 指定的列仅由空格分隔就像有一个 每对短语或 NEAR 组之间的隐式 AND 运算符。含蓄 AND 运算符从不插入在 括号。隐式 AND 运算符的分组比其他所有运算符都更紧密 运算符包括 NOT。例如 ... MATCH one two three -- one AND two AND three
... MATCH three one two -- three AND one two
... MATCH NEAR(one two) three -- NEAR(one two) AND three
... MATCH one OR two three -- one OR two AND three
... MATCH one NOT two three -- one NOT (two AND three)... MATCH (one OR two) three -- Syntax error!
... MATCH func(one two) -- Syntax error! 4. FTS5表的创建和初始化
每个参数都指定为“CREATE VIRTUAL TABLE ...使用 fts5 ...语句可以是列声明也可以是配置选项。列声明由一个或多个空格分隔的 FTS5 组成 以 SQLite 可接受的任何方式引用的裸词或字符串文字。
列声明中的第一个字符串或裸字是列名。它 尝试将 fts5 表列命名为“rowid”或“rank”或 为表本身使用的列分配相同的名称。这不是 支持。
列声明中的每个后续字符串或裸字都是一列 选项用于修改该列的行为。列选项包括 与大小写无关。与 SQLite 核心不同FTS5 考虑无法识别的列 选项是错误的。目前唯一公认的选项是“未编制索引”见下文。
配置选项由 FTS5 简字组成 - 选项名称 - 后跟“”字符后跟选项值。选项值为 使用单个 FTS5 裸字或字符串文本指定再次引用 以SQLite核心可接受的任何方式。例如 CREATE VIRTUAL TABLE mail USING fts5(sender, title, body, tokenize porter ascii); 目前有以下配置选项
“tokenize”选项用于配置自定义分词器。“prefix”选项用于向 FTS5 表添加前缀索引。“content”选项用于使 FTS5 表成为外部内容表或无内容表。“content_rowid”选项用于设置外部内容表的 rowid 字段。“columnsize”选项用于配置 FTS5 表中每个值的标记大小是否为 单独存储在数据库中。“详细信息”选项。可以使用此选项 通过省略一些信息来减小磁盘上 FTS 索引的大小 从它。 4.1. 未索引列选项
使用“未索引列”选项限定的列的内容不是 添加到FTS指数中。这意味着出于 MATCH 查询和 FTS5 辅助函数的目的该列不包含可匹配的标记。
例如避免将“uuid”字段的内容添加到 FTS 中 指数 CREATE VIRTUAL TABLE customers USING fts5(name, addr, uuid UNINDEXED); 4.2. 前缀索引
默认情况下FTS5 维护一个索引记录每个索引的位置 文档集中的令牌实例。这意味着查询完成 令牌速度很快因为它需要一次查找但需要查询前缀 令牌可能很慢因为它需要范围扫描。例如要查询 前缀令牌“abc*”需要对大于 或等于“abc”且小于“abd”。
前缀索引是一个单独的索引用于记录所有 用于加速的字符中一定长度的前缀标记的实例 对前缀令牌的向上查询。例如优化前缀查询 令牌“abc*”需要由三个字符前缀组成的前缀索引。
要向 FTS5 表添加前缀索引请将“前缀”选项设置为 单个正整数或包含空格的文本值 一个或多个正整数值的分隔列表。前缀索引是 为指定的每个整数创建。如果多个“前缀”选项是 指定为单个 CREATE VIRTUAL TABLE 语句的一部分全部适用。 4.3. 分词器
CREATE VIRTUAL TABLE “tokenize” 选项用于配置 FTS5 表使用的特定分词器。option 参数必须是 FTS5 裸字或 SQL 文本文字。论证的文本本身就是 被视为一个或多个 FTS5 裸字或 SQL 文本的空格序列 文字。其中第一个是要使用的分词器的名称。第二个 后续列表元素如果存在是传递给 分词器实现。
与选项值和列名不同SQL 文本文本意为 分词器必须使用单引号字符进行引号。例如 -- The following are all equivalent
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize porter ascii);
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize porter ascii);
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize porter ascii);
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize porter ascii);-- But this will fail:
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize porter ascii);-- This will fail too:
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize porter ascii); FTS5 具有三个内置分词器模块详见后续 部分
unicode61 分词器基于 Unicode 6.1 标准。这 是默认值。ascii 分词器它假定 ASCII 代码点范围 0-127 将被视为标记字符。porter tokenizer它实现 porter 词干提取算法。
也可以为 FTS5 创建自定义分词器。此处介绍了用于执行此操作的 API。
4.3.1. Unicode61分词器
unicode 分词器将所有 unicode 字符分类为任一字符 “分隔符”或“标记”字符。默认情况下所有空格和标点符号 Unicode 6.1 定义的字符被视为分隔符并且所有 其他字符作为标记字符。更具体地说所有 unicode 分配给以“L”或“N”开头的一般类别的字符字母和数字 具体而言或类别“Co”“其他私人用途”被视为代币。 所有其他字符都是分隔符。
一个或多个标记字符的每个连续运行都被视为 令 牌。根据定义的规则分词器不区分大小写 Unicode 6.1.
默认情况下从所有拉丁字母字符中删除变音符号。这 例如表示“A”、“a”、“À”、“à”、“”和“â” 都被认为是等价的。
令牌规范中“unicode61”后面的任何参数都将被处理 作为交替选项名称和值的列表。Unicode61 支持 以下选项
选择用法remove_diacritics此选项应设置为“0”、“1”或“2”。默认值为“1”。 如果设置为“1”或“2”则从拉丁脚本中删除变音符号 如上所述的字符。但是如果它设置为“1”则变音符号 在相当罕见的情况下单个 Unicode 代码点不会被删除 用于表示具有多个变音符号的字符。例如 变音符号不会从代码点0x1ED9中删除“拉丁文小写字母 O CIRCUMFLEX 和下面的点“。从技术上讲这是一个错误但无法修复 不会产生向后兼容性问题。如果此选项设置为 “2”则从所有拉丁字符中正确删除变音符号。类别此选项可用于修改 Unicode 常规类别集 被视为对应于标记字符。参数必须 由两个字符的常规类别的空格分隔列表组成 缩写例如“Lu”或“Nd”或与第二个字符相同 替换为星号“*”解释为球形图案。默认值 值为“L* N* Co”。令牌字符此选项用于指定其他 Unicode 字符 应被视为标记字符即使它们是空格或 标点符号符合 Unicode 6.1。中的所有字符 此选项设置为的字符串被视为标记字符。分隔符此选项用于指定其他 Unicode 字符 应被视为分隔符即使它们是标记 符合 Unicode 6.1 的字符。字符串中的所有字符 此选项设置为被视为分隔符。
例如 -- Create an FTS5 table that does not remove diacritics from Latin
-- script characters, and that considers hyphens and underscore characters
-- to be part of tokens.
CREATE VIRTUAL TABLE ft USING fts5(a, b,tokenize unicode61 remove_diacritics 0 tokenchars -_
);或 -- Create an FTS5 table that, as well as the default token character classes,
-- considers characters in class Mn to be token characters.
CREATE VIRTUAL TABLE ft USING fts5(a, b,tokenize unicode61 categories L* N* Co Mn
);fts5 unicode61 分词器与 fts3/4 逐字节兼容 Unicode61 分词器。
4.3.2. Ascii 分词器
Ascii 分词器类似于 Unicode61 分词器不同之处在于
所有非 ASCII 字符代码点大于 127 的字符都是 始终被视为标记字符。如果指定了任何非 ASCII 字符 作为分隔符选项的一部分它们将被忽略。大小写折叠仅对 ASCII 字符执行。因此虽然“A”和 “a”被认为是等价的“Ô和“ã”是不同的。不支持remove_diacritics选项。
例如 -- Create an FTS5 table that uses the ascii tokenizer, but does not
-- consider numeric characters to be part of tokens.
CREATE VIRTUAL TABLE ft USING fts5(a, b,tokenize ascii separators 0123456789
);4.3.3. Porter 分词器
porter 分词器是一个包装分词器。它需要一些输出 其他分词器并在将每个令牌返回给 FTS5 之前将 porter 词干提取算法应用于每个令牌。这允许搜索像 “更正”以匹配类似的词例如“更正”或“更正”。这 Porter Stemmer 算法设计用于英语术语 仅 - 将它与其他语言一起使用可能会也可能不会改善搜索实用程序。
默认情况下porter 分词器作为默认值的包装器运行 分词器 Unicode61。或者如果将一个或多个额外的参数添加到 “porter”之后的“tokenize”选项它们被视为 Porter Stemmer 使用的底层分词器。例如 -- Two ways to create an FTS5 table that uses the porter tokenizer to
-- stem the output of the default tokenizer (unicode61).
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize porter);
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize porter unicode61);-- A porter tokenizer used to stem the output of the unicode61 tokenizer,
-- with diacritics removed before stemming.
CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize porter unicode61 remove_diacritics 1);4.3.4. 卦分词器
卦分词器扩展了 FTS5 以支持子字符串 一般情况下的匹配而不是通常的令牌匹配。使用 卦标记器查询或短语标记可以匹配任何字符序列 在一行中而不仅仅是一个完整的令牌。例如 CREATE VIRTUAL TABLE tri USING fts5(a, tokenizetrigram);
INSERT INTO tri VALUES(abcdefghij KLMNOPQRST uvwxyz);-- The following queries all match the single row in the table
SELECT * FROM tri(cdefg);
SELECT * FROM tri(cdefg AND pqr);
SELECT * FROM tri(hij klm NOT stuv);卦分词器支持以下选项
选择用法case_sensitive此值可以设置为 1 或 0默认值。如果设置为 1 则匹配区分大小写。否则如果此选项设置为 0匹配不区分大小写。remove_diacritics此值也可以设置为 1 或 0默认值。它可能只有 如果case_sensitive选项设置为 0则设置为 1 - 同时设置 选项设置为 1 是错误的。如果设置了此选项则变音符号为 在匹配之前从文本中删除例如使“á”与“a”匹配。 -- A case-sensitive trigram index
CREATE VIRTUAL TABLE tri USING fts5(a, tokenizetrigram case_sensitive 1);除非设置了 remove_diacritics 选项否则使用卦象的 FTS5 表 分词器还支持索引 GLOB 和 LIKE 模式匹配。例如 SELECT * FROM tri WHERE a LIKE %cdefg%;
SELECT * FROM tri WHERE a GLOB *ij klm*xyz;如果创建 FTS5 卦形分词器时 case_sensitive 选项设置为 1 它可能只索引 GLOB 查询而不能索引 LIKE。
笔记
由少于 3 个 unicode 字符组成的子字符串不匹配任何 与全文查询一起使用时的行。如果 LIKE 或 GLOB 模式没有 包含至少一个非通配符 unicode 字符序列 FTS5 回退到整个表的线性扫描。如果使用 detailnone 或 detailcolumn 选项创建 FTS5 表 指定的全文查询不得包含任何长度超过 3 的标记 Unicode 字符。LIKE 和 GLOB 模式匹配可能会稍微慢一些 但仍然有效。如果索引仅用于 LIKE 和/或 GLOB 模式匹配这些选项值得尝试以减少 索引大小。 4.4. 外部内容表和无内容表
通常当将一行插入到 FTS5 表中时除了构建 索引FTS5 复制原始行内容。 当用户或 辅助函数实现这些值是 从内容的私人副本中读取。可以使用“内容”选项 创建仅存储 FTS 全文索引条目的 FTS5 表。 因为列值本身通常比 关联的全文索引条目这样可以节省大量数据库空间。
有两种方法可以使用“内容”选项
通过将其设置为空字符串来创建无内容的 FTS5 表。在 在这种情况下FTS5 假定原始列值不可用 在处理查询时。全文查询和一些辅助查询 函数仍然可以使用但除了 rowid 之外没有列值 可以从表中读取。通过将其设置为数据库对象的名称表、虚拟表或 视图FTS5 可以随时查询以检索列 值。这称为“外部内容”表。在这种情况下所有 可以使用 FTS5 功能但这是用户的责任 确保全文索引的内容与 命名的数据库对象。如果不是查询结果可能是 不可预知的。 4.4.1. 无内容表
通过将“content”选项设置为 一个空字符串。例如 CREATE VIRTUAL TABLE f1 USING fts5(a, b, c, content);无内容的 FTS5 表不支持 UPDATE 或 DELETE 语句或者 不为 rowid 字段提供非 NULL 值的 INSERT 语句。 无内容表不支持 REPLACE 冲突处理。取代 和 INSERT OR REPLACE 语句被视为常规 INSERT 语句。 可以使用 FTS5 delete 命令从无内容表中删除行。
尝试从无内容中读取除 rowid 之外的任何列值 FTS5 表返回 SQL NULL 值。 4.4.2. 无内容删除表
从版本 3.43.0 开始还提供无内容删除表。 通过将 content 选项设置为 空字符串并将 contentless_delete 选项设置为 1。例如 CREATE VIRTUAL TABLE f1 USING fts5(a, b, c, content, contentless_delete1);无内容删除表与无内容表的不同之处在于
Contentless-delete 表同时支持 DELETE 和“INSERT OR REPLACE INTO“语句。无内容删除表支持 UPDATE 语句但前提是新 为 fts5 表的所有用户定义列提供值。无内容删除表不支持 FTS5 delete 命令。 -- Supported UPDATE statement:
UPDATE f1 SET a?, b?, c? WHERE rowid?;-- This UPDATE is not supported, as it does not supply a new value
-- for column c.
UPDATE f1 SET a?, b? WHERE rowid?;除非需要向后兼容性否则新代码应首选 Contentless-Delete 表到无内容表。 4.4.3. 外部内容表
通过设置内容来创建外部内容 FTS5 表 选项添加到表、虚拟表或视图的名称以下简称“内容 表“。每当需要列值时 FTS5它查询内容表如下rowid 为 对于哪些值需要绑定到 SQL 变量 SELECT content_rowid, cols FROM content WHERE content_rowid ?;在上面的 content 中将替换为内容表的名称。 默认情况下content_rowid 将替换为文本“rowid”。或 如果在 CREATE VIRTUAL TABLE 语句中设置了 “content_rowid” 选项 按该选项的值。cols 替换为逗号分隔的列表 FTS5 表列名称。例如 -- If the database schema is:
CREATE TABLE tbl (a, b, c, d INTEGER PRIMARY KEY);
CREATE VIRTUAL TABLE fts USING fts5(a, c, contenttbl, content_rowidd);-- Fts5 may issue queries such as:
SELECT d, a, c FROM tbl WHERE d ?;还可以按如下方式查询内容表 SELECT content_rowid, cols FROM content ORDER BY content_rowid ASC;
SELECT content_rowid, cols FROM content ORDER BY content_rowid DESC;用户仍然有责任确保 外部内容 FTS5 表与内容表保持同步。 一种方法是使用触发器。例如 -- Create a table. And an external content fts5 table to index it.
CREATE TABLE tbl(a INTEGER PRIMARY KEY, b, c);
CREATE VIRTUAL TABLE fts_idx USING fts5(b, c, contenttbl, content_rowida);-- Triggers to keep the FTS index up to date.
CREATE TRIGGER tbl_ai AFTER INSERT ON tbl BEGININSERT INTO fts_idx(rowid, b, c) VALUES (new.a, new.b, new.c);
END;
CREATE TRIGGER tbl_ad AFTER DELETE ON tbl BEGININSERT INTO fts_idx(fts_idx, rowid, b, c) VALUES(delete, old.a, old.b, old.c);
END;
CREATE TRIGGER tbl_au AFTER UPDATE ON tbl BEGININSERT INTO fts_idx(fts_idx, rowid, b, c) VALUES(delete, old.a, old.b, old.c);INSERT INTO fts_idx(rowid, b, c) VALUES (new.a, new.b, new.c);
END;与无内容表一样外部内容表不支持 REPLACE 冲突处理。指定 REPLACE 冲突处理的任何操作都是 使用 ABORT 处理。 4.4.4. 外部内容表陷阱
用户有责任确保FTS5外部内容 表具有非空 content 选项的表与 content 表本身由 content 选项命名的表。如果这些是 允许变得不一致那么针对 FTS5 的查询结果 表格可能会变得不直观并且看起来不一致。
在这些情况下查询产生的明显不一致的结果 针对FTS5的外部内容表可以理解为如下 如果查询不使用全文索引 - 不包含 MATCH 运算符或等效的表值函数语法 - 然后 查询有效地传递到外部内容表。在 在这种情况下FTS指数的内容对结果没有影响 的查询。 如果查询确实使用全文索引则 FTS5 模块 查询与匹配项的文档对应的 rowid 值集 查询。然后对于每个这样的 rowid它运行类似于以下内容的查询 若要检索任何必需的列值请将“”替换为 rowid value 和 content 并由指定的值content_rowid 对于 content 和 content_rowid 选项 SELECT content_rowid, cols FROM content WHERE content_rowid ?;例如如果使用以下脚本创建数据库 -- Create and populate a table.
CREATE TABLE tbl(a INTEGER PRIMARY KEY, t TEXT);
INSERT INTO tbl VALUES(1, all that glitters);
INSERT INTO tbl VALUES(2, is not gold);-- Create an external content FTS5 table
CREATE VIRTUAL TABLE ft USING fts5(t, contenttbl, content_rowida);则内容表包含两行但 FTS 索引不包含 与它们相对应的条目。在这种情况下将返回以下查询 结果不一致如下所示 -- Returns 2 rows. Because the query does not use the FTS index, it is
-- effectively executed against table tbl directly, and so returns
-- both rows.
SELECT * FROM t1;-- Returns 0 rows. This query does use the FTS index, which currently
-- contains no entries. So it returns 0 rows.
SELECT rowid, t FROM t1(gold)或者如果按如下方式创建和填充数据库 -- Create and populate a table.
CREATE TABLE tbl(a INTEGER PRIMARY KEY, t TEXT);-- Create an external content FTS5 table
CREATE VIRTUAL TABLE ft USING fts5(t, contenttbl, content_rowida);
INSERT INTO ft(rowid, t) VALUES(1, all that glitters);
INSERT INTO ft(rowid, t) VALUES(2, is not gold);则内容表为空但 FTS 索引包含 6种不同的代币。在这种情况下将返回以下查询 结果不一致如下所示 -- Returns 0 rows. Since it does not use the FTS index, the query is
-- passed directly through to table tbl, which contains no data.
SELECT * FROM t1;-- Returns 1 row. The rowid field of the returned row is 2, and
-- the t field set to NULL. t is set to NULL because when the external
-- content table tbl was queried for the data associated with the row
-- with a2 (a is the content_rowid column), none could be found.
SELECT rowid, t FROM t1(gold)如上一节所述内容表上的触发器包括 这是确保 FTS5 外部内容表保持一致的好方法。 但是只有在插入、更新或删除行时才会触发触发器 在内容表中。这意味着例如如果创建数据库 如下 -- Create and populate a table.
CREATE TABLE tbl(a INTEGER PRIMARY KEY, t TEXT);
INSERT INTO tbl VALUES(1, all that glitters);
INSERT INTO tbl VALUES(2, is not gold);-- Create an external content FTS5 table
CREATE VIRTUAL TABLE ft USING fts5(t, contenttbl, content_rowida);-- Create triggers to keep the FTS5 table up to date
CREATE TRIGGER tbl_ai AFTER INSERT ON tbl BEGININSERT INTO ft(rowid, t) VALUES (new.a, new.t);
END;
similar triggers for update delete则内容表和外部内容 FTS5 表不一致如 创建触发器不会从内容表中复制现有行 进入富通指数。触发器只能确保对 创建后的内容表将反映在 FTS 索引中。
在这种情况下以及 FTS 指数及其内容表的任何其他情况 变得不一致可以使用“rebuild”命令完全丢弃 FTS 索引的内容和 根据内容表的当前内容重新生成它。 4.5. Columnsize 选项
通常FTS5 在数据库中维护一个特殊的支持表该表 将每个列值的大小存储在插入到主 FTS5 中的标记中 表。此支持表由 xColumnSize API 函数使用而 xColumnSize API 函数又由 内置的 BM25 排名功能并且可能很有用 以及其他排名功能。
为了节省空间可以通过设置 columnsize 选项设置为零。例如 -- A table without the xColumnSize() values stored on disk:
CREATE VIRTUAL TABLE ft USING fts5(a, b, c, columnsize0);-- Three equivalent ways of creating a table that does store the
-- xColumnSize() values on disk:
CREATE VIRTUAL TABLE ft USING fts5(a, b, c);
CREATE VIRTUAL TABLE ft USING fts5(a, b, c, columnsize1);
CREATE VIRTUAL TABLE ft USING fts5(a, b, columnsize1, c);将 columnsize 选项设置为除 0 或 1。
如果 FTS5 表配置了 columnsize0 但不是无内容表则 xColumnSize API 函数 仍然有效但运行速度要慢得多。在这种情况下而不是阅读 直接从数据库返回的值它读取文本值 本身并按需计算其中的代币。
或者如果该表也是无内容表 则以下情况适用 xColumnSize API 始终返回 -1。没有办法确定 存储在无内容 FTS5 表中的值中的令牌数 配置为 columnsize0。 插入的每个行都必须附带一个显式指定的 rowid 价值。如果无内容表配置了 columnsize0 尝试将 NULL 值插入到 rowid 中是一种SQLITE_MISMATCH 错误。 表上的所有查询都必须是全文查询。换言之 他们必须使用 MATCH 或 运算符并将 table-name 列作为 左手操作数否则使用表值函数语法。任何 不是全文查询的查询会导致错误。
存储 xColumnSize 值的表的名称 除非指定了 columnsize0为 “name_docsize”其中 name 是 FTS5 表本身的名称。可以在现有数据库上使用sqlite3_analyzer工具以确定多少 通过使用 columnsize0 重新创建 FTS5 表可以节省空间。 4.6. 细节选项
对于文档中的每个术语FTS5 维护的 FTS 索引 存储文档的 rowid即包含 列值中的术语和术语的偏移量。“细节” 选项可用于省略其中的一些信息。这样可以减少空间 索引在数据库文件中消耗但也减少了 系统的能力和效率。
详细信息选项可以设置为“full”默认值、“column”或 “无”。例如 -- The following two lines are equivalent (because the default value
-- of detail is full.
CREATE VIRTUAL TABLE ft1 USING fts5(a, b, c);
CREATE VIRTUAL TABLE ft1 USING fts5(a, b, c, detailfull);CREATE VIRTUAL TABLE ft2 USING fts5(a, b, c, detailcolumn);
CREATE VIRTUAL TABLE ft3 USING fts5(a, b, c, detailnone);如果详细信息选项设置为列则对于每个术语FTS 索引仅记录 rowid 和列号省略术语偏移量 信息。这会导致以下限制
NEAR 查询不可用。短语查询不可用。假设该表不是无内容表则 xInstCount、xInst、xPhraseFirst 和 xPhraseNext 的速度比平时慢。这是因为不是读取所需的数据 他们必须直接从 FTS 索引加载和标记文档文本 按需。如果该表也是无内容表则 xInstCount、xInst、 xPhraseFirst 和 xPhraseNext API 的行为就好像当前行不包含 短语完全匹配即 xInstCount 返回 0。
如果 detail 选项设置为 none则对于每个术语 FTS 索引仅记录 rowid。列和偏移量信息 被省略。以及上面针对 detailcolumn 逐项列出的限制 模式下这会带来以下额外限制
列筛选器查询不可用。假设该表不是无内容表则 xPhraseFirstColumn 和 xPhraseNextColumn 的速度比平时慢。如果该表也是无内容表则 xPhraseFirstColumn 和 xPhraseNextColumn API 的行为就好像当前行不包含任何短语一样 完全匹配即 xPhraseFirstColumn 将迭代器设置为 EOF。
在一项索引大量电子邮件磁盘上为 1636 MiB的测试中FTS 磁盘上的索引为 743 MiBdetailfull 为 340 MiBdetailcolumn 和 134 带有 detailnone 的 MiB。 4.7. Tokendata选项
此选项仅对实现自定义分词器的应用程序有用。通常分词器可能会 返回由任何字节序列包括 0x00 个字节组成的标记。 但是如果表指定了 tokendata1 选项则 fts5 将忽略 令牌中的第一个 0x00 字节和任何尾随数据 的匹配。它仍然存储分词器返回的整个令牌 但它被 FTS5 内核忽略了。
令牌的完整版本包括任何0x00字节和尾随数据 可用于自定义辅助 通过 xQueryToken 和 xInstToken API 进行函数。
这对于排名函数可能很有用。自定义分词器可以 向某些文档标记添加额外数据允许排名函数给出 某些标记例如文档标题中的标记的命中权重更大。
或者自定义分词器和自定义辅助工具的组合 函数可用于实现非对称搜索。分词器可以比如为每个文档令牌返回 令牌的大小写规范化和未标记版本后跟0x00 byte后跟文档中令牌的全文。查询时 fts5 将提供结果就好像查询中的所有字符都是 大小写标准化且未标记。然后可以使用自定义辅助功能 在查询的 WHERE 子句中筛选出任何不匹配的行 在文档或查询术语中的二级或三级标记上。 5. 辅助功能
辅助函数类似于 SQL 标量函数 除了它们只能在全文查询中使用那些使用 MATCH 运算符在 FTS5 表上。他们的结果是根据 仅在传递给他们的参数上而且在当前比赛和 匹配的行。例如辅助函数可以返回一个数值 指示匹配的准确性参见 bm25 函数 或匹配行中包含一个或多个文本片段的文本片段 搜索词的实例参见 Snippet 函数。
要调用辅助函数FTS5 表的名称应为 指定为第一个参数。其他论点可能遵循第一个论点 取决于所调用的特定辅助函数。例如要 调用“highlight”函数 SELECT highlight(email, 2, b, /b) FROM email WHERE email MATCH fts5作为 FTS5 的一部分提供的内置辅助功能在 以下部分。应用程序还可以在 C 中实现自定义辅助功能。
5.1. 内置辅助功能
FTS5 提供三个内置辅助功能
bm25 辅助函数返回一个实值 反映当前匹配的准确性。更好的匹配是 分配的数值较低。highlight 辅助函数返回一个副本 当前匹配的一列中的文本与每个 结果中查询项的实例周围有指定的 标记例如“b”和“/b”。snippet 辅助函数选择一个短 来自匹配行的一列的文本片段并返回 它与被标记包围的查询术语的每个实例一起 与 highlight 函数的方式相同。文本片段是 选择以最大化它包含的查询词的数量。 5.1.1. bm25 函数
内置辅助函数 bm25 返回一个实数值指示 当前行与全文查询的匹配程度。比赛越好 返回的值在数值上越小。如下所示的查询可能 用于按从最佳到最差的顺序返回匹配项 SELECT * FROM fts WHERE fts MATCH ? ORDER BY bm25(fts)为了计算文档分数全文查询是分开的 添加到其组成短语中。文档 D 和 然后按如下方式计算查询 Q 在上面nPhrase 是查询中的短语数。|D|是当前文档中的令牌数avgdl 是 FTS5 表。k1 和 b 都是常数 硬编码分别为 1.2 和 0.75。
公式开头的“-1”项在大多数中都找不到 BM25 算法的实现。没有它就会分配更好的匹配项 BM25 分数在数字上更高。由于默认排序顺序是 “升序”这意味着将“ORDER BY bm25fts”附加到查询中会 使结果按从最差到最佳的顺序返回。“DESC”关键字 为了首先返回最佳匹配项。为了 避免了这个陷阱BM25 的 FTS5 实现使结果成倍增加 在返回之前按 -1确保分配更好的匹配项 数字上较低的分数。
IDFqi 是查询的逆文档频率 短语 i.计算方法如下其中 N 是总数 FTS5 表中的行数nqi 是总数 包含至少一个短语 i 实例的行数 最后fqiD 是短语 i 的短语频率。默认情况下这只是短语的出现次数 在当前行中。但是通过将额外的实值参数传递给 bm25 SQL 函数中表的每一列都可以分配不同的 权重和短语频率的计算方法如下 其中 wc 是分配给 c 列的权重nqic 是短语 i 在 当前行的 C 列。传递给 bm25 的第一个参数 表名后面是分配给最左边列的权重 FTS5 表。第二个是分配给最左边第二个的权重 列依此类推。如果所有表列的参数都不够 其余列的权重为 1.0。如果太多 尾随参数则忽略附加内容。例如 -- Assuming the following schema:
CREATE VIRTUAL TABLE email USING fts5(sender, title, body);-- Return results in bm25 order, with each phrase hit in the sender
-- column considered the equal of 10 hits in the body column, and
-- each hit in the title column considered as valuable as 5 hits in
-- the body column.
SELECT * FROM email WHERE email MATCH ? ORDER BY bm25(email, 10.0, 5.0);有关以下内容的更多信息请参阅维基百科 BM25及其变体。 5.1.2. highlight 函数
highlight 函数从指定的文本返回文本的副本 当前行的列并插入了额外的标记文本以标记开始 和短语结尾匹配。
highlight 必须使用以下三个参数来调用 表名。解释如下
一个整数指示要读取 文本来自。列从零开始从左到右编号。在每个短语匹配之前要插入的文本。在每个短语匹配后插入的文本。
例如 -- Return a copy of the text from the leftmost column of the current
-- row, with phrase matches marked using html b tags.
SELECT highlight(fts, 0, b, /b) FROM fts WHERE fts MATCH ?在两个或多个短语实例重叠的情况下共享一个或多个 标记为每组插入一个打开和关闭标记 重叠的短语。例如 -- Assuming this:
CREATE VIRTUAL TABLE ft USING fts5(a);
INSERT INTO ft VALUES(a b c x c d e);
INSERT INTO ft VALUES(a b c c d e);
INSERT INTO ft VALUES(a b c d e);-- The following SELECT statement returns these three rows:
-- [a b c] x [c d e]
-- [a b c] [c d e]
-- [a b c d e]
SELECT highlight(ft, 0, [, ]) FROM ft WHERE ft MATCH abc AND cde;5.1.3. snippet 函数
snippet 函数类似于 highlight不同之处在于 返回整个列值它会自动选择并提取一个 要处理和返回的文档文本的简短片段。snippet 函数 必须在 table name 参数后面传递五个参数
一个整数指示要选择的 FTS 表列的索引 返回的文本。列按从左到右的顺序编号 从零开始。负值表示列应 被自动选中。在返回的文本中匹配的每个短语之前插入的文本。在返回的文本中匹配的每个短语之后插入的文本。要添加到所选文本的开头或结尾以指示的文本 返回的文本不会出现在其列的开头或结尾 分别。返回文本中的最大标记数。这必须更大 小于零且等于或小于 64。 5.2. 按辅助功能结果排序
所有 FTS5 表都具有一个名为“rank”的特殊隐藏列。如果 当前查询不是全文查询即如果它不包含 MATCH operator则 “rank” 列的值始终为 NULL。否则在 全文查询列排名默认包含与 通过执行 bm25 辅助函数返回不带尾随 参数。
从排名列读取和使用 bm25 之间的区别 直接在查询中的函数仅在按 返回值。在这种情况下使用“rank”比使用bm25更快。 -- The following queries are logically equivalent. But the second may
-- be faster, particularly if the caller abandons the query before
-- all rows have been returned (or if the queries were modified to
-- include LIMIT clauses).
SELECT * FROM fts WHERE fts MATCH ? ORDER BY bm25(fts);
SELECT * FROM fts WHERE fts MATCH ? ORDER BY rank;而不是使用没有尾随参数的 bm25特定的辅助词 映射到排名列的函数可以在每个查询上配置 基础或者为 FTS 表设置不同的持久默认值。
要更改单个查询的排名列的映射 与以下任一内容类似的术语将添加到 查询 rank MATCH auxiliary-function-name(arg1, arg2, ...)
rank auxiliary-function-name(arg1, arg2, ...)MATCH 或 运算符的右侧必须是常量 计算结果为由辅助函数组成的字符串的表达式 invoke后跟括号内零个或多个逗号分隔的参数。 参数必须是 SQL 文本。例如 -- The following queries are logically equivalent. But the second may
-- be faster. See above.
SELECT * FROM fts WHERE fts MATCH ? ORDER BY bm25(fts, 10.0, 5.0);
SELECT * FROM fts WHERE fts MATCH ? AND rank MATCH bm25(10.0, 5.0) ORDER BY rank;表值函数语法也可用于指定替代方法 排名函数。在这种情况下描述排名函数的文本应 指定为第二个表值函数参数。以下三个 查询是等效的 SELECT * FROM fts WHERE fts MATCH ? AND rank MATCH bm25(10.0, 5.0) ORDER BY rank;
SELECT * FROM fts WHERE fts ? AND rank bm25(10.0, 5.0) ORDER BY rank;
SELECT * FROM fts WHERE fts(?, bm25(10.0, 5.0)) ORDER BY rank;可以修改表的排名列的默认映射 使用 FTS5 秩配置选项。
6. 特殊 INSERT 命令 6.1. automerge 配置选项
而不是在磁盘上使用单个数据结构来存储全文 indexFTS5 使用一系列 b 树。每次新交易 committed包含已提交事务内容的新 B 树 写入数据库文件。查询全文索引时每个 b-tree 必须单独查询并合并结果然后才能 返回给用户。
为了防止数据库中的 b 树数量变得太多 大减慢查询速度较小的 B 树会定期合并到 包含相同数据的单个较大的 B 树。默认情况下会发生这种情况 自动在 INSERT、UPDATE 或 DELETE 语句中修改 全文索引。“automerge”参数确定有多少个较小的 B 树一次合并在一起。将其设置为小值可以 加快查询速度因为他们必须查询和合并更少的结果 b-trees但也会减慢对数据库的写入速度因为每个 INSERT UPDATE 或 DELETE 语句必须作为自动的一部分执行更多工作 合并过程。
构成全文索引的每个 b 树都被分配给一个“级别” 根据其大小。0 级 b 树是最小的因为它们包含 单个事务的内容。更高级别的 b 树是 将两个或多个 0 级 B 树合并在一起使它们更大。FTS5型 一旦存在 M 个或多个 b 树就开始将 b 树合并在一起 具有相同的级别其中 M 是“自动合并”的值 参数。
“automerge”参数允许的最大值为 16。默认值 值为 4。将“automerge”参数设置为 0 将禁用自动 完全 b 树的增量合并。 INSERT INTO ft(ft, rank) VALUES(automerge, 8);6.2. crisismerge 配置选项
“crisismerge”选项类似于“automerge”因为它确定 构成全文索引的组件 B 树的频率和频率 合并在一起。一旦在单个级别上存在 C 或多个 b 树 在全文索引中其中 C 是“crisismerge”的值 选项关卡上的所有 B 树都会立即合并到一个 B 树中。
此选项与“自动合并”选项之间的区别在于当 达到“自动合并”限制 FTS5 仅开始合并 b 树 一起。大部分工作是作为后续 INSERT 的一部分执行的 UPDATE 或 DELETE 操作。而当达到“crisismerge”限制时 有问题的 B 树都会立即合并。这意味着 INSERT 触发危机合并的 UPDATE 或 DELETE 可能需要很长时间才能 完成。
默认的“crisismerge”值为 16。没有上限。尝试 将“crisismerge”参数设置为值 0 或 1 等效于 将其设置为默认值 16。尝试将 “crisismerge”选项设置为负值。 INSERT INTO ft(ft, rank) VALUES(crisismerge, 16);6.3. delete 命令
此命令仅适用于外部内容和无内容表。它 用于从 全文索引。此命令和 delete-all 命令是从 无内容表。
为了使用此命令删除行文本值“delete” 必须插入到与表同名的特殊列中。 要删除的行的 rowid 将插入到 rowid 列中。这 插入到其他列中的值必须与当前值匹配 存储在表中。例如 -- Insert a row with rowid14 into the fts5 table.
INSERT INTO ft(rowid, a, b, c) VALUES(14, $a, $b, $c);-- Remove the same row from the fts5 table.
INSERT INTO ft(ft, rowid, a, b, c) VALUES(delete, 14, $a, $b, $c);如果值作为“删除”的一部分“插入”到文本列中 命令与当前存储在表中的命令不同 结果可能是不可预测的。
原因很容易理解插入文档时 在 FTS5 表中在全文索引中添加一个条目以记录 每个标记在新文档中的位置。删除文档时 需要原始数据才能确定以下条目集 需要从全文索引中删除。因此如果提供给 FTS5 的数据 删除行时使用此命令与用于删除的行不同 确定插入时的令牌实例集一些全文 索引条目可能无法正确删除或者 FTS5 可能会尝试删除索引 不存在的条目。这可以将全文索引保留在 不可预测的状态使未来的查询结果不可靠。 6.4. delete-all 命令
此命令仅适用于外部内容和无内容表包括无内容删除表。它删除了所有 全文索引中的条目。 INSERT INTO ft(ft) VALUES(delete-all);6.5. deletemerge 配置选项
“deletemerge”选项仅由无内容删除表使用。
从无内容删除表中删除行时条目 与其代币相关的代币不会立即从 FTS 指数中删除。 相反包含已删除行的 rowid 的“逻辑删除”标记是 附加到包含行的 FTS 索引条目的 b 树。当 b-tree任何存在逻辑删除的查询结果行 结果中省略了标记。当 b 树与其他 b 树合并时 b-树删除的行及其逻辑删除标记都将被丢弃。
此选项指定 b 树中必须 在 B 树符合合并条件之前有墓碑标记 - 通过自动合并或 显式用户“合并”命令 - 即使它 不符合“自动合并”和“用户合并”选项确定的通常标准。
例如指定 FTS5 应考虑合并组件 15% 行之后的 B 树具有关联的逻辑删除标记 INSERT INTO ft(ft, rank) VALUES(deletemerge, 15);此选项的默认值为 10。尝试将其设置为小于 零恢复默认值。将此选项设置为 0 或大于 100 确保 b 树永远不会因逻辑删除而符合合并条件 标记。
6.6. integrity-check 命令
此命令用于验证全文索引是否在内部 一致并且可选它与任何外部内容表一致。
通过插入文本值来调用 integrity-check 命令 integrity-check 进入与 FTS5 同名的特殊列 桌子。如果为“rank”列提供了值则该值必须是 0 或 1。例如 INSERT INTO ft(ft) VALUES(integrity-check);
INSERT INTO ft(ft, rank) VALUES(integrity-check, 0);
INSERT INTO ft(ft, rank) VALUES(integrity-check, 1);上述三种形式对于所有 FTS 表都是等效的 不是外部内容表。他们检查索引数据结构是否 没有损坏并且如果 FTS 表不是无内容的则 索引与表本身的内容匹配。
对于外部内容表索引的内容仅为 与外部内容表的内容相比如果值 为排名列指定的是 1。
在所有情况下如果发现任何差异该命令将失败 出现SQLITE_CORRUPT_VTAB错误。 6.7. merge 命令 INSERT INTO ft(ft, rank) VALUES(merge, 500);此命令将 b 树结构合并在一起直到大约 N 页 的合并数据已写入数据库其中 N 是绝对值 作为“merge”命令的一部分指定的参数的值。的大小 每个页面都由 FTS5 pgsz 选项配置。
如果参数为正值则 B 树结构仅符合条件 如果满足以下条件之一则进行合并
在 a 上有 U 或更多这样的 b 树 单级有关b树级别的说明请参阅FTS5自动合并选项的文档其中U是分配的值 到 FTS5 usermerge 选项。合并已经开始可能是通过“合并”命令 指定负参数。
可以判断“合并”命令是否找到任何 b-trees 通过在执行命令之前和之后检查 sqlite3_total_changes API 返回的值来合并在一起。如果 两个值之间的差值为 2 或更大则执行工作。 如果差值小于 2则“merge”命令是无操作的。在这个 至少没有理由再次执行相同的“合并”命令 直到 FTS 表下次更新。
如果参数为负数并且 B 树结构超过 FTS 索引中的一个级别所有 B 树结构都分配给相同的 合并操作开始前的级别。此外如果参数 为负数则 usermerge 配置选项的值不是 尊重 - 同一级别的两个 B 树可以合并在一起。
以上意味着执行“merge”命令时负数 参数直到 sqlite3_total_changes 返回值的前后差小于 2 优化 FTS 索引 与 FTS5 optimize 命令相同。但是如果添加了新的 b 树 在此过程中FTS5 将移动新的 FTS 指数 b-tree 到与现有 b 树相同的级别然后重新启动合并。自 避免这种情况只有第一次调用“merge”才应指定负参数。 每个后续调用“merge”都应指定一个正值以便 由第一次调用启动的合并将运行到完成即使新的 B 树是 添加到FTS指数中。 6.8. optimize 命令
此命令合并当前构成 将全文索引转换为单个大型 B 树结构。这确保了 全文索引占用数据库中的最小空间并且位于 最快的查询表单。
有关更多详细信息请参阅 FTS5 自动合并选项的文档 关于全文索引与其组成部分之间的关系 B树。 INSERT INTO ft(ft) VALUES(optimize);因为它重新组织了整个 FTS 索引所以 optimize 命令可以 需要很长时间才能运行。FTS5 merge 命令可用于除法 将FTS指数优化为多个步骤的工作。为此请执行以下操作
调用“merge”命令一次并将参数设置为 -N然后调用“merge”命令零次或多次并将参数设置为 N。
其中 N 是每次调用中要合并的数据页数 merge 命令。当 之前 sqlite3_total_changes 函数返回的值的差异 在合并命令下降到 2 以下之后。合并命令可以是 作为相同或单独交易的一部分发行并由相同或 不同的数据库客户端。有关详细信息请参阅合并命令的文档。 6.9. pgsz配置选项
此命令用于设置持久性“pgsz”选项。
FTS5维护的全文索引存储为一系列固定大小的索引 数据库表中的 Blob。对于使 将全文索引设置为相同的大小。pgsz 选项确定大小 由后续索引编写者创建的所有 Blob。默认值为 1000。 INSERT INTO ft(ft, rank) VALUES(pgsz, 4072);6.10. rank 配置选项
此命令用于设置永久的“rank”选项。
rank 选项用于更改默认辅助函数映射 对于排名列。该选项应设置为相同的文本值 格式如“rank MATCH”术语所述 以上。例如 INSERT INTO ft(ft, rank) VALUES(rank, bm25(10.0, 5.0));6.11. rebuild 命令
此命令首先删除整个全文索引然后重新生成它 基于表格或内容的内容 表。它不适用于无内容 表。 INSERT INTO ft(ft) VALUES(rebuild);6.12. secure-delete 配置选项
此命令用于设置持久布尔值“secure-delete”选项。 例如 INSERT INTO ft(ft, rank) VALUES(secure-delete, 1);通常当 fts5 表中的条目被更新或删除时取而代之的是 从全文索引中删除条目时删除键将添加到由 交易。这很有效但这意味着旧的全文索引 条目将保留在数据库文件中直到最终被删除 通过对全文索引的合并操作。任何有权访问 数据库可以使用这些条目来简单地重建 删除了 FTS5 表行。但是如果设置了“安全删除”选项 设置为 1则在以下情况下全文条目实际上会从数据库中删除 更新或删除现有的 FTS5 表行。这较慢但是 它可以防止使用旧的全文条目来重建已删除的全文条目 表行。
此选项可确保旧的全文条目不可用于 具有数据库 SQL 访问权限的攻击者。为了确保他们可以 无法被有权访问 SQLite 数据库文件的攻击者恢复 本身应用程序还必须启用 SQLite 核心安全删除 选项带有类似“PRAGMA secure_delete 1”的命令。
警告更新一个或多个表行后或者 如果使用此选项集删除则 FTS5 表可能不再被读取或 由早于 3.42.0 的任何版本的 FTS5第一个版本编写 其中此选项可用。尝试这样做会导致 错误并显示错误消息例如“无效的 FTS5 文件格式找到 5 预期 4 - 运行 rebuild“。FTS5 文件格式可能会被还原因此 早期版本的 FTS5 可以通过在表上运行“rebuild”命令来读取它 版本 3.42.0 或更高版本。
secure-delete 选项的默认值为 0。 6.13. usermerge 配置选项
此命令用于设置持久的“usermerge”选项。
usermerge 选项类似于 automerge 和 crisismerge 选项。 它是将合并在一起的最小 b 树段数 带有正参数的“merge”命令。例如 INSERT INTO ft(ft, rank) VALUES(usermerge, 4);usermerge 选项的默认值为 4。允许的最小值 为 2最大值为 16。 7. 扩展 FTS5
FTS5 具有 API允许通过以下方式对其进行扩展
添加在 C 语言中实现的新辅助函数以及添加新的分词器也是用 C 语言实现的。
本文中描述的内置分词器和辅助函数 文档都是使用所描述的公开可用的 API 实现的 下面。
在新的辅助函数或分词器实现之前可以 在 FTS5 中注册应用程序必须获得指向“fts5_api”的指针 结构。每个数据库连接都有一个fts5_api结构具有 FTS5 扩展已注册。若要获取指针应用程序 使用单个参数调用 SQL 用户定义函数 fts5。那 参数必须设置为指向指向 fts5_api 对象的指针的指针 使用 sqlite3_bind_pointer 接口。 以下示例代码演示了该技术 /*
** Return a pointer to the fts5_api pointer for database connection db.
** If an error occurs, return NULL and leave an error in the database
** handle (accessible using sqlite3_errcode()/errmsg()).
*/
fts5_api *fts5_api_from_db(sqlite3 *db){fts5_api *pRet 0;sqlite3_stmt *pStmt 0;if( SQLITE_OKsqlite3_prepare(db, SELECT fts5(?1), -1, pStmt, 0) ){sqlite3_bind_pointer(pStmt, 1, (void*)pRet, fts5_api_ptr, NULL);sqlite3_step(pStmt);}sqlite3_finalize(pStmt);return pRet;
}向后兼容性警告在 SQLite 版本 3.20.0 2017-08-01 之前fts5 工作得稍微好 不同。扩展 FTS5 的旧应用程序必须修改才能使用 上面显示的新技术。
fts5_api结构定义如下。它公开了三种方法 一个用于注册新的辅助函数和分词器一个用于注册 检索现有分词器。后者旨在促进 类似于内置的“分词器包装器”的实现 Porter 分词器。 typedef struct fts5_api fts5_api;
struct fts5_api {int iVersion; /* Currently always set to 2 *//* Create a new tokenizer */int (*xCreateTokenizer)(fts5_api *pApi,const char *zName,void *pUserData,fts5_tokenizer *pTokenizer,void (*xDestroy)(void*));/* Find an existing tokenizer */int (*xFindTokenizer)(fts5_api *pApi,const char *zName,void **ppUserData,fts5_tokenizer *pTokenizer);/* Create a new auxiliary function */int (*xCreateFunction)(fts5_api *pApi,const char *zName,void *pUserData,fts5_extension_function xFunction,void (*xDestroy)(void*));
};若要调用 fts5_api 对象的方法fts5_api指针本身 应作为方法的第一个参数然后是另一个方法 具体的参数。例如 rc pFts5Api-xCreateTokenizer(pFts5Api, ... other args ...);下面将分别介绍fts5_api结构方法 部分。 7.1. 自定义分词器
若要创建自定义分词器应用程序必须实现三个 函数分词器构造函数 xCreate、析构函数 xDelete 和 函数来执行实际的标记化 xTokenize。每个的类型 函数与fts5_tokenizer结构的成员变量相同 typedef struct Fts5Tokenizer Fts5Tokenizer;
typedef struct fts5_tokenizer fts5_tokenizer;
struct fts5_tokenizer {int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);void (*xDelete)(Fts5Tokenizer*);int (*xTokenize)(Fts5Tokenizer*, void *pCtx,int flags, /* Mask of FTS5_TOKENIZE_* flags */const char *pText, int nText, int (*xToken)(void *pCtx, /* Copy of 2nd argument to xTokenize() */int tflags, /* Mask of FTS5_TOKEN_* flags */const char *pToken, /* Pointer to buffer containing token */int nToken, /* Size of token in bytes */int iStart, /* Byte offset of token within input text */int iEnd /* Byte offset of end of token within input text */));
};/* Flags that may be passed as the third argument to xTokenize() */
#define FTS5_TOKENIZE_QUERY 0x0001
#define FTS5_TOKENIZE_PREFIX 0x0002
#define FTS5_TOKENIZE_DOCUMENT 0x0004
#define FTS5_TOKENIZE_AUX 0x0008/* Flags that may be passed by the tokenizer implementation back to FTS5
** as the third argument to the supplied xToken callback. */
#define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */通过调用 FTS5 模块将实现注册到 fts5_api对象的 xCreateTokenizer 方法。如果已经有 Tokenizer它被替换。如果非 NULL xDestroy 参数 传递给 xCreateTokenizer它与 pUserData 的副本一起调用 当数据库句柄关闭或 标记器被替换。
如果成功xCreateTokenizer 返回 SQLITE_OK。否则它 返回 SQLite 错误代码。在这种情况下不会调用 xDestroy 函数。
当 FTS5 表使用自定义分词器时FTS5 核心调用 xCreate 一次创建分词器然后 xTokenize 零次或多次分词 字符串然后 xDelete 释放 xCreate 分配的任何资源。更多 具体说来
x创建
此函数用于分配和初始化分词器实例。 需要一个分词器实例来实际对文本进行分词化。
传递给此函数的第一个参数是 void* 的副本 fts5_tokenizer对象时应用程序提供的指针 已向 FTS5 注册xCreateTokenizer 的第三个参数。 第二个和第三个参数是以 nul 结尾的字符串数组 包含分词器参数如果有在 分词器名称作为使用的 CREATE VIRTUAL TABLE 语句的一部分 创建 FTS5 表。
最后一个参数是输出变量。如果成功*ppOut 应设置为指向新的分词器句柄并SQLITE_OK 返回。如果发生错误则应SQLITE_OK以外的某些值 被退回。在本例中fts5 假设 *ppOut 的最终值 未定义。 x删除
调用此函数以删除先前的分词器句柄 使用 xCreate 分配。Fts5 保证此函数将 每次成功调用 xCreate 时只调用一次。 xTokenize
此函数应标记指示的 nText 字节字符串 通过参数 pText。pText 可能是 null 结尾的也可能不是 nul 结尾的。第一个 传递给此函数的参数是指向 Fts5Tokenizer 对象的指针 由之前对 xCreate 的调用返回。
第二个参数指示 FTS5 请求的原因 所提供文本的标记化。这始终是以下情况之一 四个值 FTS5_TOKENIZE_DOCUMENT - 正在将文档插入到 或从 FTS 表中删除。正在调用分词器 确定要添加到或从中删除的标记集 富通指数。 FTS5_TOKENIZE_QUERY - 正在执行 MATCH 查询 相对于富通指数。正在调用分词器进行分词化 指定为查询的一部分的裸字或带引号的字符串。 FTS5_TOKENIZE_QUERY |FTS5_TOKENIZE_PREFIX - 与 FTS5_TOKENIZE_QUERY除了裸字或带引号的字符串是 后跟一个“*”字符表示最后一个标记 分词器返回的将被视为令牌前缀。 FTS5_TOKENIZE_AUX - 正在调用分词器 满足辅助器发出的 fts5_api.xTokenize 请求 功能。或者由相同的 fts5_api.xColumnSize 发出的请求 在 columnsize0 数据库上。
对于输入字符串中的每个标记提供的回调 xToken 必须 被调用。它的第一个参数应该是指针的副本 作为第二个参数传递给 xTokenize。第三和第四 参数是指向包含标记文本的缓冲区的指针而 令牌的大小以字节为单位。第 4 个和第 5 个参数是字节偏移量 的第一个字节和紧跟在文本后面的第一个字节 令牌是在输入中派生的。
传递给 xToken 回调的第二个参数 “tflags” 应该 通常设置为 0。例外情况是如果分词器支持 同义词。在这种情况下有关详细信息请参阅下面的讨论。
FTS5 假设 xToken 回调是针对 按它们在输入文本中出现的顺序排列。
如果 xToken 回调返回除 SQLITE_OK 以外的任何值则 应该放弃标记化而 xTokenize 方法应该 立即返回 xToken 返回值的副本。或者如果 输入缓冲区已耗尽xTokenize 应返回SQLITE_OK。最后 如果 xTokenize 实现本身发生错误则 可能会放弃标记化并返回除 SQLITE_OK或SQLITE_DONE。 7.1.1. 同义词支持
自定义分词器也可能支持同义词。考虑一个案例其中 用户希望查询诸如“第一名”之类的短语。使用 内置分词器FTS5 查询“first place”将匹配实例 在文档集中的“第一名”但不是替代形式 比如“第一名”。在某些应用程序中最好匹配 “第一名”或“第一名”的所有实例无论哪种形式 在 MATCH 查询文本中指定的用户。
在 FTS5 中有几种方法可以解决这个问题 通过将所有同义词映射到单个标记。在本例中使用 在上面的例子中这意味着分词器返回 输入“first”和“1st”的标记相同。假设令牌在 事实“第一”这样当用户插入文档“我赢了” 第一名“条目被添加到代币”i“、”won“、 “第一”和“地方”。如果用户随后查询“第 1 个 地点” 分词器将“first”替换为“1st”查询有效 不出所料。 通过查询每个查询词的所有同义词的索引 分别。在本例中在标记查询文本时 Tokenizer 可以为单个术语提供多个同义词 在文档中。然后FTS5 查询每个 单独同义词。例如面对查询 ... MATCH first place分词器提供“1st”和“first”作为 MATCH 查询中的第一个令牌FTS5 有效地运行查询 似 ... MATCH (first OR 1st) place除了出于辅助函数的目的查询 仍然似乎只包含两个短语 - “第一个或第一个” 被视为一个短语。 通过向 FTS 索引添加单个术语的多个同义词。 使用此方法在标记文档文本时标记器 为每个令牌提供多个同义词。这样当一个 诸如“我获得了第一名”之类的文档被标记化条目是 添加到 FTS 指数中的“i”、“won”、“first”、“1st”和 “地方”。 这样即使分词器不提供同义词 标记查询文本时它不应该 - 这样做会是 inefficient用户是否查询 “第一名 第一名”或“第一名 第二名”因为 FTS指数对应于第一种代币的两种形式。
无论是解析文档还是查询文本任何对 xToken 的调用都会导致 指定具有 FTS5_TOKEN_COLOCATED 位的 tflags 参数 被视为为前一个令牌提供同义词。例如 解析文档“我赢了第一名”时支持 同义词将调用 xToken 5 次如下所示 xToken(pCtx, 0, i, 1, 0, 1);
xToken(pCtx, 0, won, 3, 2, 5);
xToken(pCtx, 0, first, 5, 6, 11);
xToken(pCtx, FTS5_TOKEN_COLOCATED, 1st, 3, 6, 11);
xToken(pCtx, 0, place, 5, 12, 17);第一次指定 FTS5_TOKEN_COLOCATED 标志是错误的 xToken 被调用。可以为单个令牌指定多个同义词 通过按顺序对 xTokenFTS5_TOKEN_COLOCATED 进行多次调用。 可以提供的同义词数量没有限制 单个令牌。
在许多情况下上述方法1是最佳方法。它不会添加 额外的数据到 FTS 索引或需要 FTS5 查询多个术语 因此它在磁盘空间和查询速度方面是有效的。但是它 不能很好地支持前缀查询。如果如上所述 token “first” 被 tokenizer 替换为 “1st”然后是查询 ... MATCH 1s*将不匹配包含标记“1st”的文档作为标记器 可能不会将“1s”映射到“first”的任何前缀。
对于完整的前缀支持方法3可能是首选。在这种情况下 由于索引包含“first”和“1st”的条目因此前缀 诸如“fi*”或“1s*”之类的查询将正确匹配。但是因为 额外的条目被添加到FTS索引中此方法使用更多空间 在数据库中。
方法2提供了1和3之间的中点。使用此方法 诸如“1s*”之类的查询将匹配包含文本的文档 token “1st”但不是 “first”假设分词器无法 提供前缀的同义词。但是像“1st”这样的非前缀查询 将与“1st”和“first”匹配。此方法不需要 额外的磁盘空间因为不会向 FTS 索引添加额外的条目。 另一方面它可能需要更多的 CPU 周期来运行 MATCH 查询 因为每个同义词都需要对 FTS 索引进行单独的查询。
使用方法 2 或 3 时重要的是仅使用分词器 标记化文档文本方法 3或查询时提供同义词 text方法 2而不是两者兼而有之。这样做不会导致任何错误但会导致 低 效。 7.2. 自定义辅助函数
实现自定义辅助函数类似于实现标量 SQL 函数。实现 应是 fts5_extension_function 类型的 C 函数定义如下 typedef struct Fts5ExtensionApi Fts5ExtensionApi;
typedef struct Fts5Context Fts5Context;
typedef struct Fts5PhraseIter Fts5PhraseIter;typedef void (*fts5_extension_function)(const Fts5ExtensionApi *pApi, /* API offered by current FTS version */Fts5Context *pFts, /* First arg to pass to pApi functions */sqlite3_context *pCtx, /* Context for returning result/error */int nVal, /* Number of values in apVal[] array */sqlite3_value **apVal /* Array of trailing arguments */
);通过调用 FTS5 模块将实现注册到 fts5_api对象的 xCreateFunction 方法。如果已经有一个 辅助函数同名则替换为新函数。 如果将非 NULL xDestroy 参数传递给 xCreateFunction则调用该参数 将 pUserData 指针的副本作为唯一参数传递当 数据库句柄关闭或注册的辅助函数 取代。
如果成功xCreateFunction 返回SQLITE_OK。 否则它 返回 SQLite 错误代码。在这种情况下不会调用 xDestroy 函数。
传递给辅助函数回调的最后三个参数是 类似于传递给标量 SQL 实现的三个参数 功能。除了第一个传递给辅助函数的参数外所有参数都是 可用于 apVal[] 数组中的实现。这 实现应通过内容句柄 pCtx 返回结果或错误。
传递给辅助函数回调的第一个参数是指针 到包含可以调用的方法的结构以便获得 有关当前查询或行的信息。第二个参数是 不透明的句柄应作为第一个参数传递给任何此类方法 调用。例如以下辅助函数定义返回 当前行所有列中的标记总数 /*
** Implementation of an auxiliary function that returns the number
** of tokens in the current row (including all columns).
*/
static void column_size_imp(const Fts5ExtensionApi *pApi,Fts5Context *pFts,sqlite3_context *pCtx,int nVal,sqlite3_value **apVal
){int rc;int nToken;rc pApi-xColumnSize(pFts, -1, nToken);if( rcSQLITE_OK ){sqlite3_result_int(pCtx, nToken);}else{sqlite3_result_error_code(pCtx, rc);}
}以下部分介绍提供给辅助函数的 API 详细实现。更多的例子可以在“fts5_aux.c”中找到 源代码的文件。 7.2.1. 自定义辅助函数 API 参考 struct Fts5ExtensionApi {int iVersion; /* Currently always set to 3 */void *(*xUserData)(Fts5Context*);int (*xColumnCount)(Fts5Context*);int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);int (*xTokenize)(Fts5Context*, const char *pText, int nText, /* Text to tokenize */void *pCtx, /* Context passed to xToken() */int (*xToken)(void*, int, const char*, int, int, int) /* Callback */);int (*xPhraseCount)(Fts5Context*);int (*xPhraseSize)(Fts5Context*, int iPhrase);int (*xInstCount)(Fts5Context*, int *pnInst);int (*xInst)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff);sqlite3_int64 (*xRowid)(Fts5Context*);int (*xColumnText)(Fts5Context*, int iCol, const char **pz, int *pn);int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken);int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData,int(*)(const Fts5ExtensionApi*,Fts5Context*,void*));int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*));void *(*xGetAuxdata)(Fts5Context*, int bClear);int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);/* Below this point are iVersion3 only */int (*xQueryToken)(Fts5Context*, int iPhrase, int iToken, const char **ppToken, int *pnToken);int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};无效 **xUserData Fts5Context*
返回扩展函数所在的上下文指针的副本 注册。
int *xColumnTotalSizeFts5Context* int iCol sqlite3_int64 *pnToken
如果参数 iCol 小于零则设置输出变量 *pnToken 到 FTS5 表中的代币总数。或者如果 iCol 是 非负数但小于表中的列数返回 iCol 列中的标记总数考虑 FTS5 表。
如果参数 iCol 大于或等于列数 在表中返回SQLITE_RANGE。或者如果发生错误例如 OOM 条件或 IO 错误相应的 SQLite 错误代码为 返回。
int *xColumnCount Fts5Context*
返回表中的列数。
int *xColumnSizeFts5Context* int iCol int *pnToken
如果参数 iCol 小于零则设置输出变量 *pnToken 到当前行中的令牌总数。或者如果 iCol 是 非负数但小于表中的列数设置 *pnToken 设置为当前行的 iCol 列中的令牌数。
如果参数 iCol 大于或等于列数 在表中返回SQLITE_RANGE。或者如果发生错误例如 OOM 条件或 IO 错误相应的 SQLite 错误代码为 返回。
如果与 FTS5 表一起使用此函数可能效率很低 使用“columnsize0”选项创建。
int *xColumnTextFts5Context* int iCol const char **pz int *pn
如果参数 iCol 小于零或大于或等于 返回表中的列数SQLITE_RANGE。
否则此函数将尝试检索 iCol 列的文本 当前文档。如果成功则 *pz 设置为指向缓冲区 包含 UTF-8 编码的文本*pn 设置为以字节为单位的大小 不是字符并返回SQLITE_OK。否则 如果发生错误则返回 SQLite 错误代码和最终值 of *pz 和 *pn 未定义。
int *xPhraseCountFts5Context*
返回当前查询表达式中的短语数。
int *xPhraseSizeFts5Context* int iPhrase
如果参数 iCol 小于零或大于或等于 当前查询中的短语数由 xPhraseCount 返回 返回 0。否则此函数返回 短语 i查询的短语。短语从零开始编号。
int *xInstCount Fts5Context* int *pnInst
将 *pnInst 设置为 * 中所有短语的出现总数 当前行中的查询。如果成功则返回SQLITE_OK或者 如果发生错误则为错误代码即 SQLITE_NOMEM。
如果与使用 “detailnone”或“detailcolumn”选项。如果创建了 FTS5 表 使用“detailnone”或“detailcolumn”和“content”选项 即如果它是一个无内容的表那么这个 API 总是返回 0。
int *xInstFts5Context* int iIdx int *piPhrase int *piCol int *piOff
查询当前行中短语匹配 iIdx 的详细信息。 短语匹配项从零开始编号因此 iIdx 参数 应大于或等于零且小于该值 xInstCount 输出。如果 iIdx 小于零或大于 或等于 xInstCount 返回的值SQLITE_RANGE返回。
否则输出参数 *piPhrase 设置为短语编号 *piCol 到它出现的列和 *piOff 的标记偏移量 短语的第一个标记。如果成功则返回SQLITE_OK或者返回 如果发生错误则为错误代码即 SQLITE_NOMEM。
如果与使用 “detailnone”或“detailcolumn”选项。
sqlite3_int64 *xRowidFts5Context*
返回当前行的 rowid。
int *xTokenizeFts5Context* 常量字符 *pText int nText 无效 *pCtx int *xTokenvoid* int const char* int int int )
使用属于 FTS5 表的分词器对文本进行分词化。
int *xQueryPhraseFts5Context* int iPhrase void *pUserData int*const Fts5ExtensionApi*Fts5Context*void* )
该接口用于查询 FTS 表中的短语 iPhrase 当前查询。具体而言相当于以下内容的查询 ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid将 $p 设置为与短语 iPhrase 等效的短语 执行当前查询。适用于的任何列筛选器 当前查询的短语 iPhrase 包含在$p中。对于每个 行访问回调函数作为第四个参数传递 被调用。传递给回调的上下文和 API 对象 函数可用于访问每个匹配行的属性。 调用 Api.xUserData 将返回传递为 pUserData 的第三个参数。
如果参数 iPhrase 小于零或大于或等于 查询中的短语数由 xPhraseCount 返回 此函数返回SQLITE_RANGE。
如果回调函数返回除 SQLITE_OK 以外的任何值则 查询被放弃xQueryPhrase 函数立即返回。 如果返回的值为 SQLITE_DONE则 xQueryPhrase 返回SQLITE_OK。 否则错误代码将向上传播。
如果查询运行完成且没有发生任何事件则返回SQLITE_OK。 或者如果在查询完成之前发生某些错误或被 回调时返回SQLite错误码。
int *xSetAuxdataFts5Context* void *pAux void*xDeletevoid*
将作为第二个参数传递的指针另存为扩展函数的 “辅助数据”。然后指针可以被当前或任何 将来调用作为 使用 xGetAuxdata API 的相同 MATCH 查询。
每个扩展功能都分配了一个辅助数据槽用于 每个 FTS 查询MATCH 表达式。如果调用扩展函数 对于单个 FTS 查询则所有调用共享一个 单个辅助数据上下文。
如果此函数时已经有辅助数据指针 调用则将其替换为新指针。如果 xDelete 回调 与原始指针一起指定此时调用它 点。
xDelete 回调如果指定了 xDelete 回调也会在 FTS5 查询完成后的辅助数据指针。
如果此函数中发生错误例如 OOM 条件 辅助数据设置为 NULL 并返回错误代码。如果 xDelete 参数不是 NULL而是在辅助数据上调用 指针然后返回。
void **xGetAuxdataFts5Context* int bClear
返回 fts5 扩展的当前辅助数据指针 功能。有关详细信息请参阅 xSetAuxdata 方法。
如果 bClear 参数不为零则清除辅助数据 设置为 NULL然后此函数返回。在本例中xDelete 如果有则不调用。
int *xRowCountFts5Context* sqlite3_int64 *pnRow
此函数用于检索表中的总行数。 换言之将返回的值与以下方法相同 SELECT count(*) FROM ftstable;int *xPhraseFirstFts5Context* int iPhrase Fts5PhraseIter* int* int*
此函数与类型 Fts5PhraseIter 和 xPhraseNext 一起使用 方法循环访问 当前行。这与通过 xInstCount/xInst API。而 xInstCount/xInst API 更方便 在某些情况下此 API 可能会更快。迭代 通过短语 iPhrase 的实例使用以下代码 Fts5PhraseIter iter;
int iCol, iOff;
for(pApi-xPhraseFirst(pFts, iPhrase, iter, iCol, iOff);iCol0;pApi-xPhraseNext(pFts, iter, iCol, iOff)
){// An instance of phrase iPhrase at offset iOff of column iCol
}上面定义了 Fts5PhraseIter 结构。应用程序不应 直接修改此结构 - 它只能如上所示使用 使用 xPhraseFirst 和 xPhraseNext API 方法以及 xPhraseFirstColumn 和 xPhraseNextColumn如下图所示。
如果与使用 “detailnone”或“detailcolumn”选项。如果创建了 FTS5 表 使用“detailnone”或“detailcolumn”和“content”选项 即如果它是一个无内容的表那么这个 API 总是迭代 通过一个空集所有对 xPhraseFirst 的调用将 iCol 设置为 -1。
void *xPhraseNextFts5Context* Fts5PhraseIter* int *piCol int *piOff
请参阅上面的 xPhraseFirst。
int *xPhraseFirstColumnFts5Context* int iPhrase Fts5PhraseIter* int*
此函数和 xPhraseNextColumn 类似于 xPhraseFirst 和上面描述的 xPhraseNext API。不同之处在于相反 遍历当前行中短语的所有实例这些 API 用于循环访问当前行中的列集 包含指定短语的一个或多个实例。例如 Fts5PhraseIter iter;
int iCol;
for(pApi-xPhraseFirstColumn(pFts, iPhrase, iter, iCol);iCol0;pApi-xPhraseNextColumn(pFts, iter, iCol)
){// Column iCol contains at least one instance of phrase iPhrase
}如果与使用 “detailnone”选项。如果 FTS5 表是使用 “detailnone” “content” 选项即如果它是一个无内容的表 那么这个 API 总是遍历一个空集所有对 xPhraseFirstColumn 将 iCol 设置为 -1。
使用此 API 及其配套工具访问的信息 xPhraseFirstColumn 也可以使用 xPhraseFirst/xPhraseNext 获得 或 xInst/xInstCount。这个 API 的主要优点是它是 与这些替代品一起使用时效率明显高于这些替代品 “detailcolumn”表。
void *xPhraseNextColumnFts5Context* Fts5PhraseIter* int *piCol
请参阅上面的 xPhraseFirstColumn。
int *xQueryTokenFts5Context* int iPhrase int iToken const char **ppToken int *pnToken )
这用于访问当前短语 iPhrase 的令牌 iToken 查询。返回前输出参数 *ppToken 设置为 point 添加到包含请求令牌的缓冲区并将 *pnToken 添加到 此缓冲区的大小以字节为单位。
如果 iPhrase 或 iToken 小于零或者 iPhrase 大于 或等于 查询中报告的短语数 xPhraseCount或者如果 iToken 等于或大于 短语 SQLITE_RANGE 中的 tokens 返回并且返回 *ppToken 和 *pnToken 均归零。
输出文本不是指定 令 牌。它是分词器模块的输出。对于 tokendata1 表这包括任何嵌入的0x00和尾随数据。
int *xInstTokenFts5Context* int iIdx int iToken const char** int*
这用于访问短语命中 iIdx 中的令牌 iToken 当前行。如果 iIdx 小于零或大于或等于 xInstCount 返回的值SQLITE_RANGE返回。否则 输出变量 *ppToken 设置为指向包含 将文档令牌和 *pnToken 匹配到该缓冲区的大小 字节。如果指定的令牌与 前缀查询词。在这种情况下两个输出变量始终处于设置状态 更改为 0。
输出文本不是标记化的文档文本的副本。 它是分词器模块的输出。对于 tokendata1 表这 包括任何嵌入的0x00和尾随数据。
如果与使用 “detailnone”或“detailcolumn”选项。 8. fts5vocab 虚拟表模块
fts5vocab 虚拟表模块允许用户从 直接的 FTS5 全文索引。fts5vocab 模块是 FTS5 的一部分 - 它 只要 FTS5 可用。
每个 fts5vocab 表都与一个 FTS5 表相关联。一个 fts5vocab 表通常是通过指定两个参数来代替列名来创建的 在 CREATE VIRTUAL TABLE 语句中 - 关联的 FTS5 表的名称 以及 fts5vocab 表的类型。目前有三种类型的 fts5vocab 桌子;“row”、“col”和“instance”。除非创建了 fts5vocab 表 在“temp”数据库中它必须与 关联的 FTS5 表。 -- Create an fts5vocab row table to query the full-text index belonging
-- to FTS5 table ft1.
CREATE VIRTUAL TABLE ft1_v USING fts5vocab(ft1, row);-- Create an fts5vocab col table to query the full-text index belonging
-- to FTS5 table ft2.
CREATE VIRTUAL TABLE ft2_v USING fts5vocab(ft2, col);-- Create an fts5vocab instance table to query the full-text index
-- belonging to FTS5 table ft3.
CREATE VIRTUAL TABLE ft3_v USING fts5vocab(ft3, instance); 如果在临时数据库中创建了 fts5vocab 表则该表可能是关联的 在任何附加数据库中具有 FTS5 表。为了附加 fts5vocab 表到位于“temp”以外的数据库中的 FTS5 表则 database 插入到 CREATE VIRTUAL TABLE 的 FTS5 表名之前 参数。例如 -- Create an fts5vocab row table to query the full-text index belonging
-- to FTS5 table ft1 in database main.
CREATE VIRTUAL TABLE temp.ft1_v USING fts5vocab(main, ft1, row);-- Create an fts5vocab col table to query the full-text index belonging
-- to FTS5 table ft2 in attached database aux.
CREATE VIRTUAL TABLE temp.ft2_v USING fts5vocab(aux, ft2, col);-- Create an fts5vocab instance table to query the full-text index
-- belonging to FTS5 table ft3 in attached database other.
CREATE VIRTUAL TABLE temp.ft2_v USING fts5vocab(aux, ft3, instance); 在任何数据库中创建 fts5vocab 表时指定三个参数 除“temp”外还会导致错误。
“row”类型的 fts5vocab 表包含每个不同术语的一行 在关联的 FTS5 表中。表列如下所示
列内容术语存储在 FTS5 索引中的术语。医生包含至少一个术语实例的行数。碳纳米管整个 FTS5 表中术语的实例总数。
“col”类型的 fts5vocab 表包含每个不同术语/列的一行 组合在关联的 FTS5 表中。表列如下所示
列内容术语存储在 FTS5 索引中的术语。山坳包含术语的 FTS5 表列的名称。医生FTS5 表中$col列的行数 包含该术语的至少一个实例。碳纳米管出现在 FTS5 表的第 $col 列考虑所有行。
“instance”类型的 fts5vocab 表包含每个术语的一行 实例存储在关联的 FTS 索引中。假设 FTS5 表是 在将“detail”选项设置为“full”的情况下创建时表列如下所示
列内容术语存储在 FTS5 索引中的术语。医生包含术语实例的文档的 rowid。山坳包含术语 instance 的列的名称。抵消术语实例在其列中的索引。条款 按出现顺序从 0 开始编号。
如果创建 FTS5 表时将“detail”选项设置为“col”则 实例虚拟表的偏移量列始终包含 NULL。 在本例中表中每个唯一的术语/doc/col 都有一行 组合。或者如果创建 FTS5 表时将 detail 设置为 none 则 offset 和 col 始终包含 NULL 值。为 detailnone FTS5 表则每个 fts5vocab 表中都有一行 唯一的术语/文档组合。
例 -- Assuming a database created using:
CREATE VIRTUAL TABLE ft1 USING fts5(c1, c2);
INSERT INTO ft1 VALUES(apple banana cherry, banana banana cherry);
INSERT INTO ft1 VALUES(cherry cherry cherry, date date date);-- Then querying the following fts5vocab table (type col) returns:
--
-- apple | c1 | 1 | 1
-- banana | c1 | 1 | 1
-- banana | c2 | 1 | 2
-- cherry | c1 | 2 | 4
-- cherry | c2 | 1 | 1
-- date | c3 | 1 | 3
--
CREATE VIRTUAL TABLE ft1_v_col USING fts5vocab(ft1, col);-- Querying an fts5vocab table of type row returns:
--
-- apple | 1 | 1
-- banana | 1 | 3
-- cherry | 2 | 5
-- date | 1 | 3
--
CREATE VIRTUAL TABLE ft1_v_row USING fts5vocab(ft1, row);-- And, for type instance
INSERT INTO ft1 VALUES(apple banana cherry, banana banana cherry);
INSERT INTO ft1 VALUES(cherry cherry cherry, date date date);
--
-- apple | 1 | c1 | 0
-- banana | 1 | c1 | 1
-- banana | 1 | c2 | 0
-- banana | 1 | c2 | 1
-- cherry | 1 | c1 | 2
-- cherry | 1 | c2 | 2
-- cherry | 2 | c1 | 0
-- cherry | 2 | c1 | 1
-- cherry | 2 | c1 | 2
-- date | 2 | c2 | 0
-- date | 2 | c2 | 1
-- date | 2 | c2 | 2
--
CREATE VIRTUAL TABLE ft1_v_instance USING fts5vocab(ft1, instance); 9. FTS5 数据结构
本节简要介绍了 FTS 模块存储其 数据库中的索引和内容。没有必要阅读或理解 本节中的材料以便在应用程序中使用 FTS。但是它 对于尝试分析和理解的应用程序开发人员可能有用 FTS 性能特征或考虑增强 现有的 FTS 功能集。
在数据库中创建 FTS5 虚拟表时介于 3 到 5 个真实 在数据库中创建表。这些被称为“影子表”以及 由虚拟表模块用于存储持久性数据。他们不应该 由用户直接访问。许多其他虚拟表模块包括 FTS3 和 rtree也会创建和使用影子表。
FTS5 创建以下影子表。在每种情况下实际的表名 基于 FTS5 虚拟表的名称在下文中将 % 替换为虚拟表的名称以查找实际的影子表名称。 -- This table contains most of the full-text index data.
CREATE TABLE %_data(id INTEGER PRIMARY KEY, block BLOB);-- This table contains the remainder of the full-text index data.
-- It is almost always much smaller than the %_data table.
CREATE TABLE %_idx(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID;-- Contains the values of persistent configuration parameters.
CREATE TABLE %_config(k PRIMARY KEY, v) WITHOUT ROWID;-- Contains the size of each column of each row in the virtual table
-- in tokens. This shadow table is not present if the columnsize
-- option is set to 0.
CREATE TABLE %_docsize(id INTEGER PRIMARY KEY, sz BLOB);-- Contains the actual data inserted into the FTS5 table. There
-- is one cN column for each indexed column in the FTS5 table.
-- This shadow table is not present for contentless or external
-- content FTS5 tables.
CREATE TABLE %_content(id INTEGER PRIMARY KEY, c0, c1...); 以下各节更详细地介绍了这五个表是如何实现的 用于存储 FTS5 数据。
9.1. Varint格式
以下各节引用以“varint”形式存储的 64 位有符号整数。 FTS5 使用与 SQLite 核心在不同地方使用的相同的变体格式。
变量的长度介于 1 到 9 个字节之间。变量由以下任一部分组成 零个或多个字节其中设置了高阶位后跟一个字节 高阶位为clear或9个字节以较短者为准。下层 前 8 个字节中每个字节的 7 位和第 9 个字节的所有 8 位 用于重建 64 位二进制补码整数。Varints 是 big-endian从变量的早期字节中获取的位更重要 比从后面的字节中获取的位。
9.2. FTS 指数%_idx 和 %_data 表
FTS 索引是一个有序的键值存储其中键是文档 术语或术语前缀以及关联的值是“doclists”。文档列表是一个 压缩的 varint 数组用于编码术语的每个实例的位置 在 FTS5 表中。单个项实例的位置定义为 组合
它出现在的 FTS5 表行的 rowid术语实例所在的列的索引列是 从零开始从左到右编号以及列值内项的偏移量即 在此值之前的列值中显示的标记。
FTS 索引包含 数据集其中 nPrefix 是定义的前缀索引的数量。
与主 FTS 索引不是前缀的索引关联的键 index 以字符“0”为前缀。第一个前缀的键 索引以“1”为前缀。第二个前缀索引的键是 以“2”为前缀依此类推。例如如果令牌“document”是 插入到 FTS5 表中其前缀索引由 prefix“2 4” 指定则添加到 FTS 索引的键将是 “0document”、“1do”和“2docu”。
FTS 索引条目不存储在单个树或哈希表中 结构。相反它们存储在一系列不可变的 b 树中例如 结构称为“段 B 树”。每次写入 FTS5 表被提交一个或多个但通常只有一个新段 b 树 添加包含任何已删除的新条目和逻辑删除 条目。查询 FTS 索引时读取器会查询每个段 b-tree 依次合并结果优先考虑较新的数据。
每个段 b 树都分配了一个数值级别。当新区段 b-tree 作为提交事务的一部分写入数据库 它被分配到级别 0。属于单个级别的段 b 树是 定期合并在一起以创建单个更大的段 B 树 分配给下一个级别即 0 级段 B 树是 合并为单个 1 级段 B 树。因此从数字上讲 较大的级别在通常较大的段 B 树中包含较旧的数据。 请参阅“自动合并”、“危机合并”和“用户合并”选项以及 使用“merge”和“optimize”命令详细了解如何 控制合并。
如果与术语或术语前缀关联的文档列表非常 大可能有关联的文档列表 索引。doclist 索引类似于 b 树的内部节点集。 它允许大型文档列表有效地查询 rowid 或范围 吵吵嚷嚷。例如在处理类似以下查询时 SELECT ... FROM fts_table(term) WHERE rowid BETWEEN ? AND ?FTS5 使用段 b 树索引来查找术语“term”的文档列表 然后使用其 doclist 索引假设它存在来有效地识别 与所需范围内的 rowid 匹配的子集。
9.2.1. %_data 表 Rowid 空间 CREATE TABLE %_data(id INTEGER PRIMARY KEY,block BLOB
); %_data 表用于存储三种类型的记录
特殊结构记录 以 id1 存储。特殊平均记录 以 id10 存储。用于存储每个段 b 树叶和 doclist 索引叶的记录以及 内部节点。请参阅下文了解如何计算这些 id 值 记录。
系统中的每个段 b 树都分配有一个唯一的 16 位段 ID。 只有在原始所有者段 b 树 完全合并到更高级别的段 B 树中。在段 b 树中 每个叶页都分配了一个唯一的页码 - 第一个叶页为 1,2 第二个依此类推。
每个文档列表索引叶页也分配了一个页码。第一个 文档列表索引中的最左边叶页分配的页码与 显示其术语的段 B 树叶页因为 Doclist 索引 仅为具有很长文档列表的术语创建每个文档列表最多一个术语 段 B 树叶具有关联的 Doclist 索引。将此页码称为 P。 如果文档列表太大需要第二片叶子则第二片叶子是 分配的页码 P1。第三片叶子 P2。文档列表索引的每一层 b-tree叶子、叶子的父母、祖父母等被分配了页码 以这种方式从页码 P 开始。
%_data 表中用于存储任何给定段 b 树的“id”值 leaf 或 doclist 索引 leaf 或 node 的组成如下
罗维德位内容38..4316 位段 b 树 id 值。371 位Doclist 索引标志。设置为文档列表索引页清除 对于段 B 树叶。32..365 位树中的高度。对于段 b 树此值设置为 0 和 doclist 索引叶子为 1 表示 doclist 的父母 索引叶2 个给祖父母等。0..3132 位页码
9.2.2. 结构记录格式
结构记录标识组成 当前的 FTS 指数以及任何正在进行的增量合并的详细信息 操作。它存储在 id10 的 %_data 表中。 结构记录以单个 32 位无符号值 - cookie 开头 价值。每次修改结构时此值都会递增。 cookie 值后面是三个变量值如下所示
索引中的级别数即关联的最大级别 与任何段 b 树加 1。索引中段 b 树的总数。写入级别 0 树的段 b 树叶总数 自 FTS5 表创建以来。
然后对于从 0 到 nLevel 的每个级别
用作 当前增量合并的输入如果没有 正在进行的增量合并以为此级别创建新的段 B 树。关卡上段 b 树的总数。然后对于每个段 b 树从最旧到最新 段 ID。第一页的页码通常为 1始终为 0。最后一页的页码始终为 0。
9.2.3. 平均记录格式
平均值记录始终以 id1 存储在 %_data 表中 不存储任何内容的平均值。相反它包含的向量 nCol1 填充变量值其中 nCol 是 FTS5 中的列数 表包括未编制索引的列。第一个变量包含总数 FTS5 表中的行数。第二个包含 存储在最左侧的 FTS5 表列中的所有值中的令牌。第三个 下一个最左边的所有值中的标记数依此类推。的值 未编制索引的列始终为零。
9.2.4. 段B树格式
9.2.4.1. key/doclist 格式
key/doclist 格式是一种用于存储一系列键document 术语或术语前缀以单个字符为前缀用于标识特定的 它们所属的索引按排序顺序排列每个索引都有其关联的索引 文档列表。该格式由交替键和打包在一起的文档列表组成。
第一个密钥存储为
指示键 N 中字节数的变量后跟密钥数据本身N 字节。
每个后续密钥存储为
一个变量指示键具有共同的前缀的大小 使用前一个键以字节为单位一个变量指示键中的字节数跟在 通用前缀 N后跟键后缀数据本身N 个字节。
例如如果 FTS5 密钥/文档列表记录中的前两个密钥是 “0challenger”和“0chandelier”则第一个键存储为 varint 11 后跟 11 个字节“0challenger”第二个密钥存储为 varints 4 和 7后跟 7 个字节“ndelier”。
文档列表 0文档列表 1键/文档列表 2...键 0 数据密钥 0 大小 varint密钥 1 前缀大小 varint密钥 1 后缀大小 varint键 1 前缀数据 图1 - 术语/文档列表格式
每个文档列表通过其 rowid 值标识包含 术语或术语前缀的至少一个实例以及关联的位置列表 或“poslist”枚举行中每个术语实例的位置。在 从这个意义上说“位置”被定义为列号和项偏移量 列值。
在文档列表中文档始终按 rowid 排序的顺序存储。这 文档列表中的第一个 rowid 按原样存储为 varint。它是立即的 后跟其关联的位置列表。在此之后区别 在第一个 rowid 和第二个 rowid 之间作为 varint然后是 doclist 与 DocList 中的第二个 rowid 相关联。等等。
无法通过解析文档列表来确定文档列表的大小。这必须 存储在外部。请参阅以下部分 如何在 FTS5 中实现这一点的详细信息。
职位列表 0职位列表 1职位列表 2...rowid 0 varintrowid 1delta 编码的 varintrowid 3delta 编码的 varint 图2 - 文档列表格式
位置列表通常缩写为“poslist”标识列 以及相关令牌的每个实例的行内的令牌偏移量。 poslist 的格式为
Varint 设置为 poslist 大小的两倍不包括此字段 如果在条目上设置了“删除”标志则加一。第 0 列最左边的列的偏移量列表可能是空的 行。每个偏移量都存储为 varint。第一个变量包含 第一个偏移量的值加上 2。第二个变体包含 第二次和第一次偏移量之间的差值加上 2。等。为 例如如果偏移量列表要包含偏移量 0、10、15 和 16则 通过打包以下值进行编码编码为 varints结尾为 结束 2, 12, 7, 3对于包含多个实例之一的列 0 以外的每列 令牌的 字节值 0x01。列号作为 varint。偏移量列表其格式与列 0 的偏移量列表相同。
col 0 偏移列表0x01col i offset-listnSize*2 bDel varint列号 inSize 字节 图3 - 位置列表poslist在列0和i中具有偏移量
9.2.4.2. 分页
如果它足够小默认情况下这意味着小于 4000 字节则 段 B 树的全部内容可以以 key/doclist 格式存储 在上一节中描述为 %_data 表中的单个 Blob。 否则key/doclist 将拆分为多个页面默认情况下大约 每个字节 4000 字节并存储在 %_data 表中的一组连续条目中 详见上文。
当将密钥/文档列表划分为多个页面时以下修改是 按照格式制作
单个变量或键数据字段永远不会跨越两个页面。每个页面上的第一个键不是前缀压缩的。它存储在 上面描述的文档列表第一个键的格式 - 其大小为 后跟关键数据的变量。如果页面上的第一个键之前有一个或多个 rowid则 其中第一个不是 Delta 压缩的。它按原样存储就像 如果它是其文档列表的第一个 rowid它可能是也可能不是。
每个页面还具有固定大小的 4 字节页眉和可变大小的页脚。 标头分为 2 个 16 位 big-endian 整数字段。他们 包含
页面上第一个 rowid 值的字节偏移量如果出现 在第一个键之前否则为 0。页脚的字节偏移量。
页脚由一系列包含字节偏移量的变量组成 页面上显示的每个键。页脚的大小为零字节 如果页面上没有键。
高光辐射修改后的 key/doclist 数据页脚4 字节可变尺寸 图4 - 页面格式
9.2.4.3. 段索引格式
格式化段 b 树的内容的结果 key/doclist 格式然后将其拆分为页面是非常非常的事情 类似于 B树的叶子。而不是创建格式 此 btree 的内部节点并将它们存储在 %_data 表中 除了叶子之外存储在这些节点上的密钥是 添加到 %_idx 表中定义为 CREATE TABLE %_idx(segid INTEGER, -- segment idterm TEXT, -- prefix of first key on pagepgno INTEGER, -- (2*pgno bDoclistIndex)PRIMARY KEY(segid, term)
); 对于每个包含至少一个键的“叶子”页面都会添加一个条目 添加到 %_idx 表中。字段设置如下
列内容赛吉德整数段 id。术语页面上第一个键的最小前缀 大于上一页上的所有键。对于 段中的第一页此前缀为 0 个字节 大小。PGNO的此字段对页码进行编码在 segment - 从 1 和 doclist 索引标志开始。 如果 页面具有关联的 文档列表索引。此字段的值为 (pgno*2 bDoclistIndexFlag)然后找到段 i 的叶子该叶子可能包含术语 t而不是 通过内部节点进行搜索FTS5 运行查询 SELECT pgno FROM %_idx WHERE segid$i AND term$t ORDER BY term LIMIT 1 9.2.4.4. Doclist 索引格式
上文中描述的区段索引 部分允许按术语或 假设存在所需大小的前缀索引则为术语前缀。数据 本节中描述的结构 doclist 索引允许 FTS5 在关联的文档列表中有效地搜索 rowid 或范围或 rowids 具有单个术语或术语前缀。
并非所有键都有关联的文档列表索引。默认情况下文档列表索引 仅当 Doclist 跨越 4 段 B 树叶时才会为键添加 页面。Doclist 索引本身就是 b 树既有叶子也有内部 节点存储为 %_data 表中的条目但实际上大多数文档列表是 小到可以放在一片叶子上。FTS5 对 doclist 使用相同的粗略大小 索引节点和叶子就像对段 B 树叶子一样默认为 4000 字节。
文档列表索引叶和内部节点使用相同的页面格式。第一个 byte 是一个“flags”字节。对于文档列表的根页面此设置为 0x00 索引 b 树并0x01所有其他页面。页面的其余部分是一个 一系列紧密包装的变质如下所示
最左边子页的页码后跟最左侧子页面上的最小 rowid 值后跟每个后续子页面一个变量包含以下值 0x00 如果子页面上没有 rowid这只会发生 当“子”页面实际上是段 B 树叶时或者子页面上最小的 rowid 与 存储在 DocList 索引页上的上一个 rowid 值。
对于 doclist 索引中最左边的 doclist 索引叶最左边的子项 page 是包含键的叶子之后的第一个段 b 树叶 本身。
9.3. 文档大小表%_docsize 表 CREATE TABLE %_docsize(id INTEGER PRIMARY KEY, -- id of FTS5 row this record pertains tosz BLOB -- blob containing nCol packed varints
); 许多常见的搜索结果排名函数都需要输入大小 在结果文档的标记中因为在短文档中搜索词的命中是 被认为比长文档中的一个更重要。提供快速 访问此信息对于 FTS5 表中的每一行都存在一个 %_docsize 影子表中的相应记录具有相同的 rowID 它包含行中每个列值的大小以标记为单位。
列值大小存储在包含一个打包变量的 blob 中用于 FTS5 表的每一列从左到右。变量包含 当然对应列值中的令牌总数。未编入索引 列包含在此 varint 向量中;对他们来说价值永远是 设置为零。
此表由 xColumnSize API 使用。它可以 通过指定 columnsize0 选项完全省略。在这种情况下 xColumnSize API 仍可用于辅助函数但运行更多 慢慢。
9.4. 表格内容 %_content 表 CREATE TABLE %_content(id INTEGER PRIMARY KEY, c0, c1...);实际表内容 - 插入到 FTS5 表中的值为 存储在 %_content 表中。此表由一个“c*”列创建用于 FTS5 表的每一列包括任何未编制索引的列。的值 最左边的 FTS5 表列存储在 %_content 的 “c0” 列中 表“c1”列中下一个 FTS5 表列的值依此类推。
对于外部内容或无内容的 FTS5 表将完全省略此表。 表。
9.5. 配置选项%_config表 CREATE TABLE %_config(k PRIMARY KEY, v) WITHOUT ROWID;此表存储任何持久性配置选项的值。 列“k”存储选项的名称文本列“v”存储值。 示例内容 sqlite SELECT * FROM fts_tbl_config;
┌─────────────┬──────┐
│ k │ v │
├─────────────┼──────┤
│ crisismerge │ 8 │
│ pgsz │ 8000 │
│ usermerge │ 4 │
│ version │ 4 │
└─────────────┴──────┘附录A与FTS3/4的比较
此外还有类似但更成熟的 FTS3/4 模块。 FTS5 是 FTS4 的新版本包括各种修复和解决方案 在不向后牺牲的情况下无法在 FTS4 中修复的问题 兼容性。下面将介绍其中一些问题。
应用程序移植指南
为了使用 FTS5 而不是 FTS3 或 FTS4应用程序通常需要 最小的修改。其中大多数分为三类 - 变化 用于创建 FTS 表的 CREATE VIRTUAL TABLE 语句是必需的 对用于对表执行查询的 SELECT 查询所需的更改 以及对使用 FTS 辅助功能的应用程序所需的更改。
对 CREATE VIRTUAL TABLE 语句的更改 模块名称必须从“fts3”或“fts4”更改为“fts5”。 必须从中删除所有类型信息或约束规范 列定义。FTS3/4 忽略列名后面的所有内容 列定义FTS5 尝试解析它并将报告错误 如果失败。 “matchinfofts3”选项不可用。“columnsize0”选项是等效的。 notindexed 选项不可用。将 UNINDEXED 添加到列定义是等效的。 ICU 分词器不可用。 compress、uncompress 和 languageid 选项不可用。 到目前为止它们的功能还没有等效的。 -- FTS3/4 statement
CREATE VIRTUAL TABLE t1 USING fts4(linkid INTEGER,header CHAR(20),text VARCHAR,notindexedlinkid,matchinfofts3,tokenizerunicode61
);-- FTS5 equivalent (note - the tokenizerunicode61 option is not-- required as this is the default for FTS5 anyway)
CREATE VIRTUAL TABLE t1 USING fts5(linkid UNINDEXED,header,text,columnsize0
); 对 SELECT 语句的更改 “docid”别名不存在。应用程序必须使用“rowid” 相反。 将列筛选器指定为 FTS 查询的一部分并使用列作为 MATCH 的 LHS 运算符略有不同。对于包含列“a”和“b”的表 以及类似于以下内容的查询 ... a MATCH b: stringFTS3/4 在“b”列中搜索匹配项。但是FTS5 始终 返回零行因为首先对结果进行“B”列筛选然后 对于“A”列不留下任何结果。换句话说在 FTS3/4 中 内部过滤器覆盖外部过滤器在 FTS5 中应用两个过滤器。 FTS 查询语法MATCH 运算符的右侧具有 在某些方面发生了变化。FTS5 语法与 FTS4 非常接近 “增强语法”。主要区别在于 FTS5 更挑剔 关于查询中无法识别的标点符号和类似字符 字符串。大多数使用 FTS3/4 的查询也应该使用 FTS5 和那些不返回解析错误的人应该返回解析错误。
辅助功能变更
FTS5 没有 matchinfo 或 offsets 函数而 snippet 函数 不像 FTS3/4 那样功能齐全。但是由于 FTS5 确实提供 一个 API允许应用程序创建自定义辅助函数任何 所需的功能可以在应用程序代码中实现。
FTS5提供的内置辅助功能集可以是 将来会有所改进。
其他问题 现在提供了 fts4aux 模块提供的功能 由 fts5vocab 提供。这两个表的架构略有不同。 FTS3/4 “mergeXY” 命令已替换为 FTS5 merge 命令。 FTS3/4 “automergeX” 命令已替换为 FTS5 自动合并选项。 技术差异总结
FTS5 与 FTS3/4 相似因为两者的主要任务是维护 从每个唯一令牌到该令牌实例列表的索引映射 在一组文档中其中每个实例都由文档标识 它出现在其中及其在该文档中的位置。例如 -- Given the following SQL:
CREATE VIRTUAL TABLE ft USING fts5(a, b);
INSERT INTO ft(rowid, a, b) VALUES(1, X Y, Y Z);
INSERT INTO ft(rowid, a, b) VALUES(2, A Z, Y Y);-- The FTS5 module creates the following mapping on disk:
A -- (2, 0, 0)
X -- (1, 0, 0)
Y -- (1, 0, 1) (1, 1, 0) (2, 1, 0) (2, 1, 1)
Z -- (1, 1, 1) (2, 0, 1) 在上面的示例中每个三元组标识令牌的位置 实例按 rowid、列号列按顺序编号 从 0 开始从左到右和列值中的位置 列值中的第一个标记为 0第二个标记为 1依此类推。使用这个 indexFTS5 能够及时回答诸如“集合 包含标记A的所有文档“或”所有文档的集合” 包含序列Y Z“。与 单个令牌称为“实例列表”。
FTS3/4 和 FTS5 的主要区别在于 FTS3/4 每个 instance-list 都存储为单个大型数据库记录而 在 FTS5 中大型实例列表被划分为多个数据库记录。 这对于处理大型数据库具有以下含义 包含大型列表 FTS5 能够将实例列表以增量方式加载到内存中 以减少内存使用量和峰值分配大小。FTS3/4 非常 通常将整个实例列表加载到内存中。 在处理具有多个令牌的查询时FTS5 是 有时能够确定查询可以通过以下方式回答 检查大型实例列表的子集。FTS3/4 几乎总是 必须遍历整个实例列表。 如果实例列表变得如此之大以至于超过 SQLITE_MAX_LENGTH限制FTS3/4 无法处理。FTS5型 没有这个问题。
由于这些原因许多复杂的查询可能使用更少的内存并运行得更快 使用 FTS5。
FTS5 与 FTS3/4 的其他一些不同之处在于 FTS5 支持“ORDER BY rank”按 相关性降低。 FTS5 具有一个 API允许用户创建自定义辅助设备 用于高级排名和文本处理应用程序的函数。这 特殊的“rank”列可以映射到自定义辅助函数 因此将“ORDER BY rank”添加到查询中可以按预期工作。 FTS5 通过以下方式识别 unicode 分隔符和大小写等效性 违约。这也可以使用 FTS3/4 实现但必须明确 启用。 查询语法已根据需要进行修订以删除 歧义并有可能转义特殊字符 在查询术语中。 默认情况下FTS3/4 偶尔会将两个或多个 在 INSERT、UPDATE 或 用户执行的 DELETE 语句。这意味着任何操作 在 FTS3/4 表上可能会变得非常慢因为 FTS3/4 可能会不可预测地选择将两个或多个大型 B 树合并在一起 在里面。FTS5 默认使用增量合并这限制了 在任何给定范围内可能发生的处理量 INSERT、UPDATE 或 DELETE 操作。