Small. Fast. Reliable.
Choose any three.
SQLite FTS5扩展

1. FTS5概述

FTS5是一个SQLite虚拟表模块,可 为数据库应用程序提供 全文本搜索功能。全文搜索引擎以其最基本的形式允许用户有效地搜索大量文档,以查找包含一个或多个搜索项实例的子集。Google提供给万维网用户的搜索功能 尤其是全文搜索引擎,因为它允许用户搜索网络上包含例如“ fts5”一词的所有文档。

要使用FTS5,用户将创建一个包含一列或多列的FTS5虚拟表。例如:

使用fts5(发送者,标题,正文)创建虚拟表电子邮件;

在用于创建FTS5表的CREATE VIRTUAL TABLE语句中添加类型,约束或PRIMARY KEY声明是错误的。创建FTS5表后,可以像其他任何表一样使用INSERTUPDATEDELETE语句填充FTS5表。像其他任何没有PRIMARY KEY声明的表一样,FTS5表具有名为rowid的隐式INTEGER PRIMARY KEY字段。

在上面的示例中未显示的是,还有一些 选项可以作为CREATE VIRTUAL TABLE语句的一部分提供给FTS5,以配置新表的各个方面。这些可用于修改FTS5表从文档和查询中提取术语的方式,在磁盘上创建额外的索引以加快前缀查询的速度,或用于创建FTS5表作为对其他位置存储的内容的索引。

填充后,有三种方法可以对FTS5表的内容执行全文查询:

如果使用MATCH或=运算符,则MATCH运算符左侧的表达式通常是FTS5表的名称(指定column-filter时例外 )。右侧的表达式必须是指定要搜索的术语的文本值。对于表值函数语法,将要搜索的术语指定为第一个表参数。例如:

-查询至少包含术语
“ fts5”实例一次的所有行(在任何列中)。以下三个查询是等效的。
选择*从电子邮件中,电子邮件匹配为“ fts5”;
选择*从电子邮件中,电子邮件='fts5';
SELECT * FROM email('fts5');

默认情况下,FTS5全文搜索不区分大小写。像其他任何不包含ORDER BY子句的SQL查询一样,以上示例以任意顺序返回结果。为了按相关性(最不相关)对结果进行排序,可以将ORDER BY添加到全文查询中,如下所示:

-查询至少包含术语
“ fts5”
实例一次的所有行(在任何列中)。按从最佳到最差的顺序返回结果-匹配。  
选择*从电子邮件中,电子邮件匹配“ fts5”,按等级排序;

除了匹配行的列值和rowid之外,应用程序还可以使用FTS5辅助功能来检索有关匹配行的额外信息。例如,可以使用辅助功能来检索匹配行的列值的副本,其中匹配项的所有实例都被html <b> </ b>标记包围。辅助函数的调用方式与SQLite标量函数相同,只是将FTS5表的名称指定为第一个参数。例如:

-查询与“ fts5”匹配的行。返回“ body”列的副本
-每行的匹配项都被<b> </ b>标记包围。
从email('fts5')中选择Highlight(email,2,'<b>','</ b>');

可用辅助功能,和更多的细节对于特殊的“等级”栏的结构的描述,都 可用以下自定义辅助功能也可以用C实现并向FTS5注册,就像自定义SQL函数可以向SQLite核心注册一样。

除了搜索包含术语的所有行之外,FTS5还允许用户搜索包含以下内容的行:

通过提供更复杂的FTS5查询字符串作为MATCH运算符(或=运算符,或作为表值函数语法的第一个参数)右边的文本,可以请求进行此类高级搜索。完整的查询语法在此处描述

2.编译和使用FTS5

2.1。将FTS5构建为SQLite的一部分

3.9.0版(2015-10-14)开始,FTite5作为SQLite合并的一部分包含在内。如果使用两个autoconf构建系统之一,则在运行configure脚本时通过指定“ --enable-fts5”选项来启用FTS5。(对于源树配置脚本,当前默认情况下禁用FTS5,对于合并配置脚本,默认情况下启用FTS5,但是这些默认值将来可能会更改。)

或者,如果使用其他构建系统来编译sqlite3.c,则通过安排要定义的SQLITE_ENABLE_FTS5预处理器符号来进行。

2.2。构建可加载的扩展

可替代地,FTS5可以被构建为可加载的扩展。

规范的FTS5源代码由SQLite源树的“ ext / fts5”目录中的一系列* .c和其他文件组成。构建过程将其减少为仅两个文件-“ fts5.c”和“ fts5.h”-可用于构建SQLite可加载扩展。

  1. 从化石获取最新的SQLite代码。
  2. 按照如何编译SQLite中所述创建Makefile 。
  3. 构建“ fts5.c”目标。这也会创建fts5.h。
$ wget -c http://www.sqlite.org/src/tarball/SQLite-trunk.tgz?uuid=trunk -O SQLite-trunk.tgz
.... 输出 ...
$ tar -xzf SQLite-trunk.tgz
$ cd SQLite-trunk
$ ./configure &&制作fts5.c
...很多输出...
$ ls fts5。[ch]
fts5.c fts5.h

然后,可以如“编译可加载的扩展”中所述将“ fts5.c”中的代码编译成可加载的扩展或静态链接到应用程序中 。定义了两个入口点,它们都具有相同的作用:

不需要其他文件“ fts5.h”来编译FTS5扩展名。由实现自定义FTS5标记程序或辅助功能的应用程序使用。

3.全文查询语法

以下块包含BNF形式的FTS查询语法的摘要。详细说明如下。

