小。速度很快。可靠。
选择任意三个选项。
SQLite FTS3和FTS4扩展

概述

FTS3和FTS4是SQLite虚拟表模块,允许用户执行对一组文档进行全文搜索。最常见的(也是最有效的)描述全文搜索的方式是“谷歌、雅虎和必应的做法用户输入一个术语或系列术语,可能由二进制运算符连接或组合成短语,全文查询系统会找到最佳的文档集考虑到用户的运算符和分组,匹配这些术语明确规定。本文描述了FTS3和FTS4的部署和使用。

FTS1和FTS2是SQLite的过时全文搜索模块。有已知的应该避免这些旧模块及其使用的问题。原始FTS3代码的一部分被贡献给SQLite项目作者:Scott Hess谷歌。现在是作为SQLite的一部分进行开发和维护。

1FTS3和FTS4简介

FTS3和FTS4扩展模块允许用户使用内置全文索引(以下简称“FTS表”)。全文索引允许用户高效地查询数据库中包含的所有行一个或多个单词(以下简称“标记”),即使表包含许多大型文档。

例如,如果"安然电子邮件数据集"插入到FTS表和普通SQLite表中使用以下SQL脚本创建:

使用fts3(内容文本)创建虚拟表淹没1;/*FTS3表格*/创建表格enrondata2(内容文本);/*普通桌子*/

然后可以执行以下两个查询中的任何一个,以查找数据库中包含单词“linux”(351)的文档。使用一个桌面PC硬件配置,FTS3表上的查询返回大约0.03秒,而查询普通表需要22.5秒。

SELECT count(*)FROM enrondata1 WHERE content MATCH“linux”;/*0.03秒*/SELECT count(*)FROM enrondata2 WHERE content LIKE“%linux%”;/*22.5秒*/

当然,上面的两个查询并不完全等同。例如LIKE查询匹配包含诸如“linuxophobe”等术语的行或“EnterpriseLinux”(碰巧,安然电子邮件数据集没有实际上包含任何此类术语),而FTS3表上的MATCH查询只选择包含“linux”作为离散标记的那些行。两者都有搜索是区分大小写的。FTS3表在上消耗了大约2006 MB而普通表只有1453MB。使用相同的用于执行上述SELECT查询的硬件配置,FTS3填写表格只需不到31分钟,而普通人只需25分钟表。

1.1.FTS3和FTS4之间的差异

FTS3和FTS4几乎相同。他们共享大部分代码,它们的接口是相同的。区别在于:

FTS4是对FTS3的增强。FTS3自SQLite以来一直可用版本3.5.0(2007-09-04)SQLite添加了FTS4的增强功能版本3.7.4(2010-12-07).

您应该在应用程序中使用哪个模块,FTS3或FTS4?FTS4是有时比FTS3快得多,甚至快几个数量级取决于查询,但在通常情况下,这两个查询的性能模块类似。FTS4还提供增强型匹配信息()输出,其中在对匹配操作。另一方面,在缺少matchinfo=fts3指令FTS4需要一点磁盘空间比FTS3多,尽管在大多数情况下只有两个磁盘空间的百分之一。

对于较新的应用,建议使用FTS4;尽管与旧版本兼容SQLite的版本很重要,那么FTS3通常也会起到同样的作用。

1.2.创建和销毁FTS表

与其他虚拟表类型一样,使用创建虚拟表格声明。模块名称,如下所示USING关键字为“fts3”或“fts4”。虚拟表模块参数可以保留为空,在这种情况下,使用一个用户定义的FTS表创建了名为“content”的列。或者,模块参数可以传递逗号分隔的列名列表。

如果列名称是作为FTS表的一部分显式提供的CREATE VIRTUAL TABLE语句,则可以选择数据类型名称为每列指定。这是纯语法糖FTS或SQLite内核不使用提供的类型名目的。这同样适用于与FTS列名-它们被解析,但不被系统使用或记录无论如何。

--创建一个名为“data”的FTS表,其中包含一列“content”:使用fts3()创建虚拟表数据;--创建一个名为“pages”的FTS表,其中包含三列:使用fts4(标题、关键字、正文)创建虚拟表格页面;--创建一个名为“mail”的FTS表,其中包含两列。数据类型--和列约束与每列一起指定。这些--被FTS和SQLite完全忽略。使用fts3创建虚拟表格邮件(主题VARCHAR(256)非空,正文文本检查(长度(正文)<10240));

除了列的列表外,传递给CREATE的模块参数用于创建FTS表的VIRTUAL TABLE语句可用于指定标记器。这是通过指定表单的字符串来完成的“tokenize=<tokenizer name><tokeni zer args>”代替列name,其中<标记器名称>是要使用的标记器的名称<tokenizer-args>是一个可选的以空格分隔的限定符列表传递给标记器实现。标记器规范可以是放置在列列表中的任何位置,但最多有一个标记器声明是允许用于每个CREATE VIRTUAL TABLE语句。请参见下文对于使用(如有必要,实现)标记器的详细描述。

--创建一个名为“papers”的FTS表,其中有两列使用
--象征性的“搬运工”。使用fts3(author,document,tokenize=porter)创建虚拟表格文件;--创建一个FTS表,其中只有一列“内容”,使用
--“简单”标记器。使用fts4创建虚拟表数据(标记化=简单);--创建一个FTS表,其中包含使用“icu”标记器的两列。
--限定符“en_AU”被传递给标记器实现使用fts3(a,b,tokenize=icu en_AU)创建虚拟表名称;

可以使用普通的DROP表格声明。例如:

--创建然后立即删除FTS4表。使用fts4()创建虚拟表数据;DROP TABLE数据;

1.3.填充FTS表格

FTS表使用插入,更新删除语句的方式与普通SQLite表相同。

以及用户命名的列(如果没有,则为“内容”列模块参数被指定为创建虚拟表格语句),每个FTS表都有一个“rowid”列。FTS的rowid表的行为与普通SQLite的rowid列相同表,但FTS表的rowid列中存储的值除外如果使用真空命令。对于FTS表,允许将“docid”与通常的“rowid”作为别名,“oid”和“_oid_”标识符。试图插入或更新具有表中已经存在的docid值是一个错误,就像它可能存在的那样使用普通SQLite表。

“docid”和普通SQLite之间还有一个细微的区别rowid列的别名。通常,如果INSERT或UPDATE语句为rowid列SQLite的两个或多个别名分配离散值写入INSERT或UPDATE中指定的最右边的值语句发送到数据库。但是,将非NULL值分配给插入或时使用“docid”和一个或多个SQLite rowid别名更新FTS表被视为错误。请参阅下面的示例。

--创建FTS表使用fts4(标题、正文)创建虚拟表格页面;--插入具有特定docid值的行。插入页面(docid、title、body)值(53,“主页”,“SQLite是一个软件…”);--插入一行并允许FTS使用与相同的算法分配docid值
--SQLite用于普通表。在这种情况下,新的docid将为54,
--一个大于表中当前最大的docid。插入页面(标题、正文)值(“下载”、“所有SQLite源代码…”);--更改刚刚插入的行的标题。更新页面SET title=“下载SQLite”WHERE rowid=54;--删除整个表内容。从页面删除;--以下是一个错误。不可能将非NULL值同时分配给
--FTS表的rowid和docid列。插入页面(rowid、docid、title、body)值(1、2、'A title'、'A document body');

为了支持全文查询,FTS维护了一个映射的反向索引从数据集中出现的每个唯一术语或单词到位置它出现在表内容中。对于好奇的人来说完整描述数据结构用于存储数据库文件中的索引如下所示。的一个功能这个数据结构是数据库在任何时候都可能不包含一个索引b树,但有几个不同的增量b树插入、更新和删除行时合并。这项技术提高了写入FTS表时的性能,但会导致使用索引的全文查询。评估特殊“优化”命令,的SQL语句表单“INSERT INTO<fts table>(<fts table>)VALUES('优化')”,导致FTS将所有现有的索引b树合并为单个大型包含整个索引的b-tree。这可能是一个昂贵的操作,但可能会加速未来的查询。

例如,要优化名为“文档”:

--优化FTS表“docs”的内部结构。插入文档值(“优化”);

对某些人来说,上述语句可能在语法上不正确。请参阅描述简单的fts查询以获得解释。

还有另一种不推荐使用的方法用于调用优化使用SELECT语句进行操作。新代码应使用语句与上述INSERT类似,以优化FTS结构。

1.4.简单FTS查询

对于所有其他SQLite表,无论是虚拟表还是其他表,都会检索数据使用选择声明。

使用两个SELECT语句可以有效地查询FTS表不同形式:

如果这两种查询策略都不能使用对FTS表的查询是使用对整个表的线性扫描来实现的表。如果表包含大量数据,这可能是不切实际的方法(本页上的第一个示例显示使用现代PC扫描1.5 GB的数据大约需要30秒)。

--本块中的示例采用以下FTS表:使用fts3(主题、正文)创建虚拟表格邮件;从邮件中选择*,其中rowid=15;--速度很快。Rowid查找。SELECT*FROM mail WHERE body MATCH“sqlite”;--速度很快。全文查询。SELECT*FROM mail WHERE mail MATCH“search”(从邮件中选择邮件匹配“搜索”);--速度很快。全文查询。从邮件中选择*,其中rowid介于15和20之间;--速度很快。Rowid查找。SELECT*FROM mail WHERE subject='database';--慢点。线性扫描。SELECT*FROM mail WHERE subject MATCH‘database’;--速度很快。全文查询。

在上述所有全文查询中,MATCH的右操作数操作符是由单个术语组成的字符串。在这种情况下,匹配对于包含一个或多个文档,表达式的计算结果为true指定单词的实例(“sqlite”、“search”或“database”,具体取决于你看哪个例子)。将单个术语指定为右侧MATCH运算符的操作数产生最简单、最常见的类型可以进行全文查询。然而,可能会有更复杂的查询,包括短语搜索、术语前缀搜索和文档搜索包含在每个定义的接近范围内出现的术语组合其他。查询全文索引的各种方法包括如下所述.

通常,全文查询区分大小写。然而,这取决于特定的标记器由FTS表使用正在被查询。请参阅有关标记器了解详细信息。

上面的段落指出,带有简单术语的MATCH运算符对于包含指定的术语。在这种情况下,“文档”可以指存储在FTS表行的单个列中的数据,或存储到内容一行中所有列的,具体取决于用作MATCH运算符的左侧操作数。如果标识符指定为MATCH运算符的左侧操作数是FTS表列名,则必须包含搜索词的文档为值存储在指定的列中。但是,如果标识符是名称FTS的桌子本身,则MATCH运算符的计算结果为true对于FTS表中任何列都包含搜索的每一行术语。以下示例演示了这一点:

--模式示例使用fts3(主题、正文)创建虚拟表格邮件;--示例表填充插入邮件(docid、主题、正文)值(1,“软件反馈”,“发现速度太慢”);插入邮件(docid、主题、正文)值(2,“软件反馈”,“无反馈”);插入邮件(docid、subject、body)值(3,“午餐点得慢”,“是软件问题”);--示例查询从邮件中选择*WHERE主题匹配“软件”;--选择第1行和第2行SELECT*FROM mail WHERE body MATCH“反馈”;--选择第2行SELECT*FROM mail WHERE mail MATCH“软件”;--选择第1、2和3行SELECT*FROM mail WHERE mail MATCH“慢速”;--选择第1行和第3行

乍一看,上面示例中的最后两个全文查询似乎语法不正确,因为有一个表名(“mail”)用作SQL表达式。这是可以接受的原因是每个FTS表实际上有一个隐藏具有相同名称的列作为表本身(在本例中为“mail”)。存储在此列对应用程序没有意义,但可以用作MATCH运算符的左侧操作数。此特殊列也可以是作为参数传递给FTS辅助功能.

以下示例说明了上述内容。表达式“docs”,“文档.docs”和“main.docs.docs”均指列“文档”。然而表达式“main.docs”不引用任何列。它可以用来引用表,但在其中的上下文中不允许使用表名它在下面使用。

