非常教程

Sqlite参考手册

全文索引 | Full-Text Search

SQLite FTS5 Extension

1. FTS5概述

2.编译和使用FTS5

2.1.构建FTS5作为SQLite的一部分

2.2.构建可装载的扩展

3.全文查询语法

3.1.FTS5字符串

3.2.FTS5短语

3.3.FTS5前缀查询

3.4.FTS5 NEAR查询

3.5.FTS5柱过滤器

3.6.FTS5布尔运算符

4. FTS5表创建和初始化

4.1.UNINDEXED列选项

4.2.前缀索引

4.3.断词

4.3.1.Unicode61 Tokenizer

4.3.2.Ascii Tokenizer

4.3.3. Porter Tokenizer

4.4.外部内容和无内容表格

4.4.1.无内容表格

4.4.2.外部内容表

4.5.Columnsize选项

4.6.详细选项

5.辅助功能

5.1.内置辅助功能

5.1.1.bm25()函数

5.1.2.highlight()函数

5.1.3.snippet()函数

5.2.通过辅助功能结果排序

6.特殊的INSERT命令

6.1.'automerge'配置选项

6.2.'危机综合'配置选项

6.3.'删除'命令

6.4.'全部删除'命令

6.5.'完整性检查'命令

6.6.'合并'命令

6.7.'优化'命令

6.8.'pgsz'配置选项

6.9.'等级'配置选项

6.10.'重建'命令

6.11.'usermerge'配置选项

7.扩展FTS5

7.1.自定义标志符

7.1.1.同义词支持

7.2.自定义辅助功能

7.2.1.自定义辅助函数API参考

8. fts5vocab虚拟表模块

附录A:与FTS3/4的比较

应用程序移植指南

对CREATE VIRTUAL TABLE语句的更改

对SELECT语句的更改

辅助功能更改

其他事宜

技术差异总结

FTS5是一个SQLite虚拟表模块,为数据库应用程序提供全文搜索功能。在最基本的形式中,全文搜索引擎允许用户高效地搜索包含一个或多个搜索词实例的子集的大量文档。Google向全球网络用户提供的搜索功能是全文搜索引擎,因为它允许用户搜索网络上包含例如术语“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字段。

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

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

  • 在SELECT语句的WHERE子句中使用MATCH运算符,或
  • 在SELECT语句的WHERE子句中使用equals(“=”)运算符,或
  • 使用表值函数语法。

如果使用MATCH或=运算符,则MATCH运算符左边的表达式通常是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>标记包围。除了将FTS5表的名称指定为第一个参数外,辅助函数的调用方式与SQLite标量函数相同。例如:

-- 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还允许用户搜索包含以下内容的行:

  • 任何以指定前缀开头的术语,
  • “短语” - 必须在文档中用于匹配查询的术语或前缀术语序列,
  • 出现在彼此指定的邻近范围内的术语,前缀术语或短语集合(这些被称为“近似查询”),或者
  • 任何上述的布尔组合。

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

2.1.构建FTS5作为SQLite的一部分

从版本3.9.0(2015-10-14)开始,FTS5作为SQLite合并的一部分包含在内。它默认是禁用的。如果使用两个autoconf编译系统,则在运行configure脚本时通过指定“--enable-fts5”选项来启用它。

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

2.2.构建可装载的扩展

或者,FTS5可以构建为可加载的扩展。

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

  1. 从Fossil中获取最新的SQLite代码。
  1. 按照如何编译SQLite中的描述创建一个Makefile。
  1. 构建“fts5.c”目标。这也创建fts5.h.
$ wget -c http://www.sqlite.org/src/tarball/SQLite-trunk.tgz?uuid=trunk -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_init
  • sqlite3_fts5_init

另一个文件“fts5.h”不需要编译FTS5扩展。它由实现定制FTS5标记器或辅助功能的应用程序使用。

以下块包含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风格。
  • 作为不是“AND”,“OR”或“NOT”(区分大小写)的FTS5裸字。FTS5裸字是一个或多个连续字符的字符串,它们都是:
-  Non-ASCII range characters (i.e. unicode codepoints greater than 127), or 
-  One of the 52 upper and lower case ASCII characters, or 
-  One of the 10 decimal digit ASCII characters, or 
-  The underscore character (unicode codepoint 96). 
-  The substitute character (unicode codepoint 26). 

包含任何其他字符的字符串必须被引用。裸字中当前不允许使用的字符不是引号字符,并且目前不在FTS5查询表达式中使用任何特殊用途,可能在将来的某个时间点被允许使用裸词或用于实现新的查询功能。这意味着当前语法错误的查询,因为它们在引用字符串外包含这样一个字符,可能会被某些未来版本的FTS5解释为不同。

3.2.FTS5短语

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

... MATCH '"one two three"'
... MATCH 'one + two + three'
... MATCH '"one two" + three'
... MATCH 'one.two.three'

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

3.3. FTS5 Prefix Queries

如果一个“*”字符跟在FTS表达式中的字符串之后,那么从字符串中提取的最后一个标记将被标记为前缀标记。正如您所预料的那样,前缀标记可匹配任何前缀为其的文档标记。例如,以下块中的前两个查询将匹配任何包含紧跟着标记“two”的标记“one”和任何以“thr”开头的标记的文档。

... MATCH '"one two thr" * '
... MATCH 'one + two + thr*'
... MATCH '"one two thr*"'      -- May not work as expected!

上述块中的最终查询可能无法按预期工作。由于“*”字符位于双引号内,因此它将被传递给标记器,这可能会丢弃它(或者可能取决于所使用的特定标记器,将其包括为最终标记的一部分)而不是识别它作为一个特殊的FTS角色。

3.4.FTS5 NEAR查询

两个或更多的短语可以被分组到一个NEAR组中。NEAR组由标记“NEAR”(区分大小写),后跟一个开括号字符,后跟两个或多个空白分隔的短语指定,后面紧跟一个逗号和数字参数N,后跟一个右括号。例如:

... MATCH 'NEAR("one two" "three four", 10)'
... MATCH 'NEAR("one two" thr* + four)'

如果未提供N参数,则默认为10.如果文档至少包含一个标记丛,则NEAR组将匹配文档:

  1. 包含每个短语的至少一个实例,并且