<短语>:=字符串[*]
<短语>:= <短语> + <短语>
<neargroup>:= NEAR(<phrase> <phrase> ... [,N])
<查询>:= [[-] <colspec>:] [^] <短语>
<query>:= [[-<colspec>:] <neargroup>
<查询>:= [[-] <colspec>:](<查询>)
<查询>:= <查询> AND <查询>
<查询>:= <查询>或<查询>
<查询>:= <查询>不是<查询>
<colspec>:=列名
<colspec>:= {colname1 colname2 ...}

3.1。FTS5琴弦

在FTS表达式中,可以通过以下两种方式之一指定字符串

3.2。FTS5短语

FTS查询由短语组成。短语是一个或多个标记的有序列表。通过将字符串传递给FTS表令牌生成器,可以将其转换为短语。可以使用“ +”运算符将两个词组连接成一个大词组。例如,假设使用的令牌生成器模块将输入“ one.two.three”令牌化为三个单独的令牌,则以下四个查询都指定相同的短语:

... MATCH'“一二三”'
...匹配“一+二+三”
... MATCH'“一二” +三”
...匹配“一,二,三”

如果文档包含至少一个与构成该短语的标记序列匹配的标记子序列,则该短语与文档匹配。

3.3。FTS5前缀查询

如果FTS表达式中的字符串后面跟随有“ *”字符,则将从字符串中提取的最终标记标记为前缀标记。如您所料,前缀标记会匹配任何以其为前缀的文档标记。例如,以下块中的前两个查询将匹配包含令牌“ one”的任何文档,紧随其后的是令牌“ two”,然后是任何以“ thr”开头的令牌。

... MATCH'“一二thr” *'
...匹配“一个+两个+ thr *”
... MATCH'“一二thr *”'       -可能无法正常工作!

上面块中的最终查询可能无法按预期方式工作。由于“ *”字符位于双引号内,因此将其传递给令牌生成器,该令牌生成器可能会丢弃它(或者,取决于所使用的特定令牌生成器,将其作为最终令牌的一部分包含在内),而不是将其识别出来。作为特殊的FTS字符。

3.4。FTS5初始令牌查询

如果不属于NEAR查询的短语之前出现“ ^”字符,则仅当该短语从列中的第一个标记开始时,该短语才与文档匹配。“ ^”语法可以与列过滤器结合使用 ,但不能插入短语的中间。

... MATCH'^ one'-              任何列中的第一个标记必须为“一个” 
... MATCH'^ one + 2'-       短语“ one two”必须出现在列的开头
... MATCH'^ “ one two”'        -与先前相同
... MATCH'a:^           two'-列“ a”的第一个标记必须为“ two” 
... MATCH'NEAR(^ one,two)'    -语法错误!
...匹配'一+ ^二'         -语法错误!
... MATCH'“ ^ one two”'         -可能无法正常工作!

3.5。FTS5 NEAR查询

可以将两个或多个短语分组为NEAR组。NEAR组由标记“ NEAR”(区分大小写)指定,后跟一个开放的括号字符,然后是两个或多个空格分隔的短语,并可选地后面跟一个逗号和数字参数N,然后是一个封闭的括号。例如:

... MATCH'NEAR(“一二”“三四”,10)'
... MATCH'NEAR(“一二” thr * +四)

如果未提供N参数,则默认值为10。如果文档包含至少一个以下标记集,则NEAR组与该文档匹配:

  1. 每个短语至少包含一个实例,并且
  2. 对于其中第一短语的端部和在丛最后一个短语的开始之间的令牌的数量小于或等于ñ

例如:

使用fts5(x)创建虚拟表;
插入f(rowid,x)VALUES(1,'ABCD xxx EF x');

... MATCH'NEAR(ed,4)';                       -  火柴!
... MATCH'NEAR(ed,3)';                       -  火柴!
... MATCH'NEAR(ed,2)';                       -  不匹配!

... MATCH'NEAR(“ c d”“ e f”,3)';               -  火柴!
... MATCH'NEAR(“ c”“ e f”,3)';               -  不匹配!

...匹配'NEAR(ade,6)';                     -  火柴!
...匹配'NEAR(ade,5)';                     -  不匹配!

... MATCH'NEAR(“ abc d”“ b c”“ e f”,4)';     -  火柴!
... MATCH'NEAR(“ abc d”“ b c”“ e f”,3)';     -  不匹配!

3.6。FTS5色谱柱过滤器

单个短语或NEAR组可以被限制为在FTS表的指定列中匹配文本,方法是在其前面加上列名,后跟冒号字符。或为一组列添加前缀,并在其前面加上一个用括号括起来的空格分隔的列名列表(“花括号”),后跟一个冒号。可以使用上面针对字符串描述的两种形式中的任何一种来指定列名称。与短语中的字符串不同,列名不会传递到令牌生成器模块。对于SQLite列名称,列名称通常不区分大小写-仅对ASCII范围字符理解大小写等效。

... MATCH'colname:NEAR(“一二”“三四”,10)'
... MATCH'“ colname”:一+二+三'

... MATCH'{col1 col2}:NEAR(“一二”“三四”,10)'
... MATCH'{col2 col1 col3}:一+二+三'

如果列过滤器规范前面带有“-”字符,则它将被解释为与之不匹配的列列表。例如:

-搜索除“ colname”外的所有列中的匹配项
... MATCH'-姓:NEAR(“一二”“三四”,10)'

-搜索除“ col1”,“ col2”和“ col3”以外的所有列中的匹配项
... MATCH'-{col2 col1 col3}:一+二+三'

列过滤器规范也可以应用于括号中包含的任意表达式。在这种情况下,列过滤器适用于表达式中的所有短语。嵌套列过滤器操作可能只会进一步限制匹配的列子集,不能用于重新启用过滤后的列。例如:

-以下是等效的:
... MATCH'{ab}:({bc}:“ hello” AND“ world”)'
... MATCH'(b:“ hello”)AND({ab}:“ world”)'

最后,可以通过使用列名作为MATCH运算符的LHS(而不是通常的表名)来指定单个列的列过滤器。例如:

-给出下表
使用fts5(a,b,c)创建虚拟表ft;

-以下是等效的
选择*从ft WHERE b匹配'uvw AND xyz';
选择*从ft哪里ft匹配'b:(uvw和xyz)';

-此查询无法匹配任何行(因为所有列都被过滤掉了): 
选择*从ft WHERE b匹配'a:xyz';

3.7。FTS5布尔运算符

可以使用布尔运算符将短语和NEAR组安排到表达式中。从最高(最紧密的分组)到最低(最松散的分组)的优先顺序,运算符为:

操作员 功能
<query1> NOT <query2> 如果query1匹配,而query2不匹配,则匹配。
<query1> AND <query2> 如果query1和query2都匹配,则匹配。
<query1> OR <query2> 如果query1或query2匹配,则匹配。

括号可用于对表达式进行分组,以便以通常的方式修改运算符的优先级。例如:

-匹配包含至少一个“一个”
或“两个”实例,但不包含任何标记“三个”实例的文档
...匹配“一个或两个而不是三个”

-匹配所有包含标记“两个”但不
包含“三个”的文档,或-包含标记“一个”。
...匹配“一个或(两个非三个)”

短语和NEAR组也可以通过隐式AND运算符连接。为简单起见,以上BNF语法中未显示这些内容。本质上,仅由空格分隔的短语或NEAR组的任何序列(包括限于匹配指定列的序列)的处理方式就像在每对短语或NEAR组之间存在隐式AND运算符一样。隐式AND运算符永远不会插入括号内的表达式之后或之前。例如:

...匹配“一二三”          -“一与二和三​​” 
...匹配“三“一二””        -“三与“一二”” 
...匹配“近(一二)三'    -'NEAR(一二)AND三' 
... MATCH'一或二三'       -'一二或三AND'

... MATCH'((一个或两个)三个”     -语法错误!
... MATCH'func(one two)'          -语法错误!

4. FTS5表的创建和初始化

作为“ CREATE VIRTUAL TABLE ... USING fts5 ...”语句的一部分指定的每个参数都是列声明或配置选项。甲 列声明由一个或多个空格隔开FTS5裸字或文字串在可接受的SQLite任何方式引用的。

列声明中的第一个字符串或裸字是列名。尝试将fts5表列命名为“ rowid”或“ rank”,或为表本身使用的列分配相同的名称是错误的。不支持此功能。

列声明中的每个后续字符串或裸字都是一个列选项,用于修改该列的行为。列选项不区分大小写。与SQLite核心不同,FTS5将无法识别的列选项视为错误。当前,唯一识别的选项是 “ UNINDEXED”(请参阅​​下文)

配置选项包括一个FTS5裸词的-选项名称-后跟一个“=”字符,接着是选项值。使用单个FTS5裸字或字符串文字(通过SQLite核心可接受的任何方式再次引用)指定选项值。例如:

使用fts5(发件人,标题,正文,tokenize ='porter ascii')创建虚拟表邮件;

当前有以下配置选项:

4.1。UNINDEXED列选项

使用UNINDEXED列选项限定的列的内容不会添加到FTS索引中。这意味着出于MATCH查询和FTS5辅助功能的目的 ,该列不包含可匹配的标记。

例如,为避免将“ uuid”字段的内容添加到FTS索引中:

使用fts5(name,addr,uuid UNINDEXED)创建虚拟表客户;

4.2。前缀索引

默认情况下,FTS5维护一个索引,记录每个令牌实例在文档集中的位置。这意味着查询完整令牌的速度很快,因为它需要一次查找,但是查询前缀令牌的速度可能很慢,因为它需要进行范围扫描。例如,要查询前缀标记“ abc *”,需要对所有大于或等于“ abc”且小于“ abd”的标记进行范围扫描。

前缀索引是一个单独的索引,该索引以字符形式记录一定长度的所有前缀令牌实例的位置,这些字符用于加速对前缀令牌的查询。例如,优化查询以查询前缀标记“ abc *”需要使用三个字符前缀的前缀索引。

要将前缀索引添加到FTS5表中,请将“ prefix”选项设置为单个正整数或包含一个或多个正整数值的空格分隔列表的文本值。为每个指定的整数创建一个前缀索引。如果在单个CREATE VIRTUAL TABLE语句中指定了多个“前缀”选项,则所有选项均适用。

-创建FTS5表的两种方法,该表维护以下内容的前缀索引
-两个和三个字符的前缀标记。
使用fts5(a,b,prefix ='2 3')创建虚拟表ft;
使用fts5(a,b,prefix = 2,prefix = 3)创建虚拟表ft;

4.3。分词器

CREATE VIRTUAL TABLE“ tokenize”选项用于配置FTS5表使用的特定标记器。选项参数必须是FTS5裸字或SQL文本文字。参数的文本本身被视为一个或多个FTS5裸字或SQL文本文字的空格序列。其中第一个是要使用的令牌生成器的名称。第二个和后续列表元素(如果存在)是传递给令牌生成器实现的参数。

与选项值和列名不同,用作标记符的SQL文本文字必须使用单引号引起来。例如:

-以下都是等效的
使用fts5(x,tokenize ='porter ascii')创建虚拟表t1;
使用fts5(x,tokenize =“ porter ascii”)创建虚拟表t1;
使用fts5(x,tokenize =“'porter''ascii'”)创建虚拟表t1;
使用fts5(x,tokenize ='''porter'''ascii''')创建虚拟表t1;

-但这会失败:
使用fts5(x,tokenize ='“ porter”“ ascii”')创建虚拟表t1;

-这也将失败:
使用fts5(x,tokenize ='porter''ascii')创建虚拟表t1;

FTS5具有三个内置的令牌生成器模块,在后续各节中进行介绍:

也可以为FTS5创建自定义标记器。此处描述了用于执行此操作的API 。

4.3.1。Unicode61分词器

unicode标记程序将所有unicode字符分类为“分隔符”或“令牌”字符。默认情况下,Unicode 6.1定义的所有空格和标点符号都被视为分隔符,所有其他字符都被视为标记字符。更具体地,分配给以“ L”或“ N”(具体为字母和数字)开头的 常规类别或分配为“ Co”的类别(“其他私有用途”)的所有unicode字符 均视为令牌。所有其他字符都是分隔符。

一个或多个令牌字符的每次连续运行都被视为令牌。根据Unicode 6.1定义的规则,标记生成器不区分大小写。

默认情况下,变音符号将从所有拉丁脚本字符中删除。例如,这意味着“ A”,“ a”,“À”,“à”,“”和“â”都被认为是等效的。

令牌规范中“ unicode61”之后的所有参数均被视为交替的选项名称和值的列表。Unicode61支持以下选项:

选项 用法
remove_diacritics 此选项应设置为“ 0”,“ 1”或“ 2”。默认值为“ 1”。如果将其设置为“ 1”或“ 2”,则如上所述,从拉丁字母字符中删除变音符号。但是,如果将其设置为“ 1”,则在使用单个unicode码点表示一个带有多个变音符号的字符的情况非常少见的情况下,不删除变音符号。例如,变音符号不会从代码点0x1ED9中删除(“带有小圆点和下面的点的拉丁文小写字母O”)。从技术上讲,这是一个错误,但是如果不产生向后兼容性问题就无法修复。如果此选项设置为“ 2”,则将从所有拉丁字符中正确删除变音符号。
类别 此选项可用于修改被认为与令牌字符相对应的Unicode常规类别的集合。参数必须由以空格分隔的两个字符的常规类别缩写(例如“ Lu”或“ Nd”)组成,或由第二个字符替换为星号(“ *”)的空格列表组成,并解释为全局模式。默认值为“ L * N * Co”。
代币 此选项用于指定应视为标记字符的其他unicode字符,即使它们是根据Unicode 6.1的空格或标点符号也是如此。将此选项设置为字符串中的所有字符均视为令牌字符。
分隔符 此选项用于指定应视为分隔符的其他unicode字符,即使它们是根据Unicode 6.1的标记字符也是如此。将此选项设置为字符串中的所有字符都视为分隔符。

例如:

-创建不会从拉丁语中删除变音符号的FTS5表
-脚本字符,并考虑连字符和下划线字符
-成为代币的一部分。 
使用fts5(a,b, 
    tokenize =“ unicode61 remove_diacritics 0 tokenchars'-_'”
);

或者:

-创建一个FTS5表,以及默认的令牌字符类,
-将“ Mn”类中的字符视为令牌字符。
使用fts5(a,b, 
    tokenize =“ unicode61类别'L * N * Co Mn'”
);

fts5 unicode61令牌生成器与fts3 / 4 unicode61令牌生成器逐字节兼容。

4.3.2。Ascii分词器

Ascii标记生成器与Unicode61标记生成器类似,不同之处在于:

例如:

-创建一个使用ascii分词器的FTS5表,但不使用它
-将数字字符视为标记的一部分。
使用fts5(a,b, 
    tokenize =“ ascii分隔符'0123456789'”
);

4.3.3。波特令牌生成器

搬运程序标记器是包装标记器。它获取其他令牌生成器的输出,并在 每个令牌返回到FTS5之前对每个令牌应用 porter阻止算法。这允许诸如“更正”之类的搜索词匹配类似“更正”或“更正”之类的词。搬运工词干分析器算法仅设计用于英语术语-与其他语言一起使用可能会或可能不会改善搜索实用性。

默认情况下,搬运程序令牌生成器充当默认令牌生成器(unicode61)的包装器。或者,如果将一个或多个额外的参数添加到“ porter”之后的“ tokenize”选项中,则它们将被视为该porter stemmer使用的基础标记器的规范。例如:

-创建FTS5表的两种方法,该表使用Porter标记器来
-阻止默认标记器(unicode61)的输出。 
使用fts5(x,tokenize = porter)创建虚拟表t1; 
使用fts5(x,tokenize ='porter unicode61')创建虚拟表t1;

-波特标记器,用于阻止unicode61标记器的输出,
-在词根删除之前先删除变音符号。
使用fts5(x,tokenize ='porter unicode61 remove_diacritics 1')创建虚拟表t1;

4.3.4。实验性Trigram分词器

实验分词器延伸FTS5卦以支持在一般子串匹配,而不是通常的令牌匹配。使用Trigram标记器时,查询或短语标记可以匹配一行中的任何字符序列,而不仅仅是完整的标记。例如:

使用fts5(a,tokenize =“ trigram”)创建虚拟表tri;
插入三值('abcdefghij KLMNOPQRST uvwxyz');

-以下查询均与表中的单行匹配
SELECT * FROM tri('cdefg');
SELECT * FROM tri('cdefg AND pqr');
SELECT * FROM tri('“ hij klm” NOT stuv');

Trigram分词器支持单个选项-“ case_sensitive”。默认值为0,匹配不区分大小写。如果将此值设置为1,则所有匹配项均区分大小写。

-区分大小写的三字母组合索引
使用fts5(a,tokenize =“ trigram case_sensitive 1”)创建虚拟表tri;

使用Trigram标记器的FTS5表还支持索引的GLOB和LIKE模式匹配。例如:

SELECT * FROM tri在类似'%cdefg%'的地方;
SELECT * FROM tri WHERE GLOB'* ij klm * xyz';

如果在case_sensitive选项设置为1的情况下创建了FTS5三字母标记符,则它可能仅索引GLOB查询,而不是LIKE。

笔记:

4.4。外部内容和无内容表

通常,当将一行以及各种全文索引条目和其他数据插入到FTS5表中时,该行的副本将存储在由FTS5模块管理的专用表中。当用户或辅助功能实现从FTS5表中请求列值时,将从此私有表中读取它们。“内容”选项可用于创建仅存储FTS全文索引条目的FTS5表。由于列值本身通常比关联的全文本索引条目大得多,因此可以节省大量的数据库空间。

有两种使用“内容”选项的方法:

4.4.1。无内容的表

通过将“ content”选项设置为空字符串来创建无内容的FTS5表。例如:

使用fts5(a,b,c,content ='')创建虚拟表f1;

无内容的FTS5表不支持UPDATE或DELETE语句,或者不为rowid字段提供非NULL值的INSERT语句。无内容的表不支持REPLACE冲突处理。REPLACE和INSERT OR REPLACE语句被视为常规INSERT语句。可以使用FTS5 delete命令无内容表中删除行

尝试从无内容的FTS5表中读取除rowid之外的任何列值,将返回SQL NULL值。

4.4.2。外部内容表

通过将content选项设置为同一数据库内的表,虚拟表或视图(以下称为“内容表”)的名称,可以创建外部内容FTS5表。每当FTS5需要列值时,它将按以下方式查询内容表,并将需要值的行的rowid绑定到SQL变量:

从<content>哪里选择<content_rowid>,<cols>,<content_rowid> =?;

在上面,<content>被内容表的名称替换。默认情况下,<content_rowid>被文字文本“ rowid”替换。或者,如果在CREATE VIRTUAL TABLE语句中设置了“ content_rowid”选项,则通过该选项的值进行设置。<cols>替换为FTS5表列名称的逗号分隔列表。例如:

-如果数据库架构为: 
创建表tbl(a,b,c,d整数主键);
使用fts5(a,c,content = tbl,content_rowid = d)创建虚拟表fts;

-Fts5可能发出以下查询:
从dbl中选择d,a,c d =?;

还可以按以下方式查询内容表:

从<content>中选择<content_rowid>,<cols> ORDER BY <content_rowid> ASC;
从<content>中选择<content_rowid>,<cols> ORDER BY <content_rowid> DESC;

用户仍然有责任确保外部内容FTS5表的内容与内容表保持最新。一种实现方法是使用触发器。例如:

-创建一个表格。并用外部内容fts5表对其进行索引。
创建表tbl(a整数主键,b,c);
使用fts5(b,c,content ='tbl',content_rowid ='a')创建虚拟表fts_idx;

-触发以使FTS索引保持最新。
在tbl开始时创建触发器tbl_ai之后插入
  插入fts_idx(rowid,b,c)值(new.a,new.b,new.c);
结尾;
在tbl开始删除之后创建触发器tbl_ad
  插入fts_idx(fts_idx,rowid,b,c)VALUES('delete',old.a,old.b,old.c);
结尾;
创建触发器tbl_au tbl开始更新后
  插入fts_idx(fts_idx,rowid,b,c)VALUES('delete',old.a,old.b,old.c);
  插入fts_idx(rowid,b,c)值(new.a,new.b,new.c);
结尾;

像无内容表一样,外部内容表不支持REPLACE冲突处理。任何指定REPLACE冲突处理的操作都将使用ABORT处理。

4.5。列大小选项

通常,FTS5在数据库内维护一个特殊的备份表,该表将每个列值的大小存储在插入到单独的表中的主FTS5表中的令牌中。该支持表由 xColumnSize API函数使用,而后者又由内置bm25排名函数使用(并且可能对其他排名函数也很有用)。

为了节省空间,可以通过将columnize选项设置为零来省略该支持表。例如:

-一个没有xColumnSize()值存储在磁盘上的表:
使用fts5(a,b,c,columnize = 0)创建虚拟表ft;

-创建表的三种等效方法,该表的确将
xColumnSize()值存储在磁盘上:
使用fts5(a,b,c)创建虚拟表ft;
使用fts5(a,b,c,columnize = 1)创建虚拟表ft;
使用fts5(a,b,columnize ='1',c)创建虚拟表ft;

将columnize选项设置为0或1以外的任何值都是错误的。

如果FTS5表配置为columnize = 0,但不是 无内容,则xColumnSize API函数仍然有效,但运行速度要慢得多。在这种情况下,它不是读取值直接从数据库返回,而是读取文本值本身并根据需要对其中的令牌进行计数。

或者,如果该表也是一个无内容的表,则适用以下条件:

存储xColumnSize值的表的名称(除非指定了columnize = 0)是“ <name> _docsize”,其中<name>是FTS5表本身的名称。该 sqlite3_analyzer 工具可以在现有的数据库,以确定有多少空间可以通过重新使用columnsize = 0的FTS5表保存使用。

4.6。详细选项

对于文档中的每个术语,FTS5维护的FTS索引将存储文档的rowid,包含该术语的列的列号以及该术语在列值内的偏移量。“详细信息”选项可用于省略某些信息。这减少了索引在数据库文件中占用的空间,但同时也降低了系统的功能和效率。

详细信息选项可以设置为“完整”(默认值),“列”或“无”。例如:

-以下两行是等效的(因为
“详细信息”的默认值-为“已满”。
使用fts5(a,b,c)创建虚拟表ft1;
使用fts5(a,b,c,detail = full)创建虚拟表ft1;

使用fts5(a,b,c,detail = column)创建虚拟表ft2;
使用fts5(a,b,c,detail = none)创建虚拟表ft3;

如果detail选项设置为column,则对于每个术语,FTS索引仅记录rowid和列号,而忽略术语偏移量信息。这导致以下限制:

如果detail选项设置为none,那么对于每个术语,FTS索引仅记录存储的rowid。列和偏移量信息均被省略。除了上面对detail = column模式列出的限制之外,这还施加了以下额外限制:

在对大量电子邮件建立索引(磁盘上为1636 MiB)的测试中,FTS索引为磁盘上的743 MiB,明细=完整,340 MiB的明细=列,134 MiB的明细=无。

5.辅助功能

辅助函数与SQL标量函数相似,除了它们只能在FTS5表的全文查询(使用MATCH运算符的查询)中使用。它们的结果不仅基于传递给它们的参数进行计算,而且还基于当前的匹配项和匹配的行进行计算。例如,辅助函数可能会返回一个表示匹配准确性的数字值(请参阅bm25()函数),或者是来自匹配行的包含一个或多个搜索项实例的文本片段(请参见snippet( )功能)。

要调用辅助功能,应将FTS5表的名称指定为第一个参数。其他参数可能会在第一个之后,具体取决于所调用的特定辅助功能。例如,调用“突出显示”功能:

从电子邮件WHERE电子邮件匹配'fts5'中选择高亮显示(email,2,'<b>','</ b>')

下一部分将介绍作为FTS5的一部分提供的内置辅助功能。应用程序还可以在C中实现 自定义辅助功能

5.1。内置辅助功能

FTS5提供了三个内置辅助功能:

5.1.1。bm25()函数

内置辅助函数bm25()返回一个实值,该值指示当前行与全文查询的匹配程度。匹配越好,返回值在数值上越小。诸如以下的查询可用于按从最佳匹配到最差匹配的顺序返回匹配:

选择*从英尺哪里英尺匹配?订单由bm25(fts)

为了计算文档分数,将全文查询分为其组成短语。然后,文档D和查询Q的bm25得分计算如下:

在上面,nPhrase是查询中短语的数量。 | D | 是当前文档中令牌的数量,而 avgdl是FTS5表中所有文档中的令牌的平均数量。 k 1b都是常数,分别以1.2和0.75进行硬编码。

在BM25算法的大多数实现中都找不到公式开头的“ -1”项。没有它,更好的匹配将被分配更高的BM25分数。由于默认的排序顺序是“升序”,因此这意味着在查询后附加“ ORDER BY bm25(fts)”将导致结果按从最差到最好的顺序返回。为了首先返回最佳匹配,将需要“ DESC”关键字。为了避免这种陷阱,FT25在BM25的实现中将结果乘以-1,然后将其返回,以确保为更好的匹配分配较低的分数。

IDF(q i是查询短语i的反文档频率。计算公式如下,其中N是FTS5表中的总行数,n(q i是包含至少一个词组i实例的行总数:

最后,f(q i,D)是短语i的短语频率 。默认情况下,这只是该短语在当前行中出现的次数。但是,通过将额外的实值参数传递给bm25()SQL函数,可以为表的每一列分配不同的权重,并按如下方式计算短语频率:

其中w c是分配给c列的权重, n(q i,c)是短语i在当前行的c列中出现的次数。在表名之后传递给bm25()的第一个参数是分配给FTS5表最左列的权重。第二个是分配给第二个最左列的权重,依此类推。如果所有表列的参数不足,则其余列的权重为1.0。如果尾随参数过多,则多余的部分将被忽略。例如:

-假设采用以下架构:
使用fts5(发送者,标题,正文)创建虚拟表电子邮件;

-按bm25顺序返回结果,“发件人”中的每个短语命中
–“正文”列中的
每个命中
等于10,“标题”列中的每个命中被视为有价值5个命中在-“正文”列中。
选择*从电子邮件中,在哪里发送电子邮件?ORDER BY bm25(电子邮件,10.0,5.0);

有关BM25及其变体的更多信息,请参阅维基百科 。

5.1.2。Highlight()函数

Highlight()函数从当前行的指定列返回文本的副本,并插入额外的标记文本以标记短语匹配的开始和结束。

在表名之后,必须使用正好三个参数来调用Highlight()。解释如下:

  1. 一个整数,指示要从中读取文本的FTS表列的索引。列从零开始从左到右编号。
  2. 每个词组匹配之前要插入的文本。
  3. 每个词组匹配后要插入的文本。

例如:

-从当前
的最左列返回文本的副本,并使用html“ b”标签标记短语匹配。
从fts哪里选择fts,0,'<b>','</ b>')

如果两个或多个短语实例重叠(共同共享一个或多个标记),则会为每组重叠短语插入一个打开和关闭标记。例如:

-假设这样:
使用fts5(a)创建虚拟表ft;
插入ft值('abcxcd e');
插入ft值('abccd e');
插入ft值('abcd e');

-以下SELECT语句返回这三行:
-'[abc] x [cde]' 
-'[abc] [cde]' 
-'[abcde]'
从ft WHERE ft MATCH'a + b + c AND c + d + e'中选择Highlight(ft,0,'[',']');

5.1.3。snippet()函数

snippet()函数与Highlight()相似,除了它不返回整个列的值,而是自动选择并提取文档文本的一小段以进行处理和返回,这与snippet()函数相似。snippet()函数必须在表名称参数之后传递五个参数:

  1. 一个整数,指示要从中选择返回文本的FTS表列的索引。列从零开始从左到右编号。负值表示应自动选择该列。
  2. 要在返回的文本中每个短语匹配之前插入的文本。
  3. 在返回的文本中每个短语匹配之后要插入的文本。
  4. 要添加到所选文本的开头或结尾的文本,分别指示所返回的文本不在其列的开头或结尾。
  5. 返回的文本中的最大令牌数。该值必须大于零且等于或小于64。

5.2。按辅助功能结果排序

所有FTS5表均具有一个名为“ rank”的特殊隐藏列。如果当前查询不是全文查询(即,如果它不包含MATCH运算符),则“ rank”列的值始终为NULL。否则,在全文查询中,默认情况下,列级别包含与不带尾部参数执行bm25()辅助函数所返回的值相同的值。

仅在按返回值进行排序时,从等级列读取和直接在查询中使用bm25()函数之间的区别才很重要。在这种情况下,使用“等级”比使用bm25()更快。

-以下查询在逻辑上是等效的。但是第二个可能
会更快-特别是如果调用者在之前放弃查询的情况下
-所有行都已返回(或者如果查询被修改为
-包括LIMIT子句)。
选择*从英尺哪里英尺匹配?订购bm25(fts);
选择*从英尺哪里英尺匹配?ORDER BY等级;

可以使用每个查询或通过为FTS表设置不同的持久默认值来配置映射到rank列的特定辅助功能,而不是使用不带尾随参数的bm25()。

为了更改单个查询的等级列的映射,将类似于以下任一项的术语添加到查询的WHERE子句中:

等级MATCH'辅助功能名称(arg1,arg2,...)'
rank ='辅助功能名称(arg1,arg2,...)'

MATCH或=运算符的右侧必须是一个常量表达式,该表达式的结果为一个字符串,该字符串由要调用的辅助函数组成,后跟括号内的零个或多个逗号分隔的参数。参数必须是SQL文字。例如:

-以下查询在逻辑上是等效的。但是第二个可能
会更快。看上面。
选择*从英尺哪里英尺匹配?ORDER BY bm25(fts,10.0,5.0);
选择*从英尺哪里英尺匹配?AND rank匹配'bm25(10.0,5.0)'OR BY BY rank;

表值函数语法也可以用于指定替代排名函数。在这种情况下,应将描述排名函数的文本指定为第二个表值函数参数。以下三个查询是等效的:

选择*从英尺哪里英尺匹配?AND rank匹配'bm25(10.0,5.0)'OR BY BY rank;
选择*从fts在哪里fts =?AND rank ='bm25(10.0,5.0)'OR BY BY等级;
SELECT * FROM fts WHERE fts(?,'bm25(10.0,5.0)')ORDER BY等级;

可以使用FTS5等级配置选项来修改表的等级列的默认映射。

6.特殊的INSERT命令

6.1。“自动合并”配置选项

FTS5使用一系列b树,而不是使用磁盘上的单个数据结构来存储全文索引。每次提交新事务时,都会将包含已提交事务内容的新b树写入数据库文件中。查询全文索引时,必须分别查询每个b树,并将结果合并后再返回给用户。

为了防止数据库中的b树数量过大(降低查询速度),较小的b树会定期合并为包含相同数据的单个较大的b树。默认情况下,这会在修改全文索引的INSERT,UPDATE或DELETE语句中自动发生。“自动合并”参数确定一次合并多少个较小的b树。将其设置为较小的值可以加快查询速度(因为它们必须查询并合并来自较少b树的结果),但是也可以减慢写入数据库的速度(因为每个INSERT,UPDATE或DELETE语句必须执行更多工作)作为自动合并过程的一部分)。

构成全文索引的每个b树均基于其大小分配给“级别”。级别0的b树最小,因为它们包含单个事务的内容。更高级别的b树是将两个或更多的0级b树合并在一起的结果,因此它们更大。一旦存在M个或更多具有相同级别的b树,则FTS5开始将b树合并在一起,其中M是'automerge'参数的值。

“ automerge”参数的最大允许值为16。默认值为4。将“ automerge”参数设置为0将完全禁用b树的自动增量合并。

插入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”选项设置为负值是错误的。

插入ft(ft,rank)VALUES('crisismerge',16);

6.3。'删除'命令

该命令仅适用于外部内容无内容表。它用于从全文索引中删除与单行关联的索引条目。此命令和delete-all 命令是从无内容表的全文索引中删除条目的唯一方法。

为了使用此命令删除行,必须将文本值“ delete”插入与表同名的特殊列中。要删除的行的rowid插入到rowid列中。插入其他列的值必须与表中当前存储的值匹配。例如:

-在fts5表中插入rowid = 14的行。
插入ft(rowid,a,b,c)值(14,$ a,$ b,$ c);

-从fts5表中删除同一行。
插入ft(ft,rowid,a,b,c)VALUES('delete',14,$ a,$ b,$ c);

如果作为“删除”命令的一部分“插入”到文本列中的值与表中当前存储的值不同,则结果可能无法预测。

原因很容易理解:将文档插入FTS5表时,会将条目添加到全文索引中,以记录每个令牌在新文档中的位置。删除文档时,需要原始数据才能确定需要从全文本索引中删除的条目集。因此,如果使用此命令删除行时提供给FTS5的数据与插入时用于确定令牌实例集的数据不同,则某些全文索引条目可能无法正确删除,或者FTS5可能尝试删除不存在的索引条目。这会使全文索引处于不可预测的状态,从而使将来的查询结果不可靠。

6.4。“全部删除”命令

该命令仅适用于外部内容无内容表。它从全文索引中删除所有条目。

插入ft(ft)VALUES('delete-all');

6.5。“完整性检查”命令

此命令用于验证全文索引在内部是一致的,并且可以选择与任何外部内容表一致 。

通过将文本值“ integrity-check”插入与FTS5表同名的特殊列中来调用完整性检查命令。如果为“等级”列提供了值,则该值必须为0或1。例如:

插入ft(ft)值('integrity-check');
插入ft(ft,rank)VALUES('integrity-check',0);
插入ft(ft,rank)VALUES('integrity-check',1);

对于不是外部内容表的所有FTS表,以上三种形式均等效。他们检查索引数据结构是否未损坏,并且,如果FTS表不是无内容的,则检查索引的内容是否与表本身的内容匹配。

对于外部内容表,仅当为rank列指定的值为1时,才将索引的内容与外部内容表的内容进行比较。

在所有情况下,如果发现任何差异,该命令都会失败,并显示SQLITE_CORRUPT_VTAB错误。

6.6。'合并'命令

插入ft(ft,rank)VALUES('merge',500);

此命令将b树结构合并在一起,直到已将大约N页合并数据写入数据库为止,其中N是作为“合并”命令的一部分指定的参数的绝对值。每页的大小由FTS5 pgsz选项配置。

如果参数为正值,则仅当满足以下条件之一时,B树结构才有资格合并:

可以通过在执行命令之前和之后检查sqlite3_total_changes() API返回的值来判断'merge'命令是否找到了要合并的b树 。如果两个值之间的差为2或更大,则执行工作。如果差异小于2,则“合并”命令为无操作。在这种情况下,至少在下一次更新FTS表之前,没有理由再次执行相同的“合并”命令。

如果参数为负,并且FTS索引内的B树结构在一个以上的级别上,则在开始合并操作之前,所有B树结构都将分配给相同的级别。此外,如果参数为负,则不考虑usermerge配置选项的值-可以将来自同一级别的至少两个b树合并在一起。

上面的意思是执行带有负参数的'merge'命令直到sqlite3_total_changes()的返回值之前和之后的差异 小于2为止,以与FTS5优化命令相同的方式来优化FTS索引。但是,如果在此过程进行中将新的b树添加到FTS索引中,则FTS5会将新的b树移动到与现有b树相同的级别,然后重新开始合并。为避免这种情况,只有第一次调用“ merge”时才应指定一个否定参数。以后每次对“ merge”的调用都应指定一个正值,这样即使将新的b树添加到FTS索引中,由第一个调用开始的合并也将运行到完成。

6.7。“优化”命令

此命令将当前构成全文索引的所有单个b树合并到单个大b树结构中。这样可以确保全文索引占用数据库中的最小空间,并且以最快的形式查询。

有关全文索引与其组件b树之间的关系的更多详细信息,请参阅FTS5自动合并选项的文档。

插入ft(ft)VALUES('optimize');

因为它重新组织了整个FTS索引,所以optimize命令可能需要很长时间才能运行。所述FTS5合并命令可以用来划分优化FTS索引成多个步骤的工作。去做这个:

其中N是在合并命令的每次调用中要合并的数据页数。当合并命令前后sqlite3_total_changes()函数返回的值的差降至两个以下时,应用程序应停止调用合并。合并命令可以作为相同或独立事务的一部分,并由相同或不同的数据库客户端发出。有关更多详细信息,请参考merge命令的文档 。

6.8。'pgsz'配置选项

此命令用于设置持久性“ pgsz”选项。

FTS5维护的全文本索引作为一系列固定大小的Blob存储在数据库表中。组成全文本索引的所有blob的大小都不一定严格相同。pgsz选项确定由后续索引编写器创建的所有blob的大小。默认值为1000。

插入ft(ft,rank)VALUES('pgsz',4072);

6.9。“等级”配置选项

此命令用于设置持久性“ rank”选项。

rank选项用于更改rank列的默认辅助功能映射。该选项应设置为与“ rank MATCH?”所述格式相同的文本值。以上条款。例如:

插入ft(ft,rank)VALUES('rank','bm25(10.0,5.0)');

6.10。'重建'命令

该命令首先删除整个全文索引,然后根据表或内容表的内容对其进行重建。它不适用于无内容的表

插入ft(ft)VALUES('rebuild');

6.11。“ usermerge”配置选项

此命令用于设置持久性“ usermerge”选项。

usermerge选项类似于自动合并和危机合并选项。它是带有正参数的“ merge”命令将合并在一起的b树段的最小数量。例如:

插入ft(ft,rank)VALUES('usermerge',4);

usermerge选项的默认值为4。最小允许值为2,最大为16。

7.扩展FTS5

FTS5具有API,可通过以下方式扩展它:

本文档中描述的内置标记器和辅助功能全部使用下面描述的可公开获得的API来实现。

在向FTS5注册新的辅助功能或令牌生成器实现之前,应用程序必须获取指向“ fts5_api”结构的指针。每个注册FTS5扩展名的数据库连接都有一个fts5_api结构。为了获取指针,应用程序使用单个参数调用SQL用户定义的函数fts5()。必须使用sqlite3_bind_pointer()接口将该参数设置为指向fts5_api对象的指针的指针。下面的示例代码演示了该技术:

/ *
**返回指向数据库连接数据库fts5_api的指针。
**如果发生错误,则返回NULL并将错误留在数据库中 
**句柄(可使用sqlite3_errcode()/ errmsg()访问)。
* /
fts5_api * fts5_api_from_db(sqlite3 * db){
  fts5_api * pRet = 0;
  sqlite3_stmt * pStmt = 0;

  if(SQLITE_OK == sqlite3_prepare(db,“ SELECT fts5(?1)”,-1,&pStmt,0)){
    sqlite3_bind_pointer(pStmt,(void *)&pRet,“ fts5_api_ptr”,NULL);
    sqlite3_step(pStmt);
  }
  sqlite3_finalize(pStmt);
  返回pRet;
}

向后兼容性警告: 在SQLite版本3.20.0(2017-08-01)之前,fts5()的工作方式略有不同。必须对扩展FTS5的旧应用程序进行修改,以使用上面显示的新技术。

fts5_api结构定义如下。它公开了三种方法,一种用于注册新的辅助功能和令牌生成器,另一种用于检索现有令牌生成器。后者旨在促进类似于内置波特标记器的“标记器包装器”的实现。

typedef struct fts5_api fts5_api;
struct fts5_api {
  int iVersion;                   / *当前始终设置为2 * /

  / *创建一个新的令牌生成器* /
  int(* xCreateTokenizer)(
    fts5_api * pApi,
    const char * zName,
    无效* pContext,
    fts5_tokenizer * pTokenizer,
    无效(* xDestroy)(无效*)
  );

  / *查找现有的令牌生成器* /
  int(* xFindTokenizer)(
    fts5_api * pApi,
    const char * zName,
    无效** ppContext,
    fts5_tokenizer * pTokenizer
  );

  / *创建一个新的辅助功能* /
  int(* xCreateFunction)(
    fts5_api * pApi,
    const char * zName,
    无效* pContext,
    fts5_extension_function xFunction,
    无效(* xDestroy)(无效*)
  );
};

要调用fts5_api对象的方法,应将fts5_api指针本身作为方法的第一个参数传递,然后作为其他方法特定的参数传递。例如:

rc = pFts5Api-> xCreateTokenizer(pFts5Api,...其他参数...);

以下各节分别介绍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);
  无效(* xDelete)(Fts5Tokenizer *);
  int(* xTokenize)(Fts5Tokenizer *, 
      无效* pCtx,
      int标志,             / * FTS5_TOKENIZE_ *标志的掩码* /
      const char * pText,int nText, 
      int(* xToken)(
        无效* pCtx,          / *第二个参数到xTokenize()的副本* / 
        int tflags,          / * FTS5_TOKEN_ *标志的掩码* / 
        const char * pToken,/ *指向包含令牌的缓冲区的指针* / 
        int nToken,          / *令牌的大小以字节为单位* / 
        int iStart,          / *输入文本内令牌的字节偏移量* / 
        int iEnd             / *输入文本内令牌结束的字节偏移量* /
      )
  );
};

/ *可以作为第三个参数传递给xTokenize()的标志* /
#定义FTS5_TOKENIZE_QUERY 0x0001
#定义FTS5_TOKENIZE_PREFIX 0x0002
#定义FTS5_TOKENIZE_DOCUMENT 0x0004
#定义FTS5_TOKENIZE_AUX 0x0008

/ *标记器实现可能传递回FTS5的标志
**作为提供的xToken回调的第三个参数。* / 
#define FTS5_TOKEN_COLOCATED 0x0001       / *与上一个相同的位置。令牌* /

通过调用fts5_api对象的xCreateTokenizer()方法向FTS5模块注册该实现。如果已经存在具有相同名称的令牌生成器,则将其替换。如果将非NULL xDestroy参数传递给xCreateTokenizer(),则在关闭数据库句柄或替换令牌生成器时,将使用作为唯一参数传递的pContext指针的副本来调用该参数。

如果成功,则xCreateTokenizer()返回SQLITE_OK。否则,它将返回一个SQLite错误代码。在这种情况下,不会 调用xDestroy函数。

当FTS5表使用自定义令牌生成器时,FTS5内核调用一次xCreate()创建一个令牌生成器,然后将xTokenize()调用零次或更多次以对字符串进行令牌化,然后调用xDelete()释放由xCreate()分配的任何资源。进一步来说:

xCreate:

此函数用于分配和初始化令牌生成器实例。需要标记器实例才能实际标记文本。

传递给此函数的第一个参数是在fts5_tokenizer对象向FTS5注册时由应用程序提供的(void *)指针的副本(xCreateTokenizer()的第三个参数)。第二个和第三个参数是一个以nul结尾的字符串的数组,其中包含在令牌化程序名称之后指定的令牌化程序参数(如果有的话),该令牌化程序参数是用于创建FTS5表的CREATE VIRTUAL TABLE语句的一部分。

最后一个参数是输出变量。如果成功,则应将(* ppOut)设置为指向新的令牌生成器句柄,并返回SQLITE_OK。如果发生错误,则应返回SQLITE_OK以外的其他值。在这种情况下,fts5假定* ppOut的最终值未定义。

xDelete:

调用此函数可删除以前使用xCreate()分配的令牌生成器句柄。Fts5保证每次成功调用xCreate()时,该函数将仅被调用一次。

xTokenize:

期望该函数标记由参数pText指示的nText字节字符串。pText可以终止也可以不终止。传递给此函数的第一个参数是指向Fts5Tokenizer对象的指针,该对象是由先前对xCreate()的调用返回的。

第二个参数指示FTS5请求对提供的文本进行标记化的原因。这始终是以下四个值之一:

  • FTS5_TOKENIZE_DOCUMENT-将文档插入FTS表或从FTS表中删除。正在调用令牌生成器以确定要添加到FTS索引(或从FTS索引中删除)的令牌集。

  • FTS5_TOKENIZE_QUERY-正在对FTS索引执行MATCH查询。调用分词器对在查询中指定的裸词或带引号的字符串进行分词。

  • (FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX) -与FTS5_TOKENIZE_QUERY相同,不同之处在于裸字或带引号的字符串后跟一个“ *”字符,指示令牌生成器返回的最后一个令牌将被视为令牌前缀。

  • FTS5_TOKENIZE_AUX-调用分词器以满足辅助功能发出的fts5_api.xTokenize()请求。或者是在columnize = 0数据库上由fts5_api.xColumnSize()请求。

对于输入字符串中的每个令牌,必须调用提供的回调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查询“第一+位置”将匹配文档集中“第一位置”的实例,但不匹配诸如“第一位置”之类的替代形式。在某些应用程序中,最好匹配“第一名”或“第一名”的所有实例,而不管用户在MATCH查询文本中指定的哪种形式。

在FTS5中有几种方法可以解决此问题:

  1. 通过将所有同义词映射到单个标记。在这种情况下,使用上面的示例,这意味着令牌生成器为输入“ first”和“ 1st”返回相同的令牌。假设令牌实际上是“第一”,因此,当用户插入文档时,“我赢了第一名”条目被添加到令牌“ i”,“获胜”,“第一”和“地方”的索引中。如果用户随后查询“ 1st + place”,则令牌生成器将“ first”替换为“ 1st”,并且查询将按预期工作。

  2. 通过分别查询每个查询词的所有同义词的索引。在这种情况下,当标记查询文本时,标记生成器可以为文档中的单个术语提供多个同义词。然后FTS5分别查询每个同义词的索引。例如,面对查询:

    ...比赛“第一名”
    

    令牌生成器同时提供“ 1st”和“ first”作为MATCH查询中第一个令牌的同义词,FTS5有效地运行类似于以下查询:

    ...匹配“(第一个或第一个)位置”
    

    除此之外,出于辅助功能的目的,查询似乎仍然只包含两个短语-“((第一个或第一个)”)被视为单个短语。

  3. 通过将单个术语的多个同义词添加到FTS索引。使用此方法,在标记文档文本时,标记生成器为每个标记提供多个同义词。因此,当对诸如“我获得第一名”之类的文档进行标记时,会将条目添加到FTS索引中“ i”,“获胜”,“第一”,“第一”和“位置”。

    这样,即使令牌化程序在对查询文本进行令牌化时不提供同义词(这样做也不应该这样做,否则效率很低),用户查询“第一个+位置”还是“第一个+位置”也没关系,因为FTS索引中存在与第一种令牌的两种形式相对应的条目。

无论是解析文档还是查询文本,任何对使用FTS5_TOKEN_COLOCATED位指定tflags参数的xToken的调用都被视为提供了先前令牌的同义词。例如,当解析文档“我赢了第一名”时,支持同义词的令牌生成器将调用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);

在首次调用xToken()时指定FTS5_TOKEN_COLOCATED标志是错误的。通过依次调用xToken(FTS5_TOKEN_COLOCATED),可以为单个令牌指定多个同义词。对于单个令牌可以提供的同义词数量没有限制。

在许多情况下,上述方法(1)是最好的方法。它不会在FTS索引中添加额外的数据,也不需要FTS5来查询多个术语,因此在磁盘空间和查询速度方面非常有效。但是,它不能很好地支持前缀查询。如上所述,如果令牌生成器将令牌“ first”替换为“ 1st”,则查询:

...匹配“ 1s *”

将不匹配包含令牌“ 1st”的文档(因为令牌生成器可能不会将“ 1s”映射到“ first”的任何前缀)。

对于全前缀支持,方法(3)可能是首选。在这种情况下,由于索引包含“ first”和“ 1st”的条目,因此前缀查询(例如“ fi *”或“ 1s *”)将正确匹配。但是,由于将额外的条目添加到FTS索引,因此此方法在数据库中使用更多的空间。

方法(2)提供了(1)和(3)之间的中点。使用此方法,诸如“ 1s *”之类的查询将匹配包含文字标记“ 1st”但不包含“ first”的文档(假设标记生成器无法提供前缀的同义词)。但是,非前缀查询(如“ 1st”)将与“ 1st”和“ first”匹配。此方法不需要额外的磁盘空间,因为不会在FTS索引中添加任何额外的条目。另一方面,运行MATCH查询可能需要更多的CPU周期,因为每个同义词都需要对FTS索引进行单独的查询。

使用方法(2)或(3)时,重要的是,令牌化程序仅在对文档文本(方法(2))或查询文本(方法(3))进行令牌化时才提供同义词。这样做不会导致任何错误,但是效率很低。

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,    / *当前FTS版本提供的API * / 
  Fts5Context * pFts,               / *传递给pApi函数的第一个arg * / 
  sqlite3_context * pCtx,           / *返回结果/错误的上下文* / 
  int nVal,                        / *数字apVal []数组中的值的数量* / 
  sqlite3_value ** apVal            / *尾随参数的数组* /
);

通过调用fts5_api对象的xCreateFunction()方法向FTS5模块注册该实现。如果已经存在一个具有相同名称的辅助功能,则将其替换为新功能。如果将非NULL xDestroy参数传递给xCreateFunction(),则在关闭数据库句柄或替换已注册的辅助函数时,将使用作为唯一参数传递的pContext指针的副本来调用该参数。

如果成功,则xCreateFunction()返回SQLITE_OK。否则,它将返回一个SQLite错误代码。在这种情况下,不会 调用xDestroy函数。

传递给辅助函数回调的最后三个参数类似于传递给标量SQL函数的实现的三个参数。除第一个传递给辅助功能的参数外,所有参数都可用于apVal []数组中的实现。实现应通过内容句柄pCtx返回结果或错误。

传递给辅助函数回调的第一个参数是指向结构的指针,该结构包含可以被调用以获取有关当前查询或行的信息的方法。第二个参数是一个不透明的句柄,应将其作为第一个参数传递给任何此类方法调用。例如,以下辅助函数定义返回当前行所有列中的令牌总数:

/ *
**实现返回数字的辅助功能
当前行(包括所有列)中令牌的**。
* /
静态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(rc == SQLITE_OK){
    sqlite3_result_int(pCtx,nToken);
  }别的{
    sqlite3_result_error_code(pCtx,rc);
  }
}

下一节将详细介绍提供给辅助功能实现的API。可以在源代码的“ fts5_aux.c”文件中找到更多示例。

7.2.1。 自定义辅助功能API参考

struct Fts5ExtensionApi {
  int iVersion;                   / *当前始终设置为3 * /

  无效*(* 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,/ *要标记化的文本* / 
    void * pCtx,                    / *传递给xToken()的上下文* / 
    int(* xToken)(void *,int,const char *,int,int,int)        /* 打回来 */
  );

  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 *));
  无效*(* xGetAuxdata)(Fts5Context *,int bClear);

  int(* xPhraseFirst)(Fts5Context *,int iPhrase,Fts5PhraseIter *,int *,int *);
  无效(* xPhraseNext)(Fts5Context *,Fts5PhraseIter *,int * piCol,int * piOff);

  int(* xPhraseFirst列)(Fts5Context *,int iPhrase,Fts5PhraseIter *,int *);
  无效(* xPhraseNext Column)(Fts5Context *,Fts5PhraseIter *,int * piCol);
};
无效*(* xUserData)(Fts5Context *)

返回扩展功能向其注册的上下文指针的副本。

int(* xColumnTotalSize)(Fts5Context *,int iCol,sqlite3_int64 * pnToken)

如果参数iCol小于零,则将输出变量* pnToken设置为FTS5表中的令牌总数。或者,如果iCol为非负数但小于表中的列数,则考虑FTS5表中的所有行,返回iCol列中的令牌总数。

如果参数iCol大于或等于表中的列数,则返回SQLITE_RANGE。或者,如果发生错误(例如,OOM条件或IO错误),则返回适当的SQLite错误代码。

int(* xColumnCount)(Fts5Context *)

返回表中的列数。

int(* xColumnSize)(Fts5Context *,int iCol,int * pnToken)

如果参数iCol小于零,则将输出变量* pnToken设置为当前行中的令牌总数。或者,如果iCol为非负数但小于表中的列数,则将* pnToken设置为当前行的iCol列中的令牌数。

如果参数iCol大于或等于表中的列数,则返回SQLITE_RANGE。或者,如果发生错误(例如,OOM条件或IO错误),则返回适当的SQLite错误代码。

如果与通过“ columnsize = 0”选项创建的FTS5表一起使用,此功能可能效率很低。

int(* xColumnText)(Fts5Context *,int iCol,const char ** pz,int * pn)

此函数尝试检索当前文档的iCol列的文本。如果成功,则将(* pz)设置为指向包含以utf-8编码的文本的缓冲区,将(* pn)设置为以字节(不是字符)为单位的缓冲区大小,并返回SQLITE_OK。否则,如果发生错误,则返回SQLite错误代码,并且(* pz)和(* pn)的最终值不确定。

int(* xPhraseCount)(Fts5Context *)

返回当前查询表达式中的短语数。

int(* xPhraseSize)(Fts5Context *,int iPhrase)

返回查询短语iPhrase中的令牌数。短语从零开始编号。

int(* xInstCount)(Fts5Context *,int * pnInst)

将* pnInst设置为当前行中查询内所有短语出现的总数。如果成功,则返回SQLITE_OK,如果发生错误,则返回错误代码(即SQLITE_NOMEM)。

如果与通过“ detail = none”或“ detail = column”选项创建的FTS5表一起使用,此API可能会非常慢。如果使用“ detail = none”或“ detail = column”和“ content =”选项创建FTS5表(即,如果它是一个无内容的表),则此API始终返回0。

int(* xInst)(Fts5Context *,int iIdx,int * piPhrase,int * piCol,int * piOff)

在当前行中查询短语匹配iIdx的详细信息。词组匹配从零开始编号,因此iIdx参数应大于或等于零且小于xInstCount()输出的值。

通常,将输出参数* piPhrase设置为短语编号,将* piCol设置为该短语所在的列,并将* piOff设置为该短语的第一个标记的标记偏移量。如果成功,则返回SQLITE_OK,如果发生错误,则返回错误代码(即SQLITE_NOMEM)。

如果与通过“ detail = none”或“ detail = column”选项创建的FTS5表一起使用,此API可能会非常慢。

sqlite3_int64(* xRowid)(Fts5Context *)

返回当前行的rowid。

int(* xTokenize)(Fts5Context *, const char * pText,int nText, 无效* pCtx, int(* xToken)(无效*,整数,const char *,整数,整数,整数) )

使用属于FTS5表的标记器对文本进行标记。

int(* xQueryPhrase)(Fts5Context *,int iPhrase,void * pUserData, int(*)(const Fts5ExtensionApi *,Fts5Context *,void *) )

此API函数用于在FTS表中查询当前查询的短语iPhrase。具体来说,查询等效于:

...从ftstable那里ftstable MATCH $ p ORDER BY rowid

将$ p设置为与当前查询的短语iPhrase等效的短语。$ p中包含适用于当前查询的短语iPhrase的任何列过滤器。对于访问的每一行,将调用作为第四个参数传递的回调函数。传递给回调函数的上下文和API对象可用于访问每个匹配行的属性。调用Api.xUserData()返回作为第三个参数传递给pUserData的指针的副本。

如果回调函数返回的值不是SQLITE_OK,则查询将被放弃,并且xQueryPhrase函数将立即返回。如果返回的值是SQLITE_DONE,则xQueryPhrase返回SQLITE_OK。否则,错误代码将向上传播。

如果查询运行完成而没有发生意外,则返回SQLITE_OK。或者,如果在查询完成或回调取消之前发生了一些错误,则会返回一个SQLite错误代码。

int(* xSetAuxdata)(Fts5Context *,void * pAux,void(* xDelete)(void *))

将作为第二个参数传递的指针保存为扩展功能的“辅助数据”。然后,可以使用xGetAuxdata()API通过作为相同MATCH查询的一部分而进行的相同fts5扩展功能的当前或将来调用来检索指针。

每个扩展功能都为每个FTS查询(MATCH表达式)分配了一个辅助数据时隙。如果对单个FTS查询多次调用扩展功能,则所有调用共享一个辅助数据上下文。

如果在调用此函数时已经有一个辅助数据指针,则将其替换为新的指针。如果已将xDelete回调与原始指针一起指定,则将在此时调用它。

FTS5查询完成后,还将在辅助数据指针上调用xDelete回调(如果已指定)。

如果此函数内发生错误(例如,OOM条件),则辅助数据将设置为NULL,并返回错误代码。如果xDelete参数不为NULL,则在返回之前在辅助数据指针上调用它。

无效*(* xGetAuxdata)(Fts5Context *,int bClear)

返回fts5扩展功能的当前辅助数据指针。有关详细信息,请参见xSetAuxdata()方法。

如果bClear参数不为零,则在此函数返回之前,将清除辅助数据(设置为NULL)。在这种情况下,不会调用xDelete(如果有)。

int(* xRowCount)(Fts5Context *,sqlite3_int64 * pnRow)

此函数用于检索表中的总行数。换句话说,将通过以下方式返回相同的值:

SELECT count(*)from ftstable;
int(* xPhraseFirst)(Fts5Context *,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);
    iCol> = 0;
    pApi-> xPhraseNext(pFts,&iter,&iCol,&iOff)
){
  //列iCol的偏移量iOff处的短语iPhrase的实例
}

Fts5PhraseIter结构在上面定义。应用程序不应直接修改此结构-只能将其与xPhraseFirst()和xPhraseNext()API方法一起使用(如上所示,并与xPhraseFirstColumn()和xPhraseNextColumn()一起使用,如下所示)。

如果与通过“ detail = none”或“ detail = column”选项创建的FTS5表一起使用,此API可能会非常慢。如果FTS5表是使用“ detail = none”或“ detail = column”和“ content =”选项创建的(即,如果它是一个无内容的表),则此API始终会遍历一个空集(对xPhraseFirst( ),将iCol设置为-1)。

无效(* xPhraseNext)(Fts5Context *,Fts5PhraseIter *,int * piCol,int * piOff)

请参阅上面的xPhraseFirst。

int(* xPhraseFirstColumn)(Fts5Context *,int iPhrase,Fts5PhraseIter *,int *)

此函数和xPhraseNextColumn()与上述xPhraseFirst()和xPhraseNext()API相似。区别在于,这些API不会遍历当前行中某个短语的所有实例,而是用于遍历当前行中包含一个或多个指定短语实例的一组列。例如:

Fts5PhraseIter iter;
int iCol;
for(pApi-> xPhraseFirstColumn(pFts,iPhrase,&iter,&iCol);
    iCol> = 0;
    pApi-> xPhraseNextColumn(pFts,&iter,&iCol)
){
  //列iCol至少包含一个短语iPhrase的实例
}

如果与通过“ detail = none”选项创建的FTS5表一起使用,此API的速度可能会非常慢。如果FTS5表是使用“ detail = none”“ content =”选项创建的(即,如果它是无内容的表),则此API始终会迭代一个空集(所有对xPhraseFirstColumn()的调用都将iCol设置为-1) 。

使用此API及其随附的xPhraseFirstColumn()访问的信息也可以使用xPhraseFirst / xPhraseNext(或xInst / xInstCount)获得。此API的主要优点是,与“ detail = column”表一起使用时,它比那些替代方法更有效。

无效(* xPhraseNextColumn)(Fts5Context *,Fts5PhraseIter *,int * piCol)

请参阅上面的xPhraseFirstColumn。

8. fts5vocab虚拟表模块

fts5vocab虚拟表模块允许用户直接从FTS5全文索引中提取信息。fts5vocab模块是FTS5的一部分-每当有FTS5时都可用。

每个fts5vocab表都与一个FTS5表关联。通常通过在CREATE VIRTUAL TABLE语句中指定两个参数来代替列名来创建fts5vocab表-关联的FTS5表的名称和fts5vocab表的类型。当前,fts5vocab表有三种类型:“行”,“ col”和“实例”。除非在“ temp”数据库​​中创建了fts5vocab表,否则它必须与关联的FTS5表属于同一数据库。

-创建fts5vocab“行”表以查询所属的全文索引
-到FTS5表“ ft1”。
使用fts5vocab('ft1','row');创建虚拟表ft1_v

-创建fts5vocab“ col”表以查询所属的全文索引
-到FTS5表“ ft2”。
使用fts5vocab(ft2,col)创建虚拟表ft2_v;

-创建fts5vocab“实例”表以查询全文索引
-属于FTS5表“ ft3”。
使用fts5vocab(ft3,instance)创建虚拟表ft3_v

如果在临时数据库中创建了fts5vocab表,则该表可能与任何附加数据库中的FTS5表相关联。为了将fts5vocab表附加到位于“ temp”以外的数据库中的FTS5表上,在CREATE VIRTUAL TABLE参数的FTS5表名之前插入数据库的名称。例如:

-创建fts5vocab“行”表以查询所属的全文索引
-到数据库“主”中的FTS5表“ ft1”。
使用fts5vocab(main,'ft1','row')创建虚拟表temp.ft1_v;

-创建fts5vocab“ col”表以查询所属的全文索引
-到附加数据库“ aux”中的FTS5表“ ft2”。
使用fts5vocab('aux',ft2,col)创建虚拟表temp.ft2_v;

-创建fts5vocab“实例”表以查询全文索引 
-属于附加数据库“其他”中的FTS5表“ ft3”。
使用fts5vocab('aux',ft3,'instance')创建虚拟表temp.ft2_v;

在除“ temp”以外的任何数据库中创建fts5vocab表时,指定三个参数会导致错误。

类型为“行”的fts5vocab表针对相关FTS5表中的每个不同术语包含一行。表格列如下:

柱子内容
学期 该术语,存储在FTS5索引中。
doc 包含该术语至少一个实例的行数。
碳纳米管 整个FTS5表中该术语的实例总数。

类型为“ col”的fts5vocab表包含用于相关FTS5表中每个不同术语/列组合的一行。表列如下:

柱子内容
学期 该术语,存储在FTS5索引中。
关口 包含该术语的FTS5表列的名称。
doc FTS5表中$ col列包含至少一个术语实例的行数。
碳纳米管 显示在FTS5表的$ col列中的术语的实例总数(考虑所有行)。

类型为“ instance”的fts5vocab表针对存储在关联的FTS索引中的每个术语实例包含一行。假设创建的FTS5表的'detail'选项设置为'full',则表列如下:

柱子内容
学期 该术语,存储在FTS5索引中。
doc 包含术语实例的文档的rowid。
关口 包含术语实例的列的名称。
抵消术语实例在其列中的索引。术语按从0开始的出现顺序编号。

如果FTS5表是在'detail'选项设置为'col'的情况下创建的,则实例虚拟表的offset列始终包含NULL。在这种情况下,表格中的每个唯一术语/文档/列组合只有一行。或者,如果在将“详细信息”设置为“无”的情况下创建FTS5表,则offsetcol始终都包含NULL值。对于detail = none FTS5表,对于每个唯一的term / doc组合,在fts5vocab表中都有一行。

例子:

-假设使用以下方式创建数据库:
使用fts5(c1,c2)创建虚拟表ft1;
插入ft1值(“香蕉香蕉樱桃”,“香蕉香蕉樱桃”);
插入ft1值('cherry cherry cherry','date date date');

-然后查询以下fts5vocab表(类型为“ col”)将返回:
--
-苹果| c1 | 1 | 1个
-香蕉| c1 | 1 | 1个
-香蕉| c2 | 1 | 2个
-樱桃| c1 | 2 | 4
-樱桃| c2 | 1 | 1个
-日期| c3 | 1 | 3
--
使用fts5vocab(ft1,col)创建虚拟表ft1_v_col;

-查询“行”类型的fts5vocab表将返回:
--
-苹果| 1 | 1个
-香蕉| 1 | 3
-樱桃| 2 | 5
-日期| 1 | 3
--
使用fts5vocab(ft1,row)创建虚拟表ft1_v_row;

-并且,对于“实例”类型
插入ft1值(“香蕉香蕉樱桃”,“香蕉香蕉樱桃”);
插入ft1值('cherry cherry cherry','date date date');
--
-苹果| 1 | c1 | 0
-香蕉| 1 | c1 | 1个
-香蕉| 1 | c2 | 0
-香蕉| 1 | c2 | 1个
-樱桃| 1 | c1 | 2个
-樱桃| 1 | c2 | 2个
-樱桃| 2 | c1 | 0
-樱桃| 2 | c1 | 1个
-樱桃| 2 | c1 | 2个
-日期| 2 | c2 | 0
-日期| 2 | c2 | 1个
-日期| 2 | c2 | 2个
--
使用fts5vocab(ft1,instance)创建虚拟表ft1_v_instance;

附录A:与FTS3 / 4的比较

也提供类似但更成熟的FTS3 / 4模块。FTS5是FTS4的新版本,其中包含各种修复程序和解决方案,以解决在不牺牲向后兼容性的情况下无法在FTS4中解决的问题。其中一些问题 描述如下

应用程序移植指南

为了使用FTS5代替FTS3或FTS4,应用程序通常需要进行最少的修改。其中大多数分为三类-用于创建FTS表的CREATE VIRTUAL TABLE语句所需的更改,用于对表执行查询的SELECT查询所需的更改以及使用FTS辅助功能的应用程序所需的更改。

对CREATE VIRTUAL TABLE语句的更改

  1. 模块名称必须从“ fts3”或“ fts4”更改为“ fts5”。

  2. 必须从列定义中删除所有类型信息或约束规范。FTS3 / 4会忽略列定义中列名称之后的所有内容,FTS5尝试对其进行解析(如果失败,则会报告错误)。

  3. “ matchinfo = fts3”选项不可用。所述 “columnsize = 0”选项等价。

  4. notindexed =选项不可用。将UNINDEXED添加 到列定义是等效的。

  5. ICU令牌生成器不可用。

  6. compress =,uncompress =和languageid =选项不可用。到目前为止,它们的功能还不相同。

 -FTS3 / 4声明 
使用fts4()创建虚拟表t1
  linkid INTEGER,
  标头CHAR(20),
  文字VARCHAR,
  notindexed = linkid,
  matchinfo = fts3,
  tokenizer = unicode61
);

-相当于FTS5(注意-不需要“ tokenizer = unicode61”选项
,因为无论如何这是FTS5的默认设置)
使用fts5创建虚拟表t1
  linkid UNINDEXED,
  标头
  文本,
  列大小= 0
);

对SELECT语句的更改

  1. “ docid”别名不存在。应用程序必须使用“ rowid”来代替。

  2. 将列过滤器既指定为FTS查询的一部分,又将列用作MATCH运算符的LHS时,查询的行为略有不同。对于具有“ a”和“ b”列的表,以及与以下查询类似的查询:

    ... MATCH'b:字符串'
    

    FTS3 / 4在“ b”列中搜索匹配项。但是,FTS5始终返回零行,因为首先对列“ b”过滤结果,然后对“ a”列进行过滤,而没有结果。换句话说,在FTS3 / 4中,内部过滤器优先于外部过滤器,在FTS5中,两个过滤器均被应用。

  3. FTS查询语法(MATCH运算符的右侧)已在某些方面进行了更改。FTS5语法与FTS4“增强语法”非常接近。主要区别在于FTS5对于无法识别的标点字符和查询字符串中的相似字符都比较挑剔。大多数与FTS3 / 4一起使用的查询也应该与FTS5一起使用,而那些不应该返回解析错误的查询。

辅助功能变更

FTS5没有matchinfo()或offsets()函数,并且snippet()函数的功能不如FTS3 / 4中的完整。但是,由于FTS5确实提供了允许应用程序创建自定义辅助功能的API ,因此可以在应用程序代码内实现任何必需的功能。

FTS5提供的内置辅助功能集可能会在将来得到改进。

其他事宜

  1. fts5vocab现在提供了fts4aux模块提供的功能。这两个表的架构略有不同。

  2. FTS3 / 4“ merge = X,Y”命令已由FTS5合并命令替换 。

  3. FTS3 / 4“ automerge = X”命令已由FTS5 automerge选项代替 。

技术差异摘要

FTS5与FTS3 / 4的相似之处在于,每个FTS5的主要任务是维护从每个唯一令牌到一组文档中该令牌的实例列表的索引映射,其中每个实例都由出现它的文档来标识。及其在该文档中的位置。例如:

-给出以下SQL:
使用fts5(a,b)创建虚拟表ft;
插入ft(rowid,a,b)VALUES(1,'X Y','Y Z');
插入ft(rowid,a,b)VALUES(2,'A Z','Y Y');

-FTS5模块在磁盘上创建以下映射:
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,依此类推)。使用该索引,FTS5能够及时提供查询答案,例如“包含令牌'A'的所有文档的集合”或“包含序列'Y Z'的所有文档的集合”。与单个令牌关联的实例列表称为“实例列表”。

FTS3 / 4和FTS5之间的主要区别在于,在FTS3 / 4中,每个实例列表都存储为单个大型数据库记录,而在FTS5中,大型实例列表被划分为多个数据库记录。这对于处理包含大列表的大型数据库具有以下含义:

由于这些原因,许多复杂的查询可能使用FTS5占用更少的内存并运行得更快。

FTS5与FTS3 / 4不同的其他一些方式是:

附录B:FTS5创建的影子表

在数据库中创建FTS5虚拟表时,将在数据库中创建3到5个真实表。这些被称为“影子表”,并由虚拟表模块用于存储持久性数据。用户不应直接访问它们。许多其他虚拟表模块,包括 FTS3rtree,也可以创建和使用影子表。

FTS5创建以下影子表。在每种情况下,实际的表名均基于FTS5虚拟表的名称(在下表中,将<name>替换为虚拟表的名称以找到实际的影子表名)。

表名内容
<名称> _data 该表包含大多数全文本索引数据。
<名称> _idx该表包含全文索引数据的其余部分。它几乎总是比<name> _data表小得多。
<名称> _config 包含持久性配置参数的值。
<名称> _content包含插入FTS5表中的实际数据。这个影子表不存在用于 无内容外部内容FTS5表。
<name> _docsize包含令牌中虚拟表中每一行每一列的大小。如果将“ columnsize”选项设置为0 ,则此影子表不存在。