--模式示例使用fts4创建虚拟表格文档(内容);--示例查询SELECT*FROM docs WHERE docs MATCH'sqlite';--好的。SELECT*FROM docs WHERE docs.docs MATCH'sqlite';--好的。SELECT*FROM docs WHERE main.docs.docs MATCH“sqlite”;--好的。从文档中选择*WHERE main.docs匹配“sqlite”;--错误。

1.5.总结

从用户的角度来看,FTS表类似于普通SQLite表格有多种方式。可以在中添加、修改和删除数据使用INSERT、UPDATE和DELETE命令从FTS表可能是普通桌子。类似地,可以使用SELECT命令查询数据。下表总结了FTS之间的差异和普通表格:

  1. 与所有虚拟表类型一样,不可能创建索引或附加到FTS表的触发器。也不可能使用ALTER TABLE命令向FTS表添加额外的列(尽管可以使用ALTER TABLE以重命名FTS表)。

  2. 作为“CREATE VIRTUAL TABLE”语句的一部分指定的数据类型完全忽略用于创建FTS表的。而不是应用类型的常规规则密切关系到插入的值,所有插入到FTS表列中的值(特殊行ID除外列)在存储之前转换为TEXT类型。

  3. FTS表允许使用特殊别名“docid”来引用所有支持的rowid列虚拟表.

  4. 这个FTS匹配运算符支持基于内置全文索引。

  5. 这个FTS辅助功能,片段(),偏移()、和匹配信息()可用于支持全文查询。

  6. 每个FTS表都有一个隐藏的列使用与表本身同名。每个行中包含的值隐藏列是一个blob,它仅用作匹配运算符,或作为一个运算符的最左侧参数FTS辅助功能.

2编译和启用FTS3和FTS4

尽管FTS3和FTS4包含在SQLite核心源代码中,但它们不是默认情况下启用。要构建启用FTS功能的SQLite,请定义预处理器宏SQLITE_ENABLE_FTS3数据库编译时。新应用程序还应定义SQLITE_ENABLE_FTS3_PARENTHESIS公司宏以启用增强的查询语法(见下文)。通常,这是通过添加以下两个开关切换到编译器命令行:

-DSQLITE_ENABLE_FTS3-DSQLITE_ENABLE_FTS3_区域

请注意,启用FTS3也会使FTS4可用。没有单独的SQLITE_ENABLE_FTS4编译时间选项。SQLite的构建要么支持FTS3和FTS4都支持或两者都不支持。

如果使用基于合并autoconf的构建系统,请设置CPPFLAGS运行“configure”脚本时的环境变量很容易设置这些宏的方法。例如,以下命令:

CPPFLAGS=“-DSQLITE_ENABLE_FTS3-DSQLIT_ENABLE_FTS3_PARENTHESIS”/配置<配置选项>

哪里<配置选项>这些选项通常传递给配置脚本(如果有)。

因为FTS3和FTS4是虚拟表,所以SQLITE_ENABLE_FTS3数据库编译时间选项与不兼容SQLITE_OMIT_VIRTUALTABLE数据库选项。

如果SQLite的构建不包括FTS模块,则任何准备用于创建FTS3或FTS4表或删除或访问现有FTS表格无论如何都会失败。返回的错误消息类似改为“无此类模块:ftsN”(其中N为3或4)。

如果C版本的ICU图书馆如果可用,则FTS也可以使用SQLITE_ENABLE_ICU进行编译预处理器宏已定义。使用此宏编译可以启用FTS标记器使用ICU库将文档拆分为术语(单词)使用指定语言和地区的约定。

-DSQLITE_ENABLE_ICU

三。全文索引查询

关于FTS表最有用的是查询使用内置全文索引执行。全文查询包括通过指定形式的子句执行“<column>MATCH<全文查询表达式>”作为WHERE的一部分从FTS表中读取数据的SELECT语句的子句。简单FTS查询返回所有包含上述给定术语。在那次讨论中,右手边假定MATCH运算符的操作数是由单一条款。本节介绍支持的更复杂的查询类型以及如何通过指定更多复杂查询表达式作为MATCH运算符的右操作数。

FTS表支持三种基本查询类型:

--虚拟表声明使用fts3(标题、正文)创建虚拟表格文档;--查询包含术语“linux”的所有文档:SELECT*FROM docs WHERE docs MATCH“linux”;--查询包含前缀为“lin”的术语的所有文档。这将匹配
--包含“linux”的所有文档,以及包含术语“线性”的文档,
--“链接器”、“语言”等。从文档中选择*,其中文档匹配“lin*”;
--查询数据库中出现术语“linux”的文档
--文档标题,并且术语“问题”出现在任一标题中
--或文档正文。SELECT*FROM docs WHERE docs MATCH“标题:linux问题”;--查询数据库中出现术语“linux”的文档
--文档标题,并且术语“驱动程序”出现在文档正文中
--(标题中也可能出现“driver”,但仅此一点并不能满足
--查询条件)。SELECT*FROM docs WHERE body MATCH'标题:linux驱动程序';
--“linux”是至少一个标记的第一个标记的所有文档
--列。SELECT*FROM docs WHERE docs MATCH“^linux”;--“title”列中第一个标记以“lin”开头的所有文档。SELECT*FROM docs WHERE body MATCH'标题:^lin*';
--查询包含短语“linux应用程序”的所有文档。SELECT*FROM docs WHERE docs MATCH“linux应用程序”;--查询包含与“lin*app*”匹配的短语的所有文档。以及
--“linux应用程序”,这将匹配常见短语,如“油毡设备”
--或“联系学徒”。SELECT*FROM docs WHERE docs MATCH“”lin*app*“”;
--虚拟表声明。使用fts4()创建虚拟表格文档;--虚拟表数据。INSERT INTO docs VALUES(“SQLite是一个符合ACID的嵌入式关系数据库管理系统”);--使用搜索包含术语“sqlite”和“database”的文档
--不超过10个介入条款。这与中的唯一文档匹配
--表文档(因为“SQLite”和“database”之间只有六个术语
--在文档中).SELECT*FROM docs WHERE docs MATCH“sqlite NEAR database”;--使用搜索包含术语“sqlite”和“database”的文档
--不超过6个介入条款。这也与中的唯一文档匹配
--表文档。请注意,术语在文档中的出现顺序
--不必与它们在查询中的显示顺序相同。SELECT*FROM docs WHERE docs MATCH“database NEAR/6 sqlite”;--使用搜索包含术语“sqlite”和“database”的文档
--不超过5个介入术语。此查询不匹配任何文档。SELECT*FROM docs WHERE docs MATCH“database NEAR/5 sqlite”;--搜索包含短语“ACID兼容”和术语的文档
--“数据库”,两个词之间的分隔不超过2个。这与
--存储在表docs中的文档。SELECT*FROM docs WHERE docs MATCH‘数据库NEAR/2‘ACID兼容’’;--搜索包含短语“ACID兼容”和术语的文档
--“sqlite”,两个词之间的分隔不超过2个。这也与
--表docs中存储的唯一文档。SELECT*FROM docs WHERE docs MATCH'“ACID兼容”NEAR/2 sqlite';
--以下查询选择包含术语实例的文档
--“sqlite”由两个或两个以下术语与术语“acid”的实例分隔开,
--它又被两个或更少的术语从术语实例中分隔开
--“关系型”。SELECT*FROM docs WHERE docs MATCH“sqlite NEAR/2 acid NEAR/2 relational”;--此查询不匹配任何文档。有一个术语“sqlite”的实例
--与“酸”的实例足够接近,但距离不够近
--到术语“关系”的实例。SELECT*FROM docs WHERE docs MATCH“acid NEAR/2 sqlite NEAR/2 relational”;

短语和NEAR查询不能跨越一行中的多个列。

上述三种基本查询类型可用于查询全文与指定条件匹配的文档集的索引。使用FTS查询表达式语言,可以执行各种设置对基本查询结果的操作。目前有三个支持的操作:

FTS模块可以编译为使用两个略有不同的版本之一全文查询语法、“标准”查询语法和“增强”查询语法。描述了基本术语、术语前缀、短语和NEAR查询以上两个版本的语法都相同。集合的方式指定的操作略有不同。以下两小节描述两个查询语法中与set操作相关的部分。请参阅如何编译fts用于编写注释。

3.1.使用增强的查询语法设置操作

增强的查询语法支持AND、OR和NOT二进制集运算符。运算符的两个操作数中的每一个都可以是基本FTS查询,或者另一个AND、OR或NOT设置操作的结果。必须输入运算符使用大写字母。否则,它们被解释为基本术语查询而不是设置运算符。

可以隐式指定AND运算符。如果出现两个基本查询在FTS查询字符串中没有操作符分隔它们,结果如下就好像这两个基本查询是由AND操作符分隔的一样。例如,查询表达式“隐式运算符”更简洁“隐式AND运算符”的版本。

--虚拟表声明使用fts3()创建虚拟表格文档;--虚拟表数据INSERT INTO docs(docid,content)VALUES(1,“数据库是软件系统”);INSERT INTO文档(docid,content)VALUES(2,“sqlite是一个软件系统”);INSERT INTO docs(docid,content)VALUES(3,‘sqlite是一个数据库’);--返回包含术语“sqlite”的文档集
--术语“数据库”。此查询将仅返回docid为3的文档。SELECT*FROM docs WHERE docs MATCH“sqlite AND database”;--同样,返回包含“sqlite”和
--“数据库”。这一次,使用隐式AND运算符。同样,文档
--3是此查询匹配的唯一文档。SELECT*FROM docs WHERE docs MATCH“database sqlite”;--查询包含“sqlite”或“database”的文档集。
--此查询将匹配数据库中的所有三个文档。SELECT*FROM docs WHERE docs MATCH“sqlite OR database”;--查询包含术语“数据库”但不包含
--术语“sqlite”。文档1是唯一符合此条件的文档。SELECT*FROM docs WHERE docs MATCH“database NOT sqlite”;--以下查询不匹配任何文档。因为“and”是小写字母,
--它被解释为基本术语查询,而不是运算符。操作员必须
--使用大写字母指定。实际上,此查询将匹配任何文档
--至少包含一次“数据库”、“和”和“sqlite”这三个术语中的每一个。
--上述示例数据中没有文档符合此条件。SELECT*FROM docs WHERE docs MATCH‘database and sqlite’;

以上示例都使用基本全文项查询作为演示了set操作。也可以使用短语和NEAR查询,和其他集合运算的结果一样。当多组操作时在FTS查询中,运算符的优先级如下:

操作员增强的查询语法优先级
不是最高优先级(最紧密的分组)。
最低优先级(最松散的分组)。

使用增强查询语法时,可以使用括号来覆盖各种运算符的默认优先级。例如:

--返回与包含
--两个术语“sqlite”和“database”,和/或包含术语“library”。SELECT docid FROM docs WHERE docs MATCH“sqlite AND database OR library”;--此查询与上述查询等效。从文档中选择文档ID,其中文档匹配“sqlite和数据库”联合国SELECT docid FROM docs WHERE docs MATCH'library';--查询包含术语“linux”的文档集,至少
--短语“sqlite数据库”和“sqlite库”之一。SELECT docid FROM docs WHERE docs MATCH’(“sqlite数据库”或“sqlite库”)AND linux’;--此查询与上述查询等效。从文档中选择文档匹配“linux”的文档横断从中选择docid(SELECT docid FROM docs WHERE docs MATCH“”sqlite library“”联合国SELECT docid FROM docs WHERE docs MATCH“”sqlite数据库“”);

3.2.使用标准查询语法设置操作

使用标准查询语法的FTS查询集操作类似,但不同,使用增强的查询语法设置操作。那里有四个不同点,如下所示:

  1. 仅支持AND运算符的隐式版本。将字符串“AND”指定为标准查询语法查询的一部分是解释为对包含术语的文档集的术语查询“和”。

  1. 不支持括号。

  1. 不支持NOT运算符。而不是NOT运算符,标准查询语法支持一元“-”运算符可应用于基本术语和术语前缀查询(但不适用于短语或NEAR查询)。具有一元“-”运算符的术语或术语前缀附加到它的操作数可能不会显示为OR运算符的操作数。FTS公司查询不能完全由一元术语或术语前缀查询组成附加的“-”运算符。