2. 对于其中第一短语的端部和在丛最后一个短语的开始之间的令牌的数量小于或等于ñ

例如:

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.5.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.6.FTS5布尔运算符

短语和NEAR组可以使用布尔运算符排列成表达式。按照从最高(最紧密分组)到最低(最宽松分组)的优先顺序,操作是:

操作者

功能

<query1> NOT <query2>

如果query1匹配且query2不匹配,则匹配。

<query1> AND <query2>

如果query1且query2匹配,则匹配。

<query1> OR <query2>

如果query1或query2匹配,则匹配。

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

-- 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'

-- Match all documents that contain the token "two" but not "three", or
-- contain the token "one".
... MATCH 'one OR (two NOT three)'

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

... 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 OR two) three'    -- Syntax error!
... MATCH 'func(one two)'         -- Syntax error!

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

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

列声明中的每个后续字符串或空白字都是修改该列行为的列选项。列选项与大小写无关。与SQLite核心不同,FTS5将无法识别的列选项视为错误。目前,唯一认可的选项是“UNINDEXED”(见下文)。

一个配置选项包括一个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表中每个值的标记大小是否单独存储在数据库中。
  • “detail”选项。此选项可用于通过省略某些信息来减小磁盘上FTS索引的大小。

4.1.UNINDEXED列选项

具有UNINDEXED列选项限定的列的内容不会被添加到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语句的一部分,则全部适用。

-- Two ways to create an FTS5 table that maintains prefix indexes for
-- two and three character prefix tokens.
CREATE VIRTUAL TABLE ft USING fts5(a, b, prefix='2 3');
CREATE VIRTUAL TABLE ft USING fts5(a, b, prefix=2, prefix=3);

4.3.断词

CREATE VIRTUAL TABLE“tokenize”选项用于配置FTS5表使用的特定标记器。选项参数必须是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具有三个内置的tokenizer模块,在后面的章节中有介绍:

  • unicode61标记生成器,基于Unicode 6.1标准。这是默认设置。
  • ASCII标记生成器,其中假设ASCII码点范围(0-127)之外的所有字符都被视为令牌字符。
  • porter标记生成器,它实现了波特所产生的算法。

也可以为FTS5创建自定义标记。这里描述了这样做的API。

4.3.1.Unicode61 Tokenizer

unicode标记器将所有unicode字符分类为“分隔符”或“标记”字符。默认情况下,Unicode 6.1定义的所有空格和标点符号都被视为分隔符,所有其他字符被视为令牌字符。每个连续运行的一个或多个令牌字符被视为令牌。根据Unicode 6.1定义的规则,标记器不区分大小写。

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

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

选项

用法

remove_diacritics

该选项应设置为“0”或“1”。如果已设置(默认),则从上述所有拉丁脚本字符中删除变音符号。如果很清楚,他们不是。

tokenchars

此选项用于指定应被视为令牌字符的其他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 '-_'"
);

fts5 unicode61标记器与fts3 / 4 unicode61标记器的字节对字节兼容。

4.3.2.Ascii Tokenizer

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 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.4.外部内容和无内容表格

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

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

  • 通过将其设置为空字符串来创建无内容的FTS5表。在这种情况下,FTS5假定处理查询时原始列值不可用。全文查询和一些辅助功能仍然可以使用,但是除了rowid以外,没有列值可以从表中读取。
  • 通过将其设置为可随时由FTS5查询的数据库对象(表,虚拟表或视图)的名称来检索列值。这被称为“外部内容”表。在这种情况下,可能会使用所有FTS5功能,但用户有责任确保全文索引的内容与指定的数据库对象一致。如果不是,查询结果可能不可预测。

4.4.1.无内容表格

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

CREATE VIRTUAL TABLE f1 USING fts5(a, b, c, content='');

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

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

4.4.2.外部内容表

通过将内容选项设置为同一数据库内的表,虚拟表或视图(以下称“内容表”)的名称来创建外部内容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, content=tbl, content_rowid=d);

-- 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, content='tbl', content_rowid='a');

-- Triggers to keep the FTS index up to date.
CREATE TRIGGER tbl_ai AFTER INSERT ON tbl BEGIN
  INSERT INTO fts_idx(rowid, b, c) VALUES (new.a, new.b, new.c);
END;
CREATE TRIGGER tbl_ad AFTER DELETE ON tbl BEGIN
  INSERT 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 BEGIN
  INSERT 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.5.Columnsize选项

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

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

-- A table without the xColumnSize() values stored on disk:
CREATE VIRTUAL TABLE ft USING fts5(a, b, c, columnsize=0);

-- 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, columnsize=1);
CREATE VIRTUAL TABLE ft USING fts5(a, b, columnsize='1', c);

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

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

或者,如果表格也是无内容表格,则适用以下内容:

  • xColumnSize API始终返回-1。无法确定存储在配置为columnize = 0的无内容FTS5表中的值中的令牌数量。
  • 每个插入的行必须附有明确指定的rowid值。如果使用columnsize = 0配置无内容表,则尝试向rowid中插入NULL值是SQLITE_MISMATCH错误。
  • 表格上的所有查询都必须是全文查询。换句话说,他们必须使用MATCH或=操作符和table-name列作为左操作数,否则使用表值函数语法。任何不是全文查询的查询都会导致错误。存储xColumnSize值的表的名称(除非指定了columnsize = 0)是“<name> _docsize”,其中<name>是FTS5表本身的名称。该sqlite3_analyzer工具可用于现有数据库,以便通过使用columnsize = 0重新创建FTS5表来确定可节省多少空间。4.6。详细选项对于文档中的每个术语,由FTS5维护的FTS索引存储文档的rowid,包含该术语的列的列号以及术语在列值内的偏移量。“细节”选项可用于省略某些此类信息。这减少了索引在数据库文件中占用的空间,但也降低了系统的能力和效率。详细选项可能设置为“完整”(默认值),“列”或“无”。例如: - 以下两行相同(因为“detail”的默认值为“full”)CREATE VIRTUAL TABLE ft1使用fts5(a,b,C); CREATE VIRTUAL TABLE ft1使用fts5(a,b,c,detail = full); 创建虚拟表ft2使用fts5(a,b,c,detail = column); CREATE VIRTUAL TABLE ft3使用fts5(a,b,c,detail = none);如果detail选项设置为,那么对于每个术语,FTS索引仅记录rowid和列号,省略术语偏移信息。这会导致以下限制:
  • NEAR查询不可用。
  • 短语查询不可用。
  • 假设表格不是无内容表格,则xInstCount,xInst,xPhraseFirst和xPraseraseNext比平常慢。这是因为不是直接从FTS索引中读取所需的数据,而是必须按需加载和标记文档文本。
  • 如果该表也是无内容表,则xInstCount,xInst,xPhraseFirst和xPhraseNext API的行为就好像当前行根本不包含任何词组匹配(即xInstCount()返回0)。

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

  • 列过滤器查询不可用。
  • 假设该表不是一个无内容表,xPhraseFirstColumn和xPhraseNextColumn比平时慢。
  • 如果该表也是无内容表,则xPhraseFirstColumn和xPhraseNextColumn API的行为就好像当前行根本不包含任何短语匹配(即xPhraseFirstColumn()将迭代器设置为EOF)。