--搜索包含术语“sqlite”但确实包含的文档集
--不包含术语“数据库”。SELECT*FROM docs WHERE docs MATCH'sqlite-database';从文档中选择*FROM文档,其中文档匹配'sqlite-database';
  1. 集合操作的相对优先级不同。特别是,使用标准查询语法,“OR”操作符有一个优先级高于“AND”。使用时运算符的优先级标准查询语法为:

操作员标准查询语法优先级
一元“-”最高优先级(最紧密的分组)。
最低优先级(最松散的分组)。
  1. 以下示例说明了使用标准的运算符的优先级查询语法:
--搜索至少包含一个术语“数据库”的文档
--和“sqlite”,还包含术语“库”。由于差异
--在运算符优先顺序中,此查询将具有不同的解释,使用
--增强的查询语法。SELECT*FROM docs WHERE docs MATCH“sqlite OR database library”;

4.辅助功能-代码段、偏移量和Matchinfo

FTS3和FTS4模块提供了三个可能有用的特殊SQL标量函数全文查询系统的开发人员:“snippet”、“offset”和“matchinfo”。“snippet”和“offset”函数的目的是允许用户标识所查询术语在返回文档中的位置。“matchinfo”功能为用户提供可能有用的指标用于根据相关性筛选或排序查询结果。

所有三个特殊SQL标量函数的第一个参数必须是FTS隐藏列函数所在的FTS表的应用于FTS隐藏列是在上找到的自动生成的列与FTS表本身同名的所有FTS表。例如,给定一个名为“mail”的FTS表:

SELECT offset(mail)FROM mail WHERE mail MATCH<全文查询表达式>;SELECT snippet(mail)FROM mail WHERE mail MATCH<全文查询表达式>;SELECT matchinfo(mail)FROM mail WHERE mail MATCH<全文查询表达式>;

这三个辅助函数仅在SELECT语句中有用使用FTS表的全文索引。如果在使用“按行查询”或“线性扫描”策略,然后是代码片段和offset都返回空字符串,matchinfo函数返回blob值大小为零个字节。

所有三个辅助函数都从中提取一组“匹配短语”要使用的FTS查询表达式。的匹配短语集给定的查询包含所有短语(包括未加引号的标记和标记前缀),但前缀为一元“-”运算符(标准语法)或是子表达式的一部分用作NOT运算符的右侧操作数。

在以下附带条件下,FTS表中的每个系列代币匹配查询表达式中的一个匹配短语,称为“短语匹配”:

  1. 如果匹配短语是由连接的一系列短语的一部分FTS查询表达式中的NEAR运算符,然后每个短语匹配必须与相关的其他短语匹配足够接近类型以满足NEAR条件。
  2. 如果FTS查询中的匹配短语仅限于匹配指定FTS表列中的数据,则只有短语匹配发生在该列中。

4.1.偏移功能

对于使用全文索引的SELECT查询,offsets()函数返回包含一系列空格分隔整数的文本值。对于每个学期短语匹配当前行的,返回的列表中有四个整数。每组四个整数为解释如下:

整数解释
0术语实例出现在(0表示FTS表最左边的列,1表示下一个最左边的,等等)。
1全文查询中匹配术语的术语编号表达式。查询表达式中的术语从开始编号按照它们发生的顺序从0开始。
2列中匹配项的字节偏移量。
匹配项的大小(以字节为单位)。

以下块包含使用偏移功能的示例。

使用fts3(主题、正文)创建虚拟表格邮件;插入邮件值('hello world','This message is a hello world message.');在邮件中插入值(“紧急:严重”,“此邮件被视为更严重的邮件”);--以下查询返回一行(因为它只匹配第一行
--表“mail”中的条目。偏移函数返回的文本为
-- "0 0 6 5 1 0 24 5".
--
--结果中第一组四个整数表示列0
--在字节偏移量6处包含术语0(“world”)的实例。术语实例
--大小为5字节。第二组四个整数表示第1列
--匹配行的包含术语0(“world”)在字节偏移量处的实例
-- 24. 同样,术语实例的大小为5个字节。从邮件中选择偏移量(邮件),邮件与“世界”匹配;--以下查询返回的结果也只匹配表“mail”中的第一行。
--在这种情况下,返回的文本是“1 0 5 7 1 0 30 7”。SELECT offset(mail)FROM mail WHERE mail MATCH‘message’;--以下查询与表“mail”中的第二行匹配。它返回
--文本“1 0 28 7 1 1 36 4”。仅出现“严重”和“邮件”
--是短语“严重邮件”实例的一部分;这个
--忽略其他出现的“严重”和“邮件”。从邮件中选择偏移量(邮件),其中邮件匹配“严重邮件”;

4.2.代码段函数

snippet函数用于创建文档文本的格式化片段作为全文查询结果报告的一部分显示。snippet函数可以在一到六个参数之间传递,如下所示:

参数默认值描述
0不适用代码段函数的第一个参数必须始终是FTS隐藏列正在查询的FTS表,并从中获取代码段。这个FTS隐藏列是自动生成的列,与FTS表格本身。
1“<b>”“开始匹配”文本。
2“</b>”“结束匹配”文本。
“<b>…</b>”“省略号”文本。
4-1用于提取返回片段的FTS表列号中的文本。列从左到右从零。负值表示可以提取文本来自任何列。
5-15此整数参数的绝对值用作要包含在返回文本中的(近似)标记数值。最大允许绝对值为64。的价值这个论点被称为N个在下面的讨论中。

snippet函数首先尝试查找包含以下内容的文本片段属于|N个|当前行中包含至少一个短语的标记匹配当前行中匹配的每个匹配短语,哪里|N个|是传递给代码段函数。如果存储在单列中的文本包含小于|N个|标记,然后考虑整个列值。文本片段不能跨多列。

如果可以找到这样的文本片段,则返回如下内容修改:

如果可以找到多个这样的片段,那么包含支持更多的“额外”短语匹配。的开始所选文本片段可以向前或向后移动几个标记尝试将短语匹配集中到碎片。

假设N个是正值,如果找不到碎片包含与每个可匹配短语相对应的短语匹配函数试图找到大约N个/2个代币它们之间包含每个匹配短语的至少一个短语匹配与当前行匹配。如果失败,将尝试查找三个的碎片N个/每个3个代币,最后4个N个/4个令牌碎片。如果找不到包含必填短语匹配N个/提供4个代币选择了最佳覆盖范围。

如果N个为负值,找不到单个片段包含所需短语匹配项的snippet函数将搜索两块碎片|N个|每个代币,然后是三个,然后是四个。换句话说,如果指定值为N个是负数,大小如果需要多个碎片,碎片的数量不会减少以提供所需的短语匹配覆盖率。

碎片已经找到,在那里介于如上述段落所述,将两个和四个连接在一起按排序顺序,用“省略号”文本分隔它们。三个人前面列举的修改是在对文本进行修改之前执行的返回。

注意:在这个示例块中,换行符和空白字符有已插入FTS表中的文档中,并且预期SQL注释中描述的结果。这样做只是为了增强可读性,它们不会出现在实际的SQLite命令或输出中。

--创建并填充FTS表。使用fts4()创建虚拟表格文本;插入文本值('11月30日至12月1日,温度下降2-3oC。上部冷却,最低温度14-16oC并在其他地方冷却,最低温度17-20℃。山顶从冷到非常冷,最低温度6-12oC。东北风15-30公里/小时。之后,气温增加。东北风15-30 km/hr。');--以下查询返回文本值:
--
--“<b>…</b>在其他地方冷却,最低温度17-20oC
--山顶寒冷,最低温度6</b> “。
--从文本中选择片段(文本),其中文本匹配“cold”;--以下查询返回文本值:
--
--“……上部,[最低][温度]14-16oC,其他地方冷却,
--[最低][温度]17-20oC。冷。。。"
--从文本中选择代码段(文本,'[',']','…')WHERE text MATCH'“min*tem*”'

4.3.Matchinfo函数

matchinfo函数返回blob值。如果在查询中使用不使用全文索引(“按rowid查询”或“线性扫描”),那么blob的大小为零字节。否则,blob由零组成或机器字节顺序中的多个32位无符号整数。确切的数字返回数组中整数的数量取决于查询和值传递给matchinfo函数的第二个参数(如果有)。

matchinfo函数是用一个或两个参数调用的。至于所有辅助函数,第一个参数必须是特殊的FTS隐藏列。如果指定了第二个参数,则该参数必须是文本值仅包含字符“p”、“c”、“n”、“a”、“l”、“s”、“x”、“y”和“b”。如果没有明确提供第二个参数,则默认为“pcx”。这个第二个参数被称为下面的“格式字符串”。

matchinfo格式字符串中的字符从左到右进行处理。格式字符串中的每个字符都会导致一个或多个32位无符号要添加到返回数组中的整数值。中的“值”列下表包含附加到每个支持的格式字符串字符的输出缓冲区。在公式中鉴于,是FTS表中的列数,以及短语是的数字可匹配短语在里面查询。

字符描述
第页1查询中可匹配的短语数。
c(c)1FTS中用户定义的列数表(即不包括docid或FTS隐藏列).
x个3 **短语 对于短语和表列的每个不同组合以下三个值:
  • 在当前行中,短语出现在列。
  • 短语出现在中的列中的总次数FTS表中的所有行。
  • FTS表中包含列包含短语的至少一个实例。
第一组三个值对应于最左边的列的(第0列)和中最左边的可匹配短语查询(短语0)。如果表有多个列,则第二列输出数组中的一组三个值对应于短语0和第1列。后面是短语0、列2等桌子。依次类推短语1,第0列,然后是短语1,第1列等。换句话说,短语出现的数据第页在里面c(c)可以使用以下公式进行计算:
hits_this_row=数组[3*(c+p*cols)+0]hits_all_rows=数组[3*(c+p*cols)+1]docs_with_hits=数组[3*(c+p*cols)+2]
*短语 对于短语和表列的每个不同组合列中出现的可用短语匹配项的数量。这是通常与matchinfo“x”标志。但是对于作为子表达式一部分的任何短语,“y”标志为零与当前行不匹配。这对包含作为OR后代的AND运算符的表达式操作员。例如,考虑以下表达式:
a或(b和c)
以及文档:
“a c d”
这个matchinfo“x”标志将报告短语“a”和“c”的一次点击。然而,“y”指令将“c”的点击数报告为零它是与文档(b和c)不匹配的子表达式的一部分。对于不包含从OR派生的AND运算符的查询运算符,“y”返回的结果值始终与x返回的。

整数值数组中的第一个值对应于表的最左侧列(列0)和查询中的第一个短语(短语0)。与其他列/短语组合对应的值可以使用以下公式定位:

hits_for_phrase_p_columnC=数组[c+p*cols]
对于使用OR表达式或使用LIMIT或return的查询许多行,“y”matchinfo选项可能比“x”更快。
b条((第+31列)/32列)*短语 matchinfo'b'标志为matchinfo“y”标志,但在更多紧凑的形式。“b”不是精确的点击数,而是提供一个每个短语/列组合的布尔标志。如果短语出现在列至少一次(即,如果“y”的相应整数输出将非零),则设置相应的标志。否则清除。

如果表包含32列或更少,则为输出一个无符号整数查询中的每个短语。如果短语在第0列中至少出现一次。第二个最低有效位是设置短语是否在第1列中出现一次或多次。等等。

如果表有32列以上,则会向输出中添加一个额外的整数每增加32列或不足32列,每增加一个短语。整数与同一短语相对应的词聚在一起。例如,如果一个表使用45列查询两个短语,输出4个整数。第一个对应于表的短语0和列0-31。第二个整数包含短语0和列32-44等的数据。

例如,如果nCol是表中的列数,则确定短语p出现在列c中:

p_is_in_c=数组[p*((nCol+31)/32)]&(1<<(c%32))
n个1FTS4表中的行数。此值为仅在查询FTS4表时可用,而不是FTS3。
对于每列存储在列中的文本值中的标记(考虑FTS4表格)。此值仅在查询FTS4表时可用,不是FTS3。
对于每一列FTS4表,以令牌表示。此值仅在查询时可用FTS4表格,而非FTS3。并且仅当“matchinfo=fts3”指令不是指定为用于创建的“CREATE VIRTUAL TABLE”语句的一部分FTS4表格。
对于每列,最长的短语的子序列与列值的共同点匹配带有查询文本。例如,如果表列包含文本“a b c d e”,查询为“a c”d e“”,则为最长的常见的子序列是2(短语“c”后跟短语“de”)。

例如:

--创建并填充包含两列的FTS4表:使用fts4(a,b)创建虚拟表t1;INSERT INTO t1 VALUES(“事务默认模型默认”,“非事务读取”);INSERT INTO t1 VALUES(“默认事务”,“存在这些语义”);插入t1值(“单个请求”,“默认数据”);--在下面的查询中,没有指定格式字符串,因此它是默认的
--到“pcx”。因此,它返回由单个blob组成的单行
--值80字节(20个32位整数-1表示“p”,1表示“c”和
--“x”为3*2*3)。如果每个4字节块解释blob--作为按机器字节顺序排列的无符号整数,这些值将是:
--
--     3 2  1 3 2  0 1 1  1 2 2  0 1 1  0 0 0  1 1 1
--
--返回的行对应于插入表t1的第二个条目。
--blob中的前两个整数显示查询包含三个
--短语和被查询的表有两列。下一块
--三个整数描述列0(在本例中为列“a”)和短语
--0(在本例中为“默认”)。当前行包含1次“默认”点击
--在第0列中,列中发生的“默认”共命中3次
--任何表行的0。三次点击分布在两个不同的行上。
--
--下一组三个整数(0 1 1)与“默认”匹配
--在表的第1列中(此行中为0,所有行中为1,分布在
--1行)。
--SELECT matchinfo(t1)FROM t1 WHERE t1 MATCH'默认事务“these semantics”';--此查询的格式字符串为“ns”。因此,输出数组将
--包含3个整数值-1表示“n”,2表示“s”。查询返回
--两行(表中的前两行匹配)。返回的值为:
--
--     3  1 1
--     3  2 0
--
--为这两行返回的matchinfo数组中的第一个值是3(
--表中的行数)。以下两个值是长度
--每个列中短语匹配的最长公共子序列。从t1中选择匹配信息(t1,'ns'),其中t1匹配“默认事务”;

matchinfo函数比snippet或offset快得多功能。这是因为snippet和offset的实现需要从磁盘检索正在分析的文档,而matchinfo所需的所有数据都可以作为相同部分的一部分使用实现全文查询所需的全文索引的自身。这意味着在以下两个查询中,第一个可能是比秒快一个数量级:

SELECT docid,matchinfo(tbl)FROM tbl WHERE tbl MATCH<查询表达式>;SELECT docid,offset(tbl)FROM tbl WHERE tbl MATCH<查询表达式>;

matchinfo函数提供计算所需的所有信息概率“bagof-words”相关性得分,如奥卡皮BM25/BM25F那可能用于在全文搜索应用程序中对结果进行排序。本附录A文档,“搜索应用程序提示“,包含使用matchinfo()函数。

5Fts4aux-直接访问全文索引

截止日期版本3.7.6(2011-04-12),SQLite包含一个新的虚拟表模块,名为“fts4aux”,可用于检查现有直接FTS表。尽管它的名字是fts4aux,但它与FTS3同样适用与FTS4表格一样。Fts4aux表是只读的。唯一的修改fts4aux表内容的方法是修改关联FTS表的内容。fts4aux模块自动包含在所有包含FTS的构建.

fts4aux虚拟表是用一个或两个参数构造的。什么时候?与单个参数一起使用时,该参数是将用于访问的FTS表。要访问其他数据库(例如,创建一个TEMP fts4aux表,该表将访问MAIN数据库中的FTS3表)使用双参数形式,并给出第一个参数中的目标数据库的名称(例如:“main”)和名称作为第二个参数。(双参数形式为SQLite添加了fts4aux版本3.7.17(2013-05-20)并且将在以前的版本中引发错误。)例如:

--创建FTS4表格使用fts4(x,y)创建虚拟表格ft;--创建fts4aux表以访问表“ft”的全文索引使用fts4aux(ft)创建虚拟表ft_terms;--创建一个TEMP fts4aux表,访问“main”中的“ft”表使用fts4aux(main,ft)创建虚拟表格temp.ft_terms_2;

对于FTS表中的每个术语,都有2到N+1行在fts4aux表中,其中N是关联的FTS表。fts4aux表始终具有相同的四列,如下所示,从左到右:

列名列内容
学期包含此行的术语文本。
科尔此列可以包含文本值“*”(即单个字符,U+002a)或介于0和N-1之间的整数,其中N是再次输入相应FTS表中用户定义的列数。
文件此列始终包含大于零的整数值。

如果“col”列包含值“*”,则此列包含FTS表中至少包含一行的行数术语的实例(在任何列中)。如果列包含整数值,则此列包含FTS表中在由标识的列中包含术语的至少一个实例col值。与往常一样,FTS表的列被编号从左到右,从零开始。
个事件此列还始终包含大于零的整数值。

如果“col”列包含值“*”,则此列包含该术语在所有行中的实例总数FTS表格(任意列)。否则,如果col包含一个整数值,则此列包含出现在FTS表列中的术语,由列标识值。
类语言的(隐藏) 此列确定类语言的用于从FTS3/4表中提取词汇。

languageid的默认值为0。如果使用其他语言在WHERE子句约束中指定,则该替代项为使用而不是0。每个查询只能有一个语言ID。换句话说,WHERE子句不能包含范围约束或languageid上的IN运算符。

例如,使用上面创建的表:

插入ft(x,y)值(“苹果香蕉”、“樱桃”);插入ft(x,y)值(“香蕉日期”,“樱桃”);插入ft(x,y)值(‘樱桃接骨木’,‘接骨木');--以下查询返回此数据:
--
--苹果|*|1|1
--苹果|0 |1
--香蕉|*|2|2
--香蕉|0|2|2
--樱桃|*|3|3
--樱桃|0|1|1
--樱桃|1|2|2
--日期|*|1|2
--日期|0 |1 |2
--接骨木|*|1|2
--接骨木|0|1|1
--接骨木|1|1
--从ft_terms中选择术语、列、文档、出现次数;

在示例中,“term”列中的值都是小写的,即使它们以大小写混合的形式插入表“ft”。这是因为fts4aux表包含从文档文本中提取的术语标记化器在这种情况下,由于表“ft”使用简单标记器,这意味着所有条款都被折叠为小写。此外,(例如)没有列为“term”的行设置为“apple”,列“col”设置为1。因为没有实例对于第1列中的术语“苹果”,fts4aux表中没有任何行。

在事务处理期间,写入FTS表的一些数据可能是缓存在内存中并仅在事务坚信的。然而,fts4aux模块的实现只能从数据库中读取数据。实际上,这意味着如果fts4aux从关联FTS表已修改,查询结果可能会反映只有所做更改的子集(可能为空)。

6FTS4选项

如果“CREATE VIRTUAL TABLE”语句指定模块FTS4(而不是FTS3),然后是特殊指令-FTS4选项-类似于“tokenize=*”选项也可能出现在列名的位置。FTS4选件包括选项名称,后跟“=”字符,然后是选项值。选项值可以用单引号或双引号括起来嵌入的引号字符以与SQL文本相同的方式进行转义。那里“=”字符两边不能有空格。例如,要创建FTS4表格,将选项“matchinfo”的值设置为“fts3”:

--创建一个简化的打印FTS4表。使用fts4创建虚拟表格文件(作者、文档、匹配信息=fts3);

FTS4目前支持以下选项:

选项解释
压缩压缩选项用于指定压缩函数。这是一个错误指定压缩函数而不指定解压缩功能。请参见下文了解详细信息。
内容内容允许索引的文本存储在不同于FTS4表的单独表中,甚至在SQLite之外。
语言型的languageid选项使FTS4表具有额外的隐藏标识中包含的文本语言的整数列每行。使用languageid选项可以使用相同的FTS4表用多种语言或脚本保存文本,每种语言或脚本使用不同的标记器规则,并独立于其他语言查询每种语言。
匹配信息当设置为值“fts3”时,matchinfo选项会减少FTS4存储的信息,结果是“l”选项匹配信息()不再可用。
未编制索引此选项用于指定数据所在列的名称未编制索引。存储在未编制索引的列中的值不会通过MATCH查询匹配。辅助功能也无法识别它们。单个CREATE VIRTUAL TABLE语句可以有任意数量的未索引选项。
秩序 “订单”选项可以设置为“DESC”或“ASC”(大写或小写)。如果设置为“DESC”,则FTS4将其数据存储在一种按docid降序优化返回结果的方法。如果设置为“ASC”(默认值),则数据结构为针对按docid升序返回结果进行了优化。在其他单词,如果许多查询针对FTS4表运行,请使用“ORDER BYdocid DESC”,那么添加“order=DESC”可能会提高性能CREATE VIRTUAL TABLE语句的选项。
前缀此选项可以设置为以逗号分隔的正非零列表整数。对于列表中的每个整数N,将创建一个单独的索引在数据库文件中进行优化前缀查询哪里查询项的长度为N个字节,不包括“*”字符,使用UTF-8编码时。请参见下文了解详细信息。
解压缩此选项用于指定解压缩函数。这是一个错误指定解压缩函数而不指定压缩功能。请参见下文了解详细信息。

使用FTS4时,指定包含“=”字符的列名并且不是“tokenize=*”规范或公认的FTS4选项是一个错误。对于FTS3,无法识别的指令中的第一个标记是解释为列名。类似地,指定多个“tokenize=*”使用FTS4时,单个表声明中的指令是错误的,而第二个和随后的“tokenize=*”指令被解释为列FTS3命名。例如:

--一个错误。FTS4无法识别指令“xyz=abc”。使用fts4创建虚拟表格文件(作者,文档,xyz=abc);--创建一个包含三列(“author”、“document”)的FTS3表
--和“xyz”。使用fts3创建虚拟表格文件(作者,文档,xyz=abc);--一个错误。FTS4不允许多个标记化=*指令使用fts4创建虚拟表格文件(tokenize=porter,tokenize=simple);--创建一个FTS3表,其中只有一列名为“tokenize”。这个
--table使用“porter”标记器。使用fts3创建虚拟表格文件(tokenize=porter,tokenize=simple);--一个错误。无法创建包含两个名为“tokenize”的列的表。使用fts3创建虚拟表格论文(tokenize=porter,tokenize=simple,tokenize=icu);

6.1.compress=和uncompress=选项

压缩和解压缩选项允许FTS4内容存储在压缩形式的数据库。这两个选项都应设置为名称使用注册的SQL标量函数的sqlite3_create_function()它只接受一个参数。

compress函数应返回值的压缩版本作为论点传递给它。每次数据写入FTS4表时,每个列值都传递给compress函数和结果值存储在数据库中。compression函数可以返回任何类型的SQLite值(blob、text、real、integer或null)。

解压缩函数应该解压缩以前由压缩的数据压缩函数。换句话说,对于所有SQLite值X,它应该确实,解压缩(compress(X))等于X由压缩函数压缩的数据由FTS4从数据库中读取在使用之前传递给解压缩函数。

如果指定的压缩或解压缩函数不存在可能仍会创建。直到FTS4表格读取(如果解压缩函数不存在)或写入(如果它是不存在的压缩函数)。

--创建一个FTS4表,以压缩形式存储数据
--假设标量函数zip()和unzip(
--将)添加到数据库句柄。使用fts4创建虚拟表格文件(author,document,compress=zip,uncompress=unzip);

在实现压缩和解压缩功能时,重要的是注意数据类型。具体来说,当用户从中读取值时压缩的FTS表,FTS返回的值完全相同作为解压缩函数返回的值,包括数据类型。如果该数据类型与原始值的数据类型不同传递给压缩函数(例如,如果解压缩函数为当最初传递压缩时返回BLOB),然后用户查询可能无法按预期运行。

6.2.内容=选项

内容选项允许FTS4放弃存储正在索引的文本。内容选项可以用两种方式使用:

因为索引文档本身通常比全文索引,内容选项可用于实现显著节省空间。

6.2.1.无争议FTS4表

为了创建不存储索引的副本的FTS4表文档,内容选项应该设置为一个空字符串。例如,下面的SQL创建了这样一个FTS4表,其中包含三个列-“a”、“b”和“c”:

使用fts4创建虚拟表t1(content=“”,a,b,c);

可以使用INSERT语句将数据插入到这样的FTS4表中。然而,与普通FTS4表不同,用户必须提供一个显式的整数docid值。例如:

--这句话没问题:插入t1(docid,a,b,c)值(1,‘a b c’,‘d e f’,‘g h i’);--此语句会导致错误,因为未提供docid值:插入t1(a,b,c)值('j k l','m n o','p q r');

无法更新或删除存储在无内容FTS4中的行表。试图这样做是一个错误。

无争用FTS4表也支持SELECT语句。然而,它是尝试检索除docid列。可以使用辅助函数matchinfo(),但snippet()和offsets()可能不会。例如:

--以下陈述可以:从t1中选择docid,其中t1匹配“xxx”;从t1中选择docid,其中匹配“xxx”;从t1中选择匹配信息(t1),其中t1匹配“xxx”;--以下语句都会导致错误,因为列的值
--除了docid之外,还需要对其进行评估。从t1中选择*;从t1中选择a、b,其中t1匹配“xxx”;从t1开始选择docid,其中有一个LIKE“xxx%”;从t1中选择代码段(t1),其中t1匹配“xxx”;

与尝试检索docid以外的列值相关的错误是sqlite3_step()中发生的运行时错误。在某些情况下,对于例如,如果SELECT查询中的MATCH表达式匹配零行,则即使语句引用了列值,也可能没有任何错误而不是docid。

6.2.2.外部内容FTS4表

“外部内容”FTS4表类似于无内容表,除了如果查询的求值需要除docid,FTS4尝试从表(或视图,或虚拟表)(以下简称“内容table”)。FTS4模块从不写入内容表,并且不影响全文索引。它是用户有责任确保内容表和全文索引是一致的。

通过设置内容选项创建外部内容FTS4表可以查询的表(或视图或虚拟表)的名称FTS4在需要时检索列值。如果指定的表有不存在,则外部内容表的行为方式与没有满足感的桌子。例如:

创建表t2(id整数主键,a,b,c);使用fts4创建虚拟表t3(content=“t2”,a,c);

假设指定的表确实存在,则其列必须相同作为或为FTS表定义的超集。外部桌子还必须与FTS表位于同一数据库文件中。换句话说,外部表不能位于使用连接的其他数据库文件中附件FTS表和外部内容之一也不能位于TEMP数据库,而另一个数据库位于持久数据库文件(如MAIN)中。

当用户对FTS表的查询需要除docid,FTS尝试从内容表中rowid值等于当前FTS的行文档ID。只有FTS/34中重复的内容表列的子集可以查询表声明-从任何其他必须直接查询内容表中的列。或者,如果这样的行不能如果在内容表中找到,则使用NULL值。例如:

创建表t2(id整数主键,a,b,c);使用fts4创建虚拟表t3(content=“t2”,b,c);插入t2值(2,‘a b’,‘c d’,‘e f’);插入t2值(3,‘g h’,‘i j’,‘k l’);插入t3(docid,b,c)从t2中选择id,b和c;--以下查询返回一行,其中包含两列--文本值为“ij”和“kl”。----查询使用全文索引来发现MATCH--术语与docid=3的行相匹配。然后检索值--内容表中rowid=3行的第b列和第c列--返回。--从t3中选择*,其中t3匹配“k”;--在UPDATE之后,查询仍然返回一行,这是--包含文本值“xxx”和“yyy”的时间。这是因为--全文索引仍然指示docid=3的行匹配--FTS4查询“k”,即使内容中存储了文档--表已修改。--更新t2集合b='xxx',c='yyy',其中rowid=3;从t3中选择*,其中t3匹配“k”;--在下面的DELETE之后,查询返回一行,其中包含两行--空值。由于FTS无法找到,因此返回NULL值--内容表中rowid=3的行。--从t2中删除;从t3中选择*,其中t3匹配“k”;

从外部内容FTS4表中删除行时,FTS4需要检索要从内容表中删除的行的列值。这样,FTS4可以更新每个令牌的全文索引条目出现在已删除行中,表示该行已删除。如果找不到内容表行,或者该行包含值与FTS指数的内容不一致,结果可能很困难预测。FTS索引可能包含对应于删除的行,这可能导致返回看似无意义的结果通过后续的SELECT查询。当更新行时,同样适用于在内部,UPDATE与后面跟着INSERT的DELETE相同。

这意味着为了保持FTS与外部内容同步表中,任何UPDATE或DELETE操作都必须首先应用于FTS表,然后到外部内容表。例如:

创建表t1_real(id整数主键,a,b,c,d);使用fts4创建虚拟表t1_fts(content=“t1_real”,b,c);--这很有效。当该行从FTS表中删除时,FTS检索--rowid=123的行,并对其进行标记以确定条目--必须从全文索引中删除。--从t1_fts中删除,其中rowid=123;从t1_real删除,其中rowid=123;--这个不起作用。更新FTS表时,行--已从基础内容表中删除。因此--FTS无法确定要从FTS索引中删除的条目,并且--因此,索引和内容表不同步。--从t1_real删除,其中rowid=123;从t1_fts中删除,其中rowid=123;

不是分别写入全文索引和内容表,一些用户可能希望使用数据库触发器来保留全文索引内容表中存储的文档集的最新信息。例如,使用前面示例中的表:

在t2开始更新之前创建触发器t2_bu从t3删除,其中docid=old.rowid;结束;在t2开始删除之前创建触发器t2_bd从t3删除,其中docid=old.rowid;结束;在t2开始更新后创建触发器t2_au插入t3(docid,b,c)值(new.rowid,new.b,new.c);结束;在t2开始插入后创建触发器t2_i插入t3(docid,b,c)值(new.rowid,new.b,new.c);结束;

必须在实际删除发生之前触发DELETE触发器在内容表上。这样,FTS4仍可以检索原始值以更新全文索引。INSERT触发器必须在插入新行后激发,以便处理rowid在系统内自动分配。UPDATE触发器必须分为两部分,一部分在更新内容表,原因相同。

这个FTS4“重建”命令删除整个全文索引,并基于当前内容表中的一组文档。再次假设“t3”是外部内容FTS4表的名称,rebuild命令如下所示:

插入t3(t3)值(“重建”);

此命令也可用于普通FTS4表格,例如:标记器的实现发生了变化。它是一个尝试重建由contentless维护的全文索引时出错FTS4表,因为没有内容可用于重建。

6.3.languageid=选项

当出现languageid选项时,它指定另一个隐藏的列添加到FTS4的表,用于指定存储在每行中的语言FTS4表格的。languageid隐藏列的名称必须与FTS4表中的所有其他列名不同。例子:

使用fts4(x,y,languageid=“lid”)创建虚拟表t1

languageid列的默认值为0。插入的任何值到languageid列转换为32位(非64位)有符号整数。

默认情况下,FTS查询(使用MATCH运算符的查询)只考虑那些languageid列设置为0的行。收件人查询具有其他languageid值的行形式”

=“必须添加到查询中WHERE子句。例如:
从t1中选择*,其中t1匹配“abc”且lid=5;

单个FTS查询不可能返回具有不同的languageid值。添加WHERE子句的结果使用其他运算符(例如lid!=5或lid<=5)的是未定义的。

如果content选项与languageid选项一起使用,则content=表中必须存在命名的languageid列(根据通常的规则-如果查询不需要读取内容表,则此限制不适用)。

当使用languageid选项时,SQLite会调用xLanguageid()在对象后面的sqlite3_tokenizermodule对象上是为了传递标记器应该使用。永远不会调用xLanguageid()方法对任何一个标记器对象进行多次。事实上语言的标记化可能不同,这是为什么没有单一的FTS查询可以返回具有不同languageid值的行。

6.4.matchinfo=选项

matchinfo选项只能设置为值“fts3”。尝试将matchinfo设置为“fts3”以外的任何值都是错误的。如果指定了此选项,则FTS4被省略。这减少了FTS4表格,直到其几乎与由等效的FTS3表使用,但也意味着数据通过将“l”标志传递给匹配信息()函数是无法使用的。

6.5.notindexed=选项

通常情况下,FTS模块会对所有术语进行反向索引表的所有列。此选项用于指定不应将其项添加到索引中的列。多个“notndexed”选项可用于指定多个列从索引中删除。例如:

--创建一个FTS4表,其中只包含列c2和c4的内容
--标记化并添加到反转索引中。使用fts4(c1,c2,c3,c4,notindexed=c1,notindexed=c3)创建虚拟表t1;

存储在未编制索引的列中的值不符合匹配条件操作员。它们不会影响offsets()或matchinfo()的结果辅助功能。snippet()函数也不会返回基于存储在未编制索引的列中的值的代码段。

6.6.前缀=选项

FTS4前缀选项使FTS对指定长度的术语前缀进行索引就像它总是索引完整的术语一样。前缀选项必须设置为以逗号分隔的正非零整数列表。对于列表中的每个值N,长度为N字节的前缀(编码时使用UTF-8)进行索引。FTS4使用术语前缀索引加快速度前缀查询当然,代价是索引项前缀为以及完整的术语会增加数据库大小并减缓写入速度FTS4表上的操作。

前缀索引可用于优化前缀查询有两种情况。如果查询的前缀为N字节,则创建前缀索引使用“prefix=N”可以提供最佳优化。或者,如果没有“prefix=N”索引可用,可以使用“prefix=N+1”索引代替。使用“prefix=N+1”索引的次数较少比“prefix=N”索引有效,但比根本没有前缀索引要好。

--使用索引创建FTS4表,以优化2字节和4字节前缀查询。使用fts4(c1,c2,prefix=“2,4”)创建虚拟表t1;--以下两个查询都使用前缀索引进行了优化。从t1中选择*,其中t1匹配“ab*”;从t1中选择*,其中t1匹配“abcd*”;--以下两个查询都使用前缀进行了部分优化
--索引。优化没有查询那么明显
--但仍然是对没有前缀索引的改进。从t1中选择*,其中t1匹配'a*';从t1中选择*,其中t1匹配“abc*”;

7FTS3和FTS4的特殊命令

特殊的INSERT操作可用于向FTS3和FTS4表发出命令。每个FTS3和FTS4都有一个隐藏的只读列,其名称与桌子本身。此隐藏列中的INSERT被解释为命令至FTS3/4表。对于名为“xyz”的表,使用以下命令支持:

  • 插入xyz(xyz)值(“优化”);

  • 插入xyz(xyz)值(“重新构建”);

  • 插入xyz(xyz)值(“完整性检查”);

  • 插入xyz(xyz)值('merge=X,Y');

  • 插入xyz(xyz)值('automerge=N');

7.1.“优化”命令

“optimize”命令使FTS3/4将其所有将索引b树倒置为一个完整的大b树。正在执行优化将使后续查询运行得更快,因为搜索的b-树更少,通过合并可以减少磁盘使用冗余条目。然而,对于大型FTS表,运行optimize可能和跑步一样昂贵真空.优化命令基本上必须读写整个FTS表在一笔大交易中。

在批处理模式操作中,最初建立FTS表使用大量INSERT操作,然后重复查询如果没有进一步的改变,这通常是一个好主意在最后一次INSERT之后和第一次查询之前运行“optimize”。

7.2.“重建”命令

“rebuild”命令使SQLite放弃整个FTS3/4表,然后根据原始文本重新生成。概念类似于重新索引,只适用于FTS3/4表而不是普通索引。

无论何时实现,都应运行“rebuild”命令自定义标记器的更改,以便可以重新标记所有内容。当使用FTS4内容选项对原始文件进行更改后内容表。

7.3.“完整性检查”命令

“integrity-check”命令使SQLite读取并验证通过比较FTS3/4表中所有反向指数的准确性那些与原始内容相反的索引。这个如果反转索引都正常,但会因SQLITE_CORRUPT错误而失败如果发现任何问题。

“完整性检查”命令在概念上与PRAGMA完整性检查在工作系统中,“完整性命令”应该总是成功的。完整性检查的可能原因故障包括:

  • 应用程序已对FTS影子表直接,而不使用FTS3/4虚拟表,导致影子表彼此不同步。
  • 使用FTS4内容选项并且无法手动保存与FTS4反向指数同步的内容。
  • FTS3/4虚拟表中的错误。(“完整性检查”命令最初是作为测试套件的一部分构思的用于FTS3/4。)
  • 基础SQLite数据库文件损坏。(请参见文档如何腐败和SQLite数据库其他信息。)

7.4.“merge=X,Y”命令

“merge=X,Y”命令(其中X和Y是整数)导致SQLite为合并各种倒置的将FTS3/4表的b-树索引到一个大b-树中。X值是要合并的“块”的目标数量,Y是之前所需级别上的最小b-树段数合并将应用于该级别。Y的值应该介于2和16之间,建议值为8。X的值可以是任何正整数,但值的顺序为100到300建议使用。

当FTS表在同一级别累积16个b-树段时,下一次插入该表将导致所有16个段在下一个更高级别合并为单个b树段。这个这些级别合并的效果是,大多数INSERT都会进入FTS表速度非常快,占用的内存最少,但偶尔会出现INSERT由于需要进行合并。这将导致INSERT的“尖峰”性能。

为了避免INSERT性能急剧下降,应用程序可以运行定期执行“merge=X,Y”命令,可能在空闲线程或空闲进程,以确保FTS表永远不会累积同一级别的b-tree段太多。INSERT性能通常可以避免尖峰,FTS3/4的性能可以最大化,每隔数千次运行“merge=X,Y”文档插页。每个“merge=X,Y”命令将在单独的事务(除非使用开始...承诺,当然)。通过选择一个值,可以保持交易量较小X在100到300范围内。正在运行的空闲线程通过检查差异,merge命令可以知道何时完成在里面sqlite3_total_changes()每次“merge=X,Y”之前和之后命令并在差值低于2时停止循环。

7.5.“automerge=N”命令

“automerge=N”命令(其中N是0到15之间的整数,inclusive)用于配置FTS3/4表格“自动合并”参数,控制自动增量反向索引合并。默认值新表的自动合并值为0,这意味着自动增量合并被完全禁用。如果自动合并参数的值使用“automerge=N”命令修改,新参数值为持久存储在数据库中,随后所有人都会使用已建立数据库连接。

将automerge参数设置为非零值将启用自动增量合并。这会导致SQLite执行少量反转每次INSERT操作后合并索引。合并金额执行的设计使FTS3/4表永远不会达到某一点其中,它有16个相同级别的段,因此必须进行较大的合并以完成插入。换句话说,自动增量合并旨在防止INSERT性能急剧下降。

自动增量合并的缺点是FTS3/4表运行中的每个INSERT、UPDATE和DELETE操作稍微慢一点,因为必须用额外的时间进行增量合并。为了获得最佳性能,建议应用程序禁用自动增量合并,而使用“合并”命令在空闲过程中保持反向指数合并良好。但是如果应用程序的结构不容易考虑到空闲进程,使用自动增量合并非常合理的后备解决方案。

自动合并参数的实际值决定了自动倒排索引同时合并索引段合并。如果该值设置为N,系统将等待,直到出现在开始递增之前,单个级别上至少有N个分段合并它们。设置较低的N值会导致分段合并更多快速,这可能会加快全文查询,如果工作负载包含UPDATE或DELETE操作以及INSERT,减少空间全文索引使用的磁盘上。然而,它也增加了写入磁盘的数据量。

适用于工作负载包含少量UPDATE或DELETE的情况操作,自动合并的一个好选择是8。如果工作负载包含许多UPDATE或DELETE命令,或者,如果查询速度是一个问题,那么减少自动合并可能是有利的至2。

为了向后兼容,“automerge=1”命令集automerge参数设置为8,而不是1(值为1没有意义无论如何,因为合并来自单个数据段的数据是no-op)。

8标记器

FTS标记器是用于从文档中提取术语的一组规则或基本FTS全文查询。

除非特定的标记器被指定为CREATE的一部分用于创建FTS表的VIRTUAL TABLE语句,默认使用标记器“simple”。简单标记器从中提取标记文档或基本FTS全文查询,如下所示规则:

  • 术语是合格字符的连续序列,其中合格字符是所有字母数字字符和所有具有Unicode码位值大于或等于128。所有其他字符都是将文档拆分为术语时丢弃。他们唯一的贡献是以分离相邻的术语。

  • ASCII范围内的所有大写字符(Unicode码位小于128),转换为其小写等价物标记化过程。因此,全文查询是使用简单标记器时对大小写不敏感。

例如,当一个文档包含文本“现在,他们非常沮丧的。“,从文档中提取并添加到全文索引“现在他们非常沮丧”。这样的文档将匹配全文查询,例如“match‘Frustrated’”,因为简单标记器将查询中的术语转换为小写在搜索全文索引之前。

除了“简单”标记器之外,FTS源代码还提供了一个标记器使用搬运工Stemming算法。此标记器使用相同的规则来分隔将输入文档转换为术语,包括将所有术语折叠为小写,而且还使用Porter-Stemming算法来减少相关的英语语言单词有一个共同的词根。例如,使用与上述段落中,波特标记器提取以下标记:“现在他们真的很沮丧”。即使这些术语中有一些是不均匀的英语单词,在某些情况下使用它们来构建全文索引比简单标记器产生的更容易理解的输出有用。使用porter标记器,文档不仅匹配全文查询例如“MATCH‘Frustrated’”,但也可以查询“MATCH’Frustration”,因为Porter stember算法将术语“挫折”简化为“沮丧”-就像“沮丧”一样。所以,当使用波特标记器时,FTS不仅能够找到所查询术语的精确匹配,还能够找到匹配项针对类似的英语术语。有关Porter Stemmer算法,请参阅上面链接的页面。

举例说明“simple”和“porter”之间的区别标记器:

--使用简单的标记器创建一个表。在其中插入文档。使用fts3创建简单虚拟表(tokenize=simple);插入简单的值(“现在他们“非常沮丧”);--以下两个查询中的第一个与中存储的文档匹配
--表“simple”。第二个没有。SELECT*FROM simple WHERE simple MATCH“受挫”;选择*FROM simple WHERE simple MATCH“沮丧”;--使用porter标记器创建一个表。将同一文档插入其中使用fts3创建虚拟表端口(tokenize=porter);输入波特的价值观(“现在他们“非常沮丧”);--以下两个查询都与表“porter”中存储的文档相匹配。从搬运工处选择*,搬运工匹配“受挫”;从搬运工那里选择*搬运工匹配“沮丧”;

如果使用SQLITE_ENABLE_ICU预处理器编译此扩展符号已定义,则存在一个名为“icu”的内置标记器使用ICU库实现。第一个参数传递给此标记化器的xCreate()方法(请参见fts3_tokenizer.h)可能是ICU区域设置标识符。例如,土耳其语的“tr_tr”在土耳其,或“en_AU”代表英语,在澳大利亚使用。例如:

使用fts3(text,tokenize=icu th_th)创建虚拟表格thi_text

ICU标记器的实现非常简单。它分割输入根据ICU规则查找单词边界和丢弃的文本完全由空白组成的任何标记。这可能是合适的对于某些区域设置中的某些应用程序,但不是所有应用程序。如果更复杂需要进行处理,例如实现词干提取或丢弃标点符号,这可以通过创建标记器来完成使用ICU标记器作为其实现的一部分的实现。

“unicode61”标记器从SQLite开始可用版本3.7.13(2012-06-11).Unicode61的工作方式与“simple”非常相似,只是它执行简单的unicode根据Unicode Version 6.1中的规则折叠案例,它可以识别unicode空格和标点符号,并使用它们来分隔标记。简单标记器只进行ASCII字符的大小写折叠将ASCII空格和标点符号识别为标记分隔符。

默认情况下,“unicode61”尝试从拉丁语脚本中删除音调符号字符。可以通过添加标记器参数来覆盖此行为“remove_diacritics=0”。例如:

--创建要删除的表全部的拉丁字母的变音符号
--作为标记化的一部分。使用fts4创建虚拟表txt1(tokenize=unicode61);使用fts4创建虚拟表txt2(tokenize=unicode61“remove_diacritics=2”);--创建一个不从拉丁文脚本中删除音调符号的表
--字符作为标记化的一部分。使用fts4创建虚拟表txt3(tokenize=unicode61“remove_diacritics=0”);

remove_diacritics选项可以设置为“0”、“1”或“2”。默认值值为“1”。如果设置为“1”或“2”,则从拉丁字母字符如上所述。但是如果设置为“1”,那么在相当罕见的情况下,变音符号并没有被去除unicode代码点用于用多个字符表示字符变音符号。例如,变音符号不会从代码点0x1ED9中删除(“拉丁文小写字母O,下面带圆圈和圆点”)。这是技术上的一个错误,但如果不创建向后兼容性,则无法修复问题。如果此选项设置为“2”,则音调符号正确从所有拉丁字符中删除。

还可以自定义unicode61处理的代码点集作为分隔符。“separators=”选项可用于指定一个或更多应视为分隔符的额外字符,以及“tokenchars=”选项可用于指定一个或多个额外字符它应该被视为标记的一部分,而不是分隔符。例如:

--创建一个使用unicode61标记器但考虑“”的表
--和“=”字符作为标记的一部分,大写“X”字符作为
--用作分隔符。使用fts4创建虚拟表txt3(tokenize=unicode61“tokenchars=.=”“分隔符=X”);--创建一个表,将空格字符(代码点32)视为
--象征性字符使用fts4创建虚拟表txt4(tokenize=unicode61“tokenchars=”);

如果考虑指定为“tokenchars=”参数的一部分的字符默认情况下,它将被忽略。即使是这样已被早期的“separators=”选项标记为分隔符。类似地,如果指定为“separators=”选项一部分的字符被视为分隔符默认情况下,它被忽略。如果有多个“tokenchars=”或“separators=”选项被指定,所有选项都被处理。例如:

--创建一个使用unicode61标记器但考虑“”的表
--和“=”字符作为标记的一部分,大写“X”字符作为
--用作分隔符。处理两个“tokenchars=”选项
--“separators=”选项忽略传递给它的“.”,因为“.”是由
--默认为分隔符,即使它已标记为标记
--前面的“tokenchars=”选项。使用fts4创建虚拟表txt5(tokenize=unicode61“tokenchars=。”“分隔符=X。”“tokenchars=”);

传递给“tokenchars=”或“separators=”选项的参数如下区分大小写。在上例中,指定“X”是分隔符字符不会影响处理“x”的方式。

8.1.自定义(应用程序定义的)令牌

除了提供内置的“simple”、“porter”和(可能)“icu”和“unicode61”标记器,FTS为应用程序实现和注册自定义标记器用C编写。定义了用于创建新标记器的接口并在fts3_tokenizer.h源文件中进行了描述。

注册新的FTS标记器类似于注册新的使用SQLite的虚拟表模块。用户将指针传递给结构,该结构包含指向各种回调函数的指针组成新标记器类型的实现。对于标记器,结构(在fts3tokenizer.h中定义)被调用“sqlite3_tokenizer_module”。

FTS不公开用户调用以注册新的C函数标记器类型带有数据库句柄。相反,指针必须编码为SQL blob值并通过SQL传递给FTS通过计算一个特殊的标量函数“fts3_tokenizer()”来实现引擎。可以使用一个或两个参数调用fts3_tokenizer()函数,如下:

选择fts3_tokenizer(<标记化器名称>);选择fts3_tokenizer(<tokenizer-name>,<sqlite3_tokeneizer_module ptr>);

其中<tokenizer-name>是参数使用绑定字符串的sqlite3_bind_text()其中字符串标识标记器和<sqlite3_tokenizer_module ptr>是一个参数BLOB的目标使用绑定sqlite3_bind_blob()其中BLOB的值是指向sqlite3tokenizermodule结构的指针。如果存在第二自变量,它被注册为tokenizer<tokenizer name>及其副本返回。如果只传递了一个参数,则指向标记器的指针返回当前注册为<tokenizer-name>的实现,编码为blob。或者,如果不存在这样的标记器,则会出现SQL异常(错误)被引发。

SQLite之前版本3.11.0(2016-02-15)fts3_tokenizer()可以是文本字符串或BLOB。他们不必这么做绑定参数。但这可能会导致SQL注入事件。因此,现在已禁用遗留行为默认情况下。但旧的遗留行为可以启用,用于向后真正需要的应用程序中的兼容性,通过呼叫sqlite3_db_config(分贝,SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER数据库,1,0).

下面的块包含调用fts3_tokenizer()的示例C代码中的函数:

/***使用FTS3或FTS4注册令牌化器实现。*/int寄存器标记器(sqlite3*db,char*zName,字符*zName,const sqlite3_tokenizer_module*p){整数rc;sqlite3_stmt*pStmt;const char*zSql=“SELECT fts3_tokenizer(?1,?2)”;rc=sqlite3_prepare_v2(db,zSql,-1,&pStmt,0);if(rc!=SQLITE_OK){返回rc;}sqlite3_bind_text(pStmt,1,zName,-1,SQLITE_STATIC);sqlite3_bind_blob(pStmt,2,&p,sizeof(p),SQLITE_STATIC);sqlite3_step(pStmt);return sqlite3_finalize(pStmt);}/***查询FTS以查找名为zName的标记器实现。*/int queryTokenizer(sqlite3*db,char*zName,字符*zName,const sqlite3_tokenizer_module**pp){整数rc;sqlite3_stmt*pStmt;const char*zSql=“SELECT fts3_tokenizer(?)”;*pp=0;rc=sqlite3_prepare_v2(db,zSql,-1,&pStmt,0);if(rc!=SQLITE_OK){返回rc;}sqlite3_bind_text(pStmt,1,zName,-1,SQLITE_STATIC);if(SQLITE_ROW==sqlite3_step(pStmt)){if(sqlite3_column_type(pStmt,0)==SQLITE_BLOB){成员(pp,sqlite3_column_blob(pStmt,0),大小(*pp));}}return sqlite3_finalize(pStmt);}

8.2.查询令牌

“fts3tokenize”虚拟表可用于直接访问任何标记器。下面的SQL演示了如何创建实例fts3tokenize虚拟表的:

使用fts3tokenize创建虚拟表tok1(“porter”);

所需标记化器的名称应替换为当然,示例中的“波特”。如果标记器需要一个或如果参数较多,则应在fts3tokenize中用逗号分隔声明(即使在声明中用空格分隔常规fts4表)。下面创建fts4和fts3tokenize使用相同标记器的表:

使用fts4创建虚拟表格文本1(标记化=icu en_AU);使用fts3tokenize(icu,en_AU)创建虚拟表标记1;使用fts4创建虚拟表格text2(tokenize=unicode61“tokenchars=@.”“分隔符=123”);使用fts3tokenize创建虚拟表tokens2(unicode61,“tokenchars=@.”,“separators=123”);

一旦创建了虚拟表,就可以按如下方式对其进行查询:

SELECT标记、开始、结束、位置来自tok1WHERE input=“这是一个测试句子。”;

虚拟表将为输入字符串。“token”列是token的文本。“开始”和“end”列是到原始输入字符串中的标记。“位置”列是序列号原始输入字符串中的标记。还有一个“input”列,它只是中指定的输入字符串的副本WHERE子句。请注意,“input=?”形式的约束必须出现在WHERE子句中,否则虚拟表将没有输入标记化并不会返回任何行。上述示例生成以下输出:

thi|0|4|0是|5|7|1a|8|9|2测试|10|14|3句子|15|23|4

注意,来自fts3tokenize虚拟的结果集中的标记表已根据标记器的规则进行了转换。由于本例使用了“porter”标记器,因此“this”标记为转换为“thi”。如果需要标记的原始文本,可以使用带有substr()功能。例如:

SELECT substr(输入,开始+1,结束-开始),标记,位置来自tok1WHERE input=“这是一个测试句子。”;

无论什么情况,fts3tokenize虚拟表都可以在任何tokenizer上使用是否存在实际使用的FTS3或FTS4表那个标记器。

9.数据结构

本节从较高层次描述了FTS模块存储其数据库中的索引和内容。它是无需阅读或理解本节中的材料,以便使用FTS在中应用程序。然而,它可能对尝试分析和了解FTS性能特征,或向开发人员考虑对现有FTS功能集进行增强。

9.1.阴影表

对于数据库中的每个FTS虚拟表,有三到五个真实(非虚拟)表是为了存储基础数据而创建的。这些真实的表称为“影子表”。实际表名为“%_content”,“%_segdir”、“%_sections”、“%stat”和“%_docsize”,其中“%”替换为名称FTS虚拟表的。

“%_content”表最左边的列是INTEGER PRIMARY KEY字段名为“docid”。接下来是FTS每列一列用户声明的虚拟表,通过在列名前面加前缀来命名由用户提供“cN个“,其中N个是的索引表中的列,从左到右从0开始编号。数据作为虚拟表声明的一部分提供的类型不用作%_content表声明的一部分。例如:

--虚拟表声明使用fts4(a数字,b文本,c)创建虚拟表abc;--对应%_content表声明创建表abc_content(docid INTEGER PRIMARY KEY,c0a,c1b,c2c);

%_content表包含用户插入的未经篡改的数据用户输入到FTS虚拟表中。如果用户没有明确在插入记录时提供“docid”值,将自动选择一个由系统决定。

只有在FTS表使用FTS4模块,而非FTS3。此外,如果FTS4表格是用“matchinfo=fts3”指令指定为CREATE VIRTUAL TABLE语句的一部分。如果它们被创建,这两个表的模式如下:

创建表格%_stat(id整数主键,BLOB值);创建表格%_docsize(docid整数主键,尺寸BLOB);

对于FTS表中的每一行,%_docsize表包含一个对应的具有相同“docid”值的行。“大小”字段包含一个blob,其中包括属于N个FTS变量,其中N个是用户定义的列数在表中。“大小”blob中的每个变量都是FTS表中关联行的相应列。%_stat表始终包含“id”列设置为0的单行。“价值”列包含一个blob,该blob由无+1FTS变量,其中N个也是FTS表中用户定义的列数。第一个blob中的varint设置为FTS表中的总行数。这个第二个变量和后续变量包含存储在FTS表中所有行的对应列。

剩下的两个表%_segments和%_segdir用于存储全文索引。从概念上讲,该索引是映射每个索引的查找表对应于%_包含一个或多个术语出现的内容表。收件人检索包含指定术语的所有文档,即FTS模块查询此索引以确定以下记录的docid值集包含术语,然后从%content中检索所需的文档表。无论FTS虚拟表的架构如何,%_segments和%_segdir表始终按如下方式创建:

创建表格%_segments(块整数主键,--B树节点id块blob--B树节点数据);创建表格%_segdir(整数级,idx整数,start_block整数,--%_segments中第一个节点的块IDleaves_end_block整数,--%_segments中最后一个叶节点的块IDend_block整数,--%_segments中最后一个节点的块ID根BLOB,--B树根节点主键(级别,idx));

上面描述的架构不是为了存储全文索引而设计的直接。相反,它用于存储一个或多个b-tree结构。那里是%_segdir表中每行的一个b树。%_segdir表行包含根节点和与b-tree结构,%_segments表包含所有其他(非根)b-tree节点。每个b树称为“段”。一旦有了段b树不会更新(尽管它可能会全部删除)。

每个段b-tree使用的键是术语(单词)。以及键,每个段b树条目都有一个关联的“文档列表”(doclist)。文档列表由零个或多个条目组成,其中每个条目包括:

  • 文档id(文档id),以及
  • 术语偏移列表,每个术语在中出现一次文档。术语偏移量表示标记(单词)的数量出现在相关术语之前,而不是字符数或字节。例如,在短语“祖先的声音预示着战争!”是3。

doclist中的条目按docid排序。文档列表中的位置条目按升序存储。

逻辑全文索引的内容是通过合并所有线段b树的内容。如果一个术语出现在多个术语中分割b-tree,然后映射到每个单独doclist的并集。如果,对于单个术语,相同的docid出现在多个doclist中,然后仅作为最近创建的段b树的一部分的doclist是视为有效。

使用多个b-tree结构代替单个b-tree来减少将记录插入FTS表的成本。当新记录插入到已经包含大量数据的FTS表中很可能新记录中的许多术语已经出现在大量现有记录。如果使用单个b树,则大型doclist结构必须从数据库中加载,修改后包括新的docid和条款集列表,然后写回到数据库。使用多个b-tree表可以避免这种情况通过创建一个新的b树,它可以与现有的b树合并(或b树)。b树结构的合并可以执行为一个后台任务,或一次特定数量的单独b树结构已经积累。当然,这个方案使得查询的开销更大(因为FTS代码可能需要在多个术语中查找单个术语b-tree并合并结果),但在实践中发现开销通常可以忽略不计。

9.2.可变长度整数(varint)格式

存储为段b-tree节点一部分的整数值使用FTS不同格式。此编码类似,但不完全相同,至这个SQLite变量格式.

编码的FTS变量消耗1到10个字节的空间。这个所需的字节数由整数值编码。更准确地说,用于存储的字节数编码的整数取决于最高有效集位的位置在整数值的64位二进制补码表示中。否定值总是具有最高有效位集(符号位)始终使用完整的十个字节进行存储。正整数值可以是使用更少的空间存储。

编码FTS变量的最后一个字节具有其最高有效位变明朗。所有前面的字节都有最高有效位集。数据存储在每个字节的其余七个最低有效位中。编码表示的第一个字节包含最低有效值编码整数值的七位。编码的第二个字节表示法(如果存在)包含接下来的七个最不重要的整数值的位,依此类推。下表包含示例编码整数值的:

十进制的十六进制编码表示法
430x0000000000002B0x2B型
2008150x000000000003106F0xEF 0xA0 0x0C
-10xFFFFFFFFFFFFFFFF0xFF 0xFF xFF 0xFF 0xFF 0x%FF 0xOFF 0xFF×FF 0x01

9.3.段B-树格式

b段树是前缀压缩的b+-树。有一个段b-tree对于%segdir表中的每一行(请参见上文)。段的根节点b-tree作为blob存储在相应行的“root”字段中%_segdir表的。所有其他节点(如果存在)存储在%_segments表的“blob”列。%_segments表中的节点为由对应的blockid字段中的整数值标识行。下表描述了%_segdir表的字段:

解释
水平在它们之间,“level”和“idx”字段的内容定义了b段树的相对年龄。存储在“level”字段中,最近创建的段b-tree。如果两个段b-树是相同的“级别”,段具有较大的存储在“idx”列中的值是最新的。PRIMARY KEY约束在%segdir表上,防止任何两个段具有相同的值用于“level”和“idx”字段。
国际数据交换请参见上文。
启动块(_B)对应于具有最小块ID的节点的块ID属于此段b树。如果整个段b-tree为零适合根节点。如果存在,则此节点始终是叶节点。
离开_结束_块对应于具有最大块ID的叶节点的块ID属于此段b树的。如果整个段b-tree为零适合根节点。
结束块(_B)此字段可以包含整数或由以下内容组成的文本字段由空格字符分隔的两个整数(unicode代码点0x20)。

第一个或唯一的整数是与内部对应的块ID具有属于该分段b树的最大blockid的节点。或为零如果整个线段b树适合根节点。如果存在,则此节点始终是内部节点。

第二个整数(如果存在)是所有数据的聚合大小以字节形式存储在叶页面上。如果值为负,则段是未完成的增量合并操作的输出绝对值是以字节为单位的当前大小。

包含段b树的根节点的Blob。

除了根节点之外,组成单段b树的节点还有总是使用块id的连续序列来存储。此外构成b-tree的单个级别的节点本身存储为b树顺序的连续块。块的连续序列用于存储b树叶子的分配从blockid开始值存储在相应的%_segdir行的“start_block”列中,并以“leaves_end_block”中存储的blockid值结束同一行的字段。因此,可以迭代所有通过遍历%_segments,按键顺序排列段b-tree的叶子表按blockid顺序从“start_block”到“leaves_end_block”。

9.3.1.B段树叶节点

下图描述了段b-树叶节点的格式。

B段树叶节点格式

每个节点上存储的第一个术语(上图中的“术语1”)为逐字存储。每个后续术语都会根据以下方面进行前缀压缩其前身。术语以排序方式存储在页面中(memcmp)订单。

9.3.2.B段树内部节点

下图描述了段b树内部的格式(非叶)节点。

B段树内部节点格式

9.4.文档列表格式

doclist由64位有符号整数数组组成,使用FTS变量格式。每个doclist条目由两个系列组成或多个整数,如下所示:

  1. docid值。doclist中的第一个条目包含文本docid值。每个后续文档列表条目的第一个字段包含新docid和前一个docid之间的差异(始终为正数编号)。
  2. 零个或多个条款集列表。每个都有一个条款集列表包含术语的FTS虚拟表的列。定期抵销列表包括以下内容:
    1. 常量值1。任何条款集列表都省略此字段与列0关联。
    2. 列号(1表示第二个最左边的列,等等)。这个对于与列0关联的任何术语集列表,该字段被省略。
    3. 从最小到最大排序的术语集列表。相反逐字存储term-offset值,存储每个整数是当前条款集和上一个条款集之间的差异1(如果当前条款集是第一个,则为零),加上2。
  3. 常量值0。

FTS3文件列表格式

FTS文件列表输入格式

对于术语出现在FTS多个列中的文档列表虚拟表,doclist中的术语集列表存储在列中数字顺序。这确保了与列0(如果有)总是第一个,允许在这种情况下,将省略术语集列表。

10.限制

10.1.UTF-16字节顺序标记问题

对于UTF-16数据库,当使用“简单”标记器时,可以使用格式错误的unicode字符串导致完整性检查特殊命令谎报腐败,或辅助功能返回结果不正确。更具体地说,错误可能由以下任何一种情况触发:
  • UTF-16字节顺序标记(BOM)嵌入在SQL字符串的开头插入到FTS3表中的文字值。例如:

    INSERT INTO fts_table(col)VALUES(字符(0xfeff)||“文本…”);
  • SQLite转换为UTF-16字节顺序标记的UTF-8格式错误嵌入在插入的SQL字符串文字值的开头转换为FTS3表格。

  • 通过转换以两个字符开头的blob创建的文本值字节0xFF和0xFE以任意可能的顺序插入FTS3表格。例如:

    插入fts_table(col)值(CAST(X'FEFF'作为文本));
如果以下任何一项属实,则一切正常:
  • 这个数据库编码UTF-8。
  • 使用sqlite3_bind_text()函数族。
  • 文字字符串不包含字节顺序标记。
  • 使用标记器来识别字节顺序标记作为空白。(默认的“简单”标记器FTS3/4不认为BOM是空白,但unicode标记器可以。)
为了解决问题,上述所有条件都必须为假发生。即使上面的条件都是假的,大多数事情仍然会正常运行。只有完整性检查命令和辅助功能可能给出的意外结果。

附录A:搜索应用程序提示

FTS主要用于支持布尔全文查询查找符合指定条件的文档集。但是,许多(大多数?)搜索应用程序要求以某种方式按顺序排列结果“相关性”,其中“相关性”定义为用户执行搜索的人对返回的特定元素感兴趣一套文件。使用搜索引擎查找世界上的文档时用户期望最有用或“相关”的文档将作为结果的第一页返回,并且每个后续页包含逐渐减少的相关结果。机器的确切功能基于用户查询确定文档相关性是一个复杂的问题以及许多正在进行的研究的主题。

一个非常简单的方案可能是计算用户在每个结果文档中搜索术语。包含以下内容的文件许多术语实例被认为比那些每个术语的少量实例。在FTS应用程序中每个结果中的术语实例数可以通过计数来确定返回值中的整数数偏移功能。以下示例显示了一个可用于获取用户输入的查询的十个最相关的结果:

--这个示例(以及本节中的所有其他示例)假设以下模式使用fts3(标题、内容)创建虚拟表格文档;--假设应用程序提供了名为“countintegers”的SQLite用户函数
--返回其唯一参数中包含的空格分隔整数的数量,
--以下查询可用于返回包含以下内容的10个文档的标题
--用户查询词的最大实例数。希望这10个
--文档将是那些用户或多或少认为最“相关”的文档。从文档中选择标题WHERE文档匹配<查询>ORDER BY countintegers(偏移量(文档))DESC限制10偏移0

使用FTS可以使上述查询运行得更快匹配信息函数确定每个查询项中出现的查询项实例数结果。matchinfo函数比偏移更有效功能。此外,matchinfo函数还提供额外的信息关于每个查询词在整个文档集(不仅仅是当前行)和其中的文档数每个查询词都会出现。这可以用于(例如)连接更高的对不太常见的术语进行加权,这可能会增加总体计算相关性用户认为这些结果更有趣。

--如果应用程序提供一个名为“rank”的SQLite用户函数
--解释matchinfo返回的数据blob并返回一个数字
--基于相关性,则可以使用以下SQL返回
--用于用户查询的数据集中10个最相关文档的标题。从文档中选择标题WHERE文档匹配<查询>按等级排序(匹配信息(文档))DESC限制10偏移0

与第一个示例相比,上面示例中的SQL查询使用的CPU更少在本节中,仍然存在不明显的性能问题。数据库通过检索“title”列的值和来自FTS模块的用户匹配的每一行的matchinfo数据在排序和限制结果之前进行查询。因为SQLite的方式虚拟表接口工作,检索“title”列的值需要从磁盘加载整行(包括“content”字段,可能很大)。这意味着如果用户查询匹配数千个文档,许多兆字节的“标题”和“内容”数据可以从磁盘加载到内存中,即使它们永远不会被使用出于任何目的。

以下示例块中的SQL查询是一种解决方案问题。在SQLite中,当子查询在联接中使用时包含LIMIT子句,子查询的结果是在执行主查询之前计算并存储在临时表中。这意味着SQLite将只加载每个文件的docid和matchinfo数据将用户查询匹配到内存中的行,确定docid值对应于十个最相关的文档,然后只加载标题以及仅针对这10个文档的内容信息。因为匹配信息和docid值完全从全文索引中收集,结果如下从数据库加载到内存的数据大大减少。

从文档中选择标题JOIN(SELECT docid,rank(matchinfo(documents))AS等级FROM文档WHERE文档匹配<查询>按等级DESC排序限制10偏移0)使用可分级(docid)按排序表排序。银行描述

SQL的下一个块通过解决另外两个问题来增强查询使用FTS开发搜索应用程序时可能出现的问题:

  1. 这个片段函数不能用于上述查询。因为外部查询不包含“WHERE…MATCH”子句函数不能与它一起使用。一个解决方案是复制WHERE子查询在外部查询中使用的子句。相关间接费用这通常可以忽略不计。

  2. 文档的相关性可能取决于其他因素,而不仅仅是matchinfo返回值中的可用数据。例如数据库中的每个文档都可以分配一个基于静态权重的与内容无关的因素(来源、作者、年龄、数量参考文献等)。应用程序可以存储这些值在可以与documents表联接的单独表中以便秩函数可以访问它们。

此版本的查询与sqlite.org文档搜索应用程序。

--此表存储分配给FTS表中每个文档的静态权重
--“文档”。对于文档表中的每一行,都有一个对应的行
--具有此表中相同的docid值。创建表格documents_data(docid整数主键,重量);--此查询与上面块中的查询类似,只是:
--
--   1. 它返回一个文本“片段”以及文档标题以供显示。所以
--可以使用代码段函数,则来自的“WHERE…MATCH…”子句
--子查询在外部查询中重复。
--
--   2. 子查询将documents表与document_data表联接起来,以便
--秩函数的实现可以访问分配的静态权重
--到每个文档。从文档中选择标题、片段(文档)JOIN(选择docid,rank(matchinfo(documents),documents_data.weight)作为rank从文档JOIN documents_data USING(docid)WHERE文档匹配<查询>按等级排序DESC限制10偏移0)使用可分级(docid)WHERE文档匹配<查询>按ranktable.rank DESC订购

以上所有示例查询都返回十个最相关的查询结果。通过修改OFFSET和LIMIT子句使用的值,查询返回(比如)接下来十个最相关的结果很容易构建。这可用于获取搜索应用程序秒所需的数据以及后续页面的结果。

下一个块包含使用matchinfo数据的示例rank函数在C中实现。它允许权重外部分配给每个文档的每一列。可以注册与其他任何用户函数一样,使用SQLitesqlite3_创建_函数.

安全警告:因为它只是一个普通的SQL函数,rank()可以在任何上下文中作为任何SQL查询的一部分调用。这意味着传递的第一个参数可能不是有效的matchinfo blob。实现者应该小心处理这种情况,而不会造成缓冲超支或其他潜在的安全问题。

/*
**与matchinfo()一起使用的SQLite用户定义函数,用于计算
**FTS比赛的相关性。返回的值是相关性得分
**(实际值大于或等于零)。较大的值表示
**更相关的文件。
**
**返回的总体相关性是每个相关性的总和
**FTS表中的列值。列值的相关性是
**FTS查询中每个可报告短语的以下总和:
**
**(<命中计数>/<全局命中计数>)*<列权重>
**
**其中,<hit count>是
**当前行的列值,<global hit count>是数字
**FTS中所有行的同一列中短语的实例
**表。<列权重>是分配给每个
**列(见下文)。
**
**此函数的第一个参数必须是FTS的返回值
**matchinfo()函数。在此之后,每列必须有一个参数
**FTS表格的
**列。例子:
**
**使用fts3(标题、内容)创建虚拟表格文档
**
**以下查询返回与全文匹配的文档的docid
**query<query>按相关性从大到小排序。计算时
**相关性,“标题”列中的查询词实例是
**“内容”列中的权重。
**
**从文档中选择docid
**WHERE文档匹配<查询>
**按等级排序(matchinfo(documents),1.0,0.5)DESC
*/静态void rankfunc(sqlite3_context*pCtx,int nVal,sqlite3_value**apVal){int*aMatchinfo;/*matchinfo()的返回值*/int nMatchinfo;/*aMatchinfo[]中的元素数*/int nCol=0;/*表中的列数*/int n短语=0;/*查询中的短语数*/内部iPhrase;/*当前短语*/双重得分=0.0;/*要返回的值*/断言(sizeof(int)==4);/*检查传递给此函数的参数数量是否正确。
**如果不是,则跳转到wrong_number_args。设置一个Matchinfo以指向数组
**FTS函数matchinfo返回的无符号整数值(共个)。设置
**nPhrase以包含用户全文中可报告短语的数量
**查询,并将nCol设置为表中的列数。然后检查
**matchinfo blob的大小符合预期。如果不是,则返回错误。
*/如果(nVal<1)转到错误的_number_args;aMatchinfo=(unsigned int*)sqlite3_value_blob(apVal[0]);nMatchinfo=sqlite3_value_bytes(apVal[0])/sizeof(int);if(nMatchinfo>=2){nPhrase=aMatchinfo[0];nCol=aMatchinfo[1];}if(nMatchinfo!=(2+3*nCol*nPhrase)){sqlite3_result_error(pCtx,“传递给函数rank()的matchinfo blob无效”,-1);回报;}如果(nVal!=(1+nCol))转到错误的number_args;/*重复用户查询中的每个短语*/for(iPhrase=0;iPhrase<nPhrase;iPhrase++){输入iCol;/*当前列*/

/*现在遍历用户查询中的每一列。对于每一列,
**相关性得分增加:
**
**(<命中计数>/<全局命中计数>)*<列权重>
**
**aPhraseinfo[]指向短语iPhrase数据的开头。所以
**每个列的命中数和全局命中数位于
**aPhraseinfo[iCol*3]和aPhraseenfo[iCol*3+1]。
*/int*aPhraseinfo=&aMatchinfo[2+iPhrase*nCol*3];对于(iCol=0;iCol<nCol;iCol++){int nHitCount=aPhraseinfo[3*iCol];int nGlobalHitCount=aPhraseinfo[3*iCol+1];双权重=sqlite3_value_double(apVal[iCol+1]);如果(nHitCount>0){score+=((双)nHitCount/(双)nGlobalHitCount)*权重;}}}sqlite3_result_double(pCtx,分数);回报;/*如果传递给此函数的参数数量错误,请跳到此处*/错误的编号参数:sqlite3_result_error(pCtx,“函数rank()的参数数量错误”,-1);}

此页面上次修改时间2023-10-10 17:29:48联合技术公司