在一个对大量电子邮件进行索引的测试中(磁盘上的1636MBB),FTS索引在磁盘上为743MB,其中detail = full,340Mib,detail = column,134Mib,detail = none。

辅助函数与SQL标量函数类似,但它们只能在FTS5表中的全文查询(使用MATCH运算符的那些查询)中使用。他们的结果不仅根据传递给他们的参数进行计算,还会根据当前匹配和匹配的行进行计算。例如,辅助函数可能会返回一个表示匹配准确性的数值(请参阅bm25()函数),或者匹配行中包含搜索项的一个或多个实例的文本片段(请参阅片段( )功能)。

要调用辅助函数,应将FTS5表的名称指定为第一个参数。其他参数可以在第一个之后,具体取决于被调用的特定辅助功能。例如,要调用“高亮”功能:

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的bm25分数:

SQLite FTS5 Extension

在上面,nPhrase是查询中的短语数量。| d | 是当前文档中的标记数量,avgdl是FTS5表格内所有文档中平均标记数量。k1b都是常数,分别以1.2和0.75进行硬编码。

公式开始处的“-1”项在BM25算法的大多数实现中未找到。没有它,一个更好的匹配被分配一个数值更高的BM25分数。由于默认的排序顺序是“升序”,这意味着将“ORDER BY bm25(fts)”附加到查询会导致结果从最坏到最佳顺序返回。为了首先返回最佳匹配,将需要“DESC”关键字。为了避免这个陷阱,BM25的FTS5实现在返回之前将结果乘以-1,确保更好的匹配在数值上被分配较低的分数。

IDF(qi)是查询短语i的逆文档频率。它计算如下,其中N是FTS5表中的总行数,n(qi)是包含至少一个短语i的实例的行的总数:

SQLite FTS5 Extension

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

SQLite FTS5 Extension

其中wc是分配给c列的权重,n(qi,c)是当前行的c列中词组i的出现次数。传递给表名后面的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()。解释如下:

  1. 一个整数,指示要从中读取文本的FTS表列的索引。列从零开始从左到右进行编号。

2. 在每个词组匹配前插入的文本。

3. 在每个词组匹配后插入的文本。

例如:

-- 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 'a+b+c AND c+d+e';

5.1.3.snippet()函数

snippet()函数与highlight()类似,不同之处在于它不是返回整个列值,而是自动选择并提取文档文本的短片段以进行处理和返回。snippet()函数必须在表名参数之后传递五个参数:

  1. 一个整数,指示从中选择返回文本的FTS表列的索引。列从零开始从左到右进行编号。负值表示应该自动选择该列。

2. 要在返回的文本中匹配每个词组之前插入的文本。

3. 要在返回的文本中匹配每个词组之后插入的文本。

4. 要添加到选定文本的开头或结尾的文本,以分别指示返回的文本不会在其列的开头或结尾出现。

5. 返回文本中令牌的最大数量。这必须大于零并且等于或小于64。

5.2.通过辅助功能结果排序

所有FTS5表格都有一个名为“rank”的特殊隐藏栏。如果当前查询不是全文查询(即,如果它不包含MATCH运算符),则“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()而不使用尾随参数,映射到rank列的特定辅助函数可以基于每个查询进行配置,也可以通过为FTS表设置不同的持久性默认值来进行配置。

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

rank MATCH 'auxiliary-function-name(arg1, arg2, ...)'
rank = 'auxiliary-function-name(arg1, arg2, ...)'

MATCH或=运算符的右侧必须是一个常量表达式,该表达式的结果是由要调用的辅助函数组成的字符串,然后是括号内的零个或多个以逗号分隔的参数。参数必须是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.1.'automerge'配置选项

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

为了防止数据库中b树的数量变得过大(减慢查询速度),将较小的b树周期性地合并为包含相同数据的单个较大的b树。默认情况下,这会在修改全文索引的INSERT,UPDATE或DELETE语句中自动发生。'automerge'参数确定一次有多少个小型b-树合并在一起。将它设置为一个较小的值可以加快查询速度(因为它们必须查询和合并来自较少b-树的结果),但是也会减慢对数据库的写入速度(因为每个INSERT,UPDATE或DELETE语句都需要做更多的工作作为自动合并过程的一部分)。

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

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

INSERT INTO ft(ft, rank) VALUES('automerge', 8);

6.2.'危机综合'配置选项

'危机合并'选项与'automerge'类似,因为它决定了组成全文索引的组件B树如何以及经常合并到一起。一旦全文索引中的单一级别存在C或更多的B树,其中C是“危机合并”选项的值,则该级别上的所有B树都会立即合并为一棵B树。

此选项和“automerge”选项之间的区别在于,当达到“automerge”限制时,FTS5仅开始将b树合并在一起。大部分工作是作为后续INSERT,UPDATE或DELETE操作的一部分执行的。而当达到“危机酝酿”的限制时,违规的B树就会立即合并。这意味着触发危机合并的INSERT,UPDATE或DELETE可能需要很长时间才能完成。

默认的“危机汇编”值为16.没有最大限制。尝试将'crisismerge'参数设置为0或1的值相当于将其设置为默认值(16)。试图将“危机合并”选项设置为负值是错误的。

INSERT INTO ft(ft, rank) VALUES('crisismerge', 16);

6.3.'删除'命令

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

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

-- Insert a row with rowid=14 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.'全部删除'命令

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

INSERT INTO ft(ft) VALUES('delete-all');

6.5.'完整性检查'命令

该命令用于验证全文索引是否与FTS5表或内容表的内容一致。它不适用于无内容表格。

通过将文本值'integrity-check'插入到与FTS5表同名的特殊列中来调用integrity-check命令。例如:

INSERT INTO ft(ft) VALUES('integrity-check');

如果全文索引与表的内容一致,则用于调用integrity-check命令的INSERT成功。或者,如果发现任何差异,则会失败并显示SQLITE_CORRUPT_VTAB错误。

6.6.'合并'命令

INSERT INTO ft(ft, rank) VALUES('merge', 500);

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

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

  • 在单个级别上有多个或多个这样的b树(有关b树级别的解释,请参阅FTS5 automerge选项的文档),其中U是分配给FTS5 usermerge选项选项的值。
  • 合并已经开始(可能通过指定否定参数的“合并”命令)。

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

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

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

6.7.'优化'命令

该命令将当前构成全文索引的所有单个b树合并为一个大的b树结构。这可确保全文索引消耗数据库中的最小空间,并以最快的形式进行查询。

有关全文索引及其组件b-trees之间关系的更多详细信息,请参阅FTS5 automerge选项的文档。

INSERT INTO ft(ft) VALUES('optimize');

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

  • 然后调用参数设置为-N的“merge”命令一次
  • 将参数设置为N,调用“合并”命令零次或多次

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

6.8.'pgsz'配置选项

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

由FTS5维护的全文索引作为一系列固定大小的斑点存储在数据库表中。构成全文索引的所有斑点的大小并不是完全必要的。pgsz选项决定后续索引编写者创建的所有斑点的大小。缺省值是1000。

INSERT INTO ft(ft, rank) VALUES('pgsz', 4072);

6.9.'等级'配置选项

该命令用于设置持久“排序”选项。

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

INSERT INTO ft(ft, rank) VALUES('rank', 'bm25(10.0, 5.0)');

6.10.'重建'命令

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

INSERT INTO ft(ft) VALUES('rebuild');

6.11.'usermerge'配置选项

该命令用于设置持久的“用户合并”选项。

usermerge选项类似于automerge和crisismerge选项。它是通过具有正参数的“合并”命令合并在一起的最小数量的b-树段。例如:

INSERT INTO ft(ft, rank) VALUES('usermerge', 4);

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

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

  • 添加在C中实现的新辅助功能,以及
  • 添加新的标记器,也在C中实现。

本文档中描述的内置令牌处理器和辅助功能都是使用下面描述的公共可用API实现的。

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

/*
** 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_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);
  return pRet;
}

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

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

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 *pContext,
    fts5_tokenizer *pTokenizer,
    void (*xDestroy)(void*)
  );

  /* Find an existing tokenizer */
  int (*xFindTokenizer)(
    fts5_api *pApi,
    const char *zName,
    void **ppContext,
    fts5_tokenizer *pTokenizer
  );

  /* Create a new auxiliary function */
  int (*xCreateFunction)(
    fts5_api *pApi,
    const char *zName,
    void *pContext,
    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内核调用xCreate()一次创建标记器,然后xTokenize()零次或多次标记字符串,然后使用xDelete()释放由xCreate()分配的任何资源。进一步来说:

xCreate:

该函数用于分配和初始化标记器实例。标记器实例需要实际标记文本。

传递给此函数的第一个参数是当fts5_tokenizer对象使用FTS5(xCreateTokenizer()的第三个参数)注册时,应用程序提供的(void *)指针的副本。第二个和第三个参数是一个由nul结尾的字符串组成的数组,包含标记器参数(如果有),作为用于创建FTS5表的CREATE VIRTUAL TABLE语句的一部分,在标记器名称后指定。

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

xDelete:

调用此函数以删除先前使用xCreate()分配的标记器句柄。Fts5保证每次成功调用xCreate()时都会调用该函数一次。

xTokenize:

这个函数预计会标记由参数pText指示的nText字节字符串。pText可能会或可能不会被终止。传递给此函数的第一个参数是一个指向先前调用xCreate()所返回的Fts5Tokenizer对象的指针。

第二个参数表明FTS5正在请求提供文本的标记的原因。这总是以下四个值之一:

  • FTS5_TOKENIZE_DOCUMENT - 正在将文档插入FTS表中或从中删除。正在调用标记器以确定要添加到(或从中删除)FTS索引的标记集。
  • FTS5_TOKENIZE_QUERY - MATCH查询正在针对FTS索引执行。正在调用标记器来标记指定为查询一部分的裸字或带引号的字符串。
  • (FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX) - 与FTS5_TOKENIZE_QUERY相同,不同之处在于裸词或带引号的字符串后跟“*”字符,表示标记器返回的最后一个标记将被视为标记前缀。
  • FTS5_TOKENIZE_AUX - 正在调用标记器以满足辅助函数所做的fts5_api.xTokenize()请求。或者在columnize = 0的数据库上使用相同的fts5_api.xColumnSize()请求。

对于输入字符串中的每个标记,必须调用提供的回调xToken()。它的第一个参数应该是作为xTokenize()的第二个参数传递的指针的副本。第三和第四个参数是指向包含标记文本的缓冲区的指针,以及以字节为单位的标记大小。第四个和第五个参数是紧接在输入中衍生出令牌的文本之后的第一个字节和第一个字节的字节偏移量。

传递给xToken()回调(“tflags”)的第二个参数通常应设置为0.例外是,如果标记器支持同义词。在这种情况下,请参阅下面的讨论了解详情。

FTS5假定xToken()回调是按照它们在输入文本中出现的顺序为每个令牌调用的。

如果xToken()回调函数返回除SQLITE_OK以外的任何值,则应放弃标记化处理,并且xTokenize()方法应立即返回xToken()返回值的副本。或者,如果输入缓冲区耗尽,xTokenize()应返回SQLITE_OK。最后,如果xTokenize()实现本身发生错误,它可能会放弃标记并返回除SQLITE_OK或SQLITE_DONE以外的任何错误代码。

7.1.1.同义词支持

自定义标记器也可能支持同义词。考虑用户希望查询诸如“首位”之类的短语的情况。使用内置的分词器,FTS5查询“first + place”将匹配文档集内“first place”的实例,但不能替代“first place”等其他形式。在某些应用程序中,最好匹配“第一位”或“第一位”的所有实例,无论MATCH查询文本中指定的用户是哪一种形式。

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

  1. 通过将所有同义词映射到单个令牌。在这种情况下,在上面的例子中,这意味着标记器为输入“first”和“1st”返回相同的标记。说令牌实际上是“第一”,这样当用户插入文件“我赢了第一名”时,条目将被添加到令牌“我”,“赢”,“第一”和“地点”的索引中。如果用户然后查询“1st + place”,则标记器将“first”替换为“1st”,并且查询按预期工作。
  1. 通过为FTS索引添加单个词语的多个同义词。在这种情况下,当标记化查询文本时,标记器可以为文档中的单个词提供多个同义词。然后FTS5分别查询每个同义词的索引。例如,面对查询:... MATCH'第一名'

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

... MATCH '(first OR 1st) place'

除了为了辅助功能的目的,查询仍然包含两个短语 - “(first OR 1st)”被视为单个短语。

  1. 通过为FTS索引添加单个词语的多个同义词。使用此方法时,标记文档文本时,标记器为每个标记提供多个同义词。因此,当诸如“我赢了第一名”之类的文档被标记时,条目将被添加到“我”,“赢”,“第一”,“第一”和“地点”的FTS索引。这样,即使标记器在标记查询文本时不提供同义词(它不应该 - 这样做效率不高),如果用户查询“first + place”或“1st + place”,则无关紧要,因为在FTS索引中存在与第一令牌的两种形式相对应的实体。无论是解析文档还是查询文本,对xToken的任何调用都会指定一个tflags与FTS5_TOKEN_COLOCATED位的参数被认为是为前一个标记提供同义词。例如,当解析文档“我赢了第一名”时,支持同义词的分词器将调用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查询多个术语,因此它在磁盘空间和查询速度方面效率很高。但是,它不能很好地支持前缀查询。如果如上所述,令牌“第一”被标记器取代为“1st”,则查询:... MATCH'1s *'将不匹配包含标记“1st”的文档(因为标记器可能不将“1”映射到任何前缀“第一”)。对于完整的前缀支持,方法(3)可能是首选。在这种情况下,因为索引包含“first”和“1st”的条目,所以前缀查询(如'fi *'或'1s *' 将正确匹配。但是,由于额外的条目被添加到FTS索引,此方法在数据库中使用更多的空间。方法(2)提供(1)和(3)之间的中点。使用这种方法,诸如'1s *'的查询将匹配包含文字标记“1st”但不是“first”的文档(假设标记器不能提供前缀的同义词)。但是,像'1st'这样的非前缀查询将匹配“1st”和“first”。此方法不需要额外的磁盘空间,因为没有额外的条目添加到FTS索引。另一方面,它可能需要更多CPU周期来运行MATCH查询,因为每个同义词需要单独的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函数* / sqlite3_context * pCtx,/ *返回结果/ / int nVal,/ * apVal []数组中的值的数量* / sqlite3_value ** apVal / *结尾参数数组* /); 通过调用fts5_api对象的xCreateFunction()方法将实现注册到FTS5模块。如果已经有一个具有相同名称的辅助功能,它将被新功能取代。如果将非空xDestroy参数传递给xCreateFunction(),则会在数据库句柄关闭时或者替换已注册的辅助函数时,使用作为唯一参数传递的pContext指针的副本来调用它。传递给辅助函数回调的最后三个参数与传递给标量SQL函数的三个参数类似。除了首先传递给辅助函数的所有参数都可以在apVal []数组中实现。实现应该通过内容句柄pCtx返回结果或错误。传递给辅助函数回调的第一个参数是一个指向包含方法的结构的指针,这些方法可能会被调用以获取有关当前查询或行的信息。第二个参数是一个不透明的句柄,它应该作为第一个参数传递给任何这样的方法调用。例如,以下辅助函数定义将返回当前行所有列中的标记总数:/ * **实现返回当前行中标记数量**的辅助函数(包括所有列)。* / 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); 如果(rc == SQLITE_OK){sqlite3_result_int(pCtx,nToken); } else {sqlite3_result_error_code(pCtx,rc); }}以下部分详细介绍了提供给辅助功能实现的API。更多的例子可以在源代码的“fts5_aux.c”文件中找到。7.2.1。自定义辅助函数API Referencestruct Fts5ExtensionApi {int iVersion; / *目前总是设置为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,/ *上下文传递给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 *)); 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); };void *(* xUserData)(Fts5Context *)返回扩展函数注册的上下文指针的副本。int(* xColumnTotalSize)(Fts5Context *,int iCol,sqlite3_int64 * pnToken)如果参数iCol小于零,请将输出变量* pnToken设置为FTS5表中的标记总数。或者,如果iCol非负数但小于表中的列数,则返回列iCol中的标记总数,考虑FTS5表中的所有行。如果参数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可能会很慢。如果FTS5表是使用“detail = none”或“detail = column”和“content =”选项(即它是无内容表)创建的,则此API始终返回0. int(* xInst)(Fts5Context * ,int iIdx,int * piPhrase,int * piCol,int * piOff)查询当前行中词组匹配iIdx的详细信息。短语匹配从零开始编号,因此iIdx参数应该大于或等于零并小于xInstCount()输出的值。通常,输出参数* piPhrase设置为短语编号,* piCol设置为其出现的列,* piOff设置短语的第一个标记的标记位移。例外情况是如果该表是使用指定的偏移量= 0选项创建的。在这种情况下* piOff始终设置为-1。如果成功,则返回SQLITE_OK;如果发生错误,则返回错误代码(即SQLITE_NOMEM)。如果与使用“detail = none”或“detail = column”选项创建的FTS5表一起使用,此API可能会很慢。sqlite3_int64(* xRowid)(Fts5Context *)返回当前行的rowid。int(* xTokenize)(Fts5Context *,const char * pText,int nText,void * pCtx,int(* xToken)(void *,int,const char *,int,int,int)使用属于FTS5表。int(* xQueryPhrase)(Fts5Context *,int iPhrase,void * pUserData,int(*)(const Fts5ExtensionApi *,Fts5Context *,void *))此API函数用于查询当前查询的短语iPhrase的FTS表。具体而言,查询等同于:... FROM ftstable WHERE ftstable MATCH $ p ORDER BY rowid with $ p设置为与当前查询的短语iPhrase相当的短语执行。任何适用于当前查询的短语iPhrase的列过滤器都包含在$ p中。对于访问的每一行,都会调用作为第四个参数传递的回调函数。传递给回调函数的上下文和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回调与原始指针一起指定,则在此时调用它。如果指定了xDelete回调,也在FTS5查询完成后在辅助数据指针上调用。如果在此函数中发生错误(例如OOM条件),则辅助数据将设置为NULL并返回错误代码。如果xDelete参数不是NULL,则在返回之前在辅助数据指针上调用它。void *(* 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; 对于(pApi-> xPhraseFirst(pFts,iPhrase,&iter,&iCol,&iOff); iCol> = 0; pApi-> xPhraseNext(pFts,&iter,&iCol,&iOff)){//在列偏移量iOff处的短语iPhrase实例iCol}上面定义了Fts5PhraseIter结构。应用程序不应该直接修改此结构 - 只应按照以下所示使用xPhraseFirst()和xPhraseNext()API方法(以及xPhraseFirstColumn()和xPhraseNextColumn())如上所示。如果与使用“detail = none”或“detail = column”选项创建的FTS5表一起使用,此API可能会很慢。如果FTS5表是用“detail = none”或“detail = column”和“content =”选项创建的(即如果它是无内容表),那么这个API总是遍历一个空集(所有调用xPhraseFirst )将iCol设置为-1)。void(* xPhraseNext)(Fts5Context *,Fts5PhraseIter *,int * piCol,int * piOff)请参见上面的xPhraseFirst。int(* xPhraseFirstColumn)(Fts5Context *,int iPhrase,Fts5PhraseIter *,int *)这个函数和xPhraseNextColumn()类似于上面描述的xPhraseFirst()和xPhraseNext()API。不同之处在于,不是遍历当前行中所有短语的实例,而是使用这些API遍历当前行中包含指定短语的一个或多个实例的一组列。例如:Fts5PhraseIter iter; int iCol; 对于(pApi-> xPhraseFirstColumn(pFts,iPhrase,&iter,&iCol); iCol> = 0; pApi-> xPhraseNextColumn(pFts,&iter,&iCol)){//列iCol包含至少一个短语iPhrase实例}如果与使用“detail = none”选项创建的FTS5表一起使用,则速度会很慢。如果FTS5表是用“detail = none”“content =”选项创建的(即如果它是无内容表),那么这个API总是遍历一个空集(所有对xPhraseFirstColumn()的调用都将iCol设置为-1)。使用此API及其伴侣xPhraseFirstColumn()访问的信息也可以使用xPhraseFirst / xPhraseNext(或xInst / xInstCount)获取。这个API的主要优点是,与“detail = column”表一起使用时,它的效率明显高于这些替代方法。void(* xPhraseNextColumn)(Fts5Context *,Fts5PhraseIter *,int * piCol)请参阅上面的xPhraseFirstColumn。8. fts5vocab虚拟表模块fts5vocab虚拟表模块允许用户直接从FTS5全文索引中提取信息。fts5vocab模块是FTS5的一部分 - 只要FTS5是可用的。每个fts5vocab表都与一个FTS5表相关联。通常通过在CREATE VIRTUAL TABLE语句中指定两个参数来替代列名创建fts5vocab表 - 关联的FTS5表的名称和fts5vocab表的类型。目前有三种类型的fts5vocab表格; “row”,“col”和“instance”。除非在“temp”数据库​​中创建了fts5vocab表,否则它必须是与关联的FTS5表相同数据库的一部分。 - 创建一个fts5vocab“行” 表来查询属于FTS5表“ft1”的全文索引。CREATE VIRTUAL TABLE ft1_v USING fts5vocab('ft1','row'); - 创建一个fts5vocab“col”表来查询属于FTS5表“ft2”的全文索引。CREATE VIRTUAL TABLE ft2_v USING fts5vocab(ft2,col); - 创建一个fts5vocab“实例”表来查询全文索引 - 属于FTS5表“ft3”。CREATE VIRTUAL TABLE ft3_v USING fts5vocab(ft3,instance); 如果在临时数据库中创建了fts5vocab表,则它可能与任何附加数据库中的FTS5表相关联。为了将fts5vocab表附加到位于非temp的数据库中的FTS5表,数据库的名称将插入CREATE VIRTUAL TABLE参数中的FTS5表名之前。例如: - 创建一个fts5vocab“行”表来查询属于数据库“main”中的FTS5表“ft1”的全文索引。CREATE VIRTUAL TABLE temp.ft1_v USING fts5vocab(main,'ft1','row'); - 创建一个fts5vocab“col”表来查询属于附属数据库“aux”中的FTS5表“ft2”的全文索引。CREATE VIRTUAL TABLE temp.ft2_v USING fts5vocab('aux',ft2,col); - 创建一个fts5vocab“实例”表来查询全文索引 - 属于附加数据库“other”中的FTS5表“ft3”。CREATE VIRTUAL TABLE temp.ft2_v USING fts5vocab('aux',ft3,'instance'); 在除“temp”之外的任何数据库中创建fts5vocab表时,指定三个参数会导致错误。类型“行”的fts5vocab表包含关联的FTS5表中每个不同术语的一行。表格列如下所示:ColumnContentsterm存储在FTS5 index中的术语.doc至少包含一个term.cnt实例的行数。该术语在整个FTS5表中的总实例数。类型为“col”的fts5vocab表包含关联的FTS5表中每个不同的术语/列组合的一行。表格列如下所示:ColumnContentsterm存储在FTS5 index.col中的术语包含term的FTS5表格列的名称.docFTS5表中列数为$ col的列至少包含一个术语实例的行数。 cnt在FTS5表的列$ col中出现的术语实例的总数(考虑所有行)。类型为“instance”的fts5vocab表包含存储在关联的FTS索引中的每个词条实例的一行。假设FTS5表格的'detail'选项设置为'full',表格列如下所示:ColumnContentsterm该术语,存储在FTS5索引中..doc包含术语instance.col的文档的rowid列名称其中包含术语instance.offset其列中的术语实例的索引。术语按从0开始的顺序进行编号。如果FTS5表格的'detail'选项设置为'col',则创建 ColumnContentsterm存储在FTS5索引中的术语.doc包含术语instance.col的文档的rowid包含术语instance.offset的列的名称其列中的术语实例索引。术语按从0开始的顺序进行编号。如果FTS5表格的'detail'选项设置为'col',则创建 ColumnContentsterm存储在FTS5索引中的术语.doc包含术语instance.col的文档的rowid包含术语instance.offset的列的名称其列中的术语实例索引。术语按从0开始的顺序进行编号。如果FTS5表格的'detail'选项设置为'col',则创建实例虚拟表的偏移列始终包含NULL。在这种情况下,每个唯一的term / doc / col组合在表格中有一行。或者,如果FTS5表的'detail'设置为'none',那么offsetcol都是创建的始终包含NULL值。对于detail = none FTS5表,每个唯一的term / doc组合在fts5vocab表中有一行。示例: - 假定使用以下命令创建数据库:CREATE VIRTUAL TABLE ft1 USING fts5(c1,c2); INSERT INTO ft1 VALUES('apple banana cherry','banana banana cherry'); INSERT INTO ft1 VALUES('樱桃樱桃','约会日期'); - 然后查询下面的fts5vocab表(键入“col”)返回: - - apple | c1 | 1 | 1 - 香蕉| c1 | 1 | 1 - 香蕉| c2 | 1 | 2 - 樱桃| c1 | 2 | 4 - 樱桃| c2 | 1 | 1 - 日期| c3 | 1 | 3 - 创建虚拟表格ft1_v_col使用fts5vocab(ft1,col); - 查询类型为“row”的fts5vocab表返回: - - 苹果| 1 | 1 - 香蕉| 1 | 3 - 樱桃| 2 | 5 - 日期| 1 | 3 - CREATE VIRTUAL TABLE ft1_v_row使用fts5vocab(ft1,row); - 对于类型“实例”INSERT INTO ft1 VALUES('apple banana cherry','banana banana cherry'); INSERT INTO ft1 VALUES('樱桃樱桃','约会日期'); - - 苹果| 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 - CREATE VIRTUAL TABLE ft1_v_instance USING fts5vocab(ft1,instance);附录A:与FTS3 / 4比较也可以使用类似但更成熟的FTS3 / 4模块。FTS5是FTS4的新版本,包含各种修补程序和问题解决方案,在不牺牲向后兼容性的情况下无法在FTS4中修复。下面介绍其中的一些问题。应用程序移植指南为了使用FTS5而不是FTS3或FTS4,应用程序通常只需要很少的修改。其中大部分分为三类 - 用于创建FTS表的CREATE VIRTUAL TABLE语句所需的更改,用于对表执行查询的SELECT查询所需的更改以及使用FTS辅助功能的应用程序所需的更改。对CREATE VIRTUAL TABLE语句的更改 应用程序移植指南为了使用FTS5而不是FTS3或FTS4,应用程序通常只需要很少的修改。其中大部分分为三类 - 用于创建FTS表的CREATE VIRTUAL TABLE语句所需的更改,用于对表执行查询的SELECT查询所需的更改以及使用FTS辅助功能的应用程序所需的更改。对CREATE VIRTUAL TABLE语句的更改 应用程序移植指南为了使用FTS5而不是FTS3或FTS4,应用程序通常只需要很少的修改。其中大部分分为三类 - 用于创建FTS表的CREATE VIRTUAL TABLE语句所需的更改,用于对表执行查询的SELECT查询所需的更改以及使用FTS辅助功能的应用程序所需的更改。对CREATE VIRTUAL TABLE语句的更改
  1. 模块名称必须从“fts3”或“fts4”更改为“fts5”。
  1. 必须从列定义中删除所有类型信息或约束规范。FTS3 / 4忽略列定义中列名后面的所有内容,FTS5尝试解析它(并且如果失败则报告错误)。
  1. “matchinfo = fts3”选项不可用。“columnsize = 0”选项是等效的。
  1. notindexed =选项不可用。将UNINDEXED添加到列定义中是等效的。
  1. ICU标记器不可用。
  1. The compress=, uncompress= and languageid= options are not available. There is as of yet no equivalent for their functionality. -- FTS3/4 statement CREATE VIRTUAL TABLE t1 USING fts4( linkid INTEGER, header CHAR(20), text VARCHAR, notindexed=linkid, matchinfo=fts3, tokenizer=unicode61 ); -- FTS5 equivalent (note - the "tokenizer=unicode61" option is not -- required as this is the default for FTS5 anyway) CREATE VIRTUAL TABLE t1 USING fts5( linkid UNINDEXED, header, text, columnsize=0 );Changes to SELECT statements
  1. “docid”别名不存在。应用程序必须使用“rowid”。
  1. 将列过滤器既指定为FTS查询的一部分,又将列用作MATCH操作符的LHS时的查询行为稍有不同。对于列“a”和“b”以及类似于以下查询的表格:... MATCH'b:字符串'FTS3 / 4在列“b”中搜索匹配项。但是,FTS5总是返回零行,因为首先对列“b”进行筛选,然后对列“a”进行筛选,而不留下任何结果。换句话说,在FTS3 / 4中,内部过滤器覆盖外部,在FTS5中,两个过滤器都被应用。
  1. FTS查询语法(MATCH运算符的右侧)在某些方面发生了变化。FTS5语法非常接近FTS4“增强语法”。主要区别在于FTS5对查询字符串中无法识别的标点符号和类似内容感到烦恼。与FTS3 / 4一起工作的大多数查询也应该与FTS5一起使用,而那些不应该返回解析错误的查询也适用于FTS5。

辅助功能更改

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

FTS5提供的一组内置辅助功能可能在未来得到改进。

其他事宜

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

2. FTS3 / 4“merge = X,Y”命令已被FTS5合并命令取代。

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

技术差异总结

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,依此类推)。使用此索引,FTS5能够及时为诸如“包含标记”A“的所有文档的集合”或“包含序列'Y Z'的所有文档的集合”提供及时答案。与单个令牌关联的实例列表称为“实例列表”。

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

  • FTS5能够将实例列表逐步加载到内存中,以减少内存使用量和峰值分配大小。FTS3 / 4经常将整个实例列表加载到内存中。
  • 处理具有多个标记的查询时,FTS5有时可以通过检查大型实例列表的子集来确定查询是否可以得到应答。FTS3 / 4几乎总是需要遍历整个实例列表。
  • 如果实例列表增长得如此之大以至于超过了SQLITE_MAX_LENGTH限制,FTS3 / 4将无法处理它。FTS5没有这个问题。由于这些原因,许多复杂的查询可能会使用更少的内存并使用FTS5运行得更快。FTS5与FTS3 / 4的其他不同之处在于:
  • FTS5支持“ORDER BY排名”,按照相关性递减的顺序返回结果。
  • FTS5具有一个API,允许用户为高级排名和文本处理应用程序创建自定义辅助功能。特殊的“等级”列可以映射到一个自定义的辅助函数,这样在查询中添加“ORDER BY等级”就可以按预期工作。
  • FTS5默认识别unicode分隔符和大小写等价。这也可以使用FTS3 / 4,但必须明确启用。
  • 查询语法已在必要时进行了修改,以消除歧义,并使查询条件中的特殊字符变得可能。
  • 默认情况下,FTS3 / 4偶尔会在用户执行的INSERT,UPDATE或DELETE语句中将两个或更多构成其全文索引的b-树合并在一起。这意味着在FTS3 / 4表上的任何操作可能会变得非常慢,因为FTS3 / 4可能无法预料地选择将两个或更多的大型b-树合并在一起。FTS5默认使用增量合并,这限制了在任何给定的INSERT,UPDATE或DELETE操作中可能发生的处理量。
 SQLite is in the Public Domain.

全文索引 | Full-Text Search相关

Sqlite

SQLite,是一款轻型的数据库,是遵守ACID的关系型数据库管理系统,它包含在一个相对小的C库中。它是D.RichardHipp建立的公有领域项目。它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合,比如 Tcl、C#、PHP、Java等,还有ODBC接口,同样比起Mysql、PostgreSQL这两款开源的世界著名数据库管理系统来

主页 https://sqlite.org/
源码 https://www.sqlite.org/src/
发布版本 3.21.0

Sqlite目录

1.C界面 | C Interface
2.C Interface: Session Module
3.CLI
4.数据库文件表 | Database File Format
5.数据类 | Datatypes
6.动态内存分配 | Dynamic Memory Allocation
7.外键约束 | Foreign Key Constraints
8.全文索引 | Full-Text Search
9.损坏方式 | How To Corrupt
10.JSON
11.语言 | Language
12.局限性 | Limits
13.锁定和并发 | Locking and Concurrency
14.其他 | Miscellaneous
15.PRAGMA Statements
16.查询计划程序 | Query Planner
17.R*Tree Module
18.RBU Extension
19.语法图 | Syntax Diagrams
20.Tcl Interface
21.虚拟表机制 | Virtual Table Mechanism
22.预写日志 | Write-Ahead Logging
23.SQL 教程
24.SQL 简介
25.SQL 语法
26.SQL DELETE 语句
27.SQL UPDATE 语句
28.SQL NOT NULL 约束
29.SQL 约束
30.SQL CREATE TABLE 语句
31.SQL CREATE DATABASE 语句
32.SQL INSERT INTO SELECT 语句
33.SQL SELECT INTO 语句
34.SQL CREATE VIEW、REPLACE VIEW、 DROP VIEW 语句
35.SQL AUTO INCREMENT 字段
36.SQL ALTER TABLE 语句
37.SQL 撤销索引、表以及数据库
38.SQL CREATE INDEX 语句
39.SQL DEFAULT 约束
40.SQL CHECK 约束
41.SQL FOREIGN KEY 约束
42.SQL PRIMARY KEY 约束
43.SQL UNIQUE 约束
44.SQL 通用数据类型
45.SQL ISNULL()、NVL()、IFNULL() 和 COALESCE() 函数
46.SQL NULL 值 – IS NULL 和 IS NOT NULL
47.SQL Server 和 MySQL 中的 Date 函数
48.SQL MS Access、MySQL 和 SQL Server 数据类型
49.SQL 函数
50.SQL 总结
51.SQL 主机
52.SQL 快速参考
53.SQL ROUND() 函数
54.SQL Server GETDATE() 函数
55.MySQL DATE_FORMAT() 函数
56.MySQL DATEDIFF() 函数
57.MySQL DATE_SUB() 函数
58.MySQL DATE_ADD() 函数
59.MySQL EXTRACT() 函数
60.MySQL DATE() 函数
61.MySQL CURTIME() 函数
62.MySQL CURDATE() 函数
63.MySQL NOW() 函数
64.SQL Server CONVERT() 函数
65.SQL Server DATEDIFF() 函数
66.SQL Server DATEADD() 函数
67.SQL Server DATEPART() 函数
68.SQLite 命令
69.SQLite 安装
70.SQLite 简介
71.SQLite 运算符
72.SQLite Select 语句
73.SQLite 删除表
74.SQLite 创建表
75.SQLite Insert 语句
76.SQLite 分离数据库
77.SQLite 附加数据库
78.SQLite 创建数据库
79.SQLite 数据类型
80.SQLite 语法
81.SQLite Order By
82.SQLite Limit 子句
83.SQLite Glob 子句
84.SQLite Like 子句
85.SQLite Delete 语句
86.SQLite Update 语句
87.SQLite AND/OR 运算符
88.SQLite Where 子句
89.SQLite 表达式
90.SQLite Distinct 关键字
91.SQLite Having 子句
92.SQLite Group By
93.SQLite Join
94.SQLite 约束
95.SQLite PRAGMA
96.SQLite 事务
97.SQLite 视图
98.SQLite Truncate Table
99.SQLite Alter 命令
100.SQLite Indexed By