概述
FTS3和FTS4是SQLite虚拟表模块,允许用户执行 对一组文档进行全文搜索。 最常见的(也是最有效的) 描述全文搜索的方式是“谷歌、雅虎和必应的做法 用户输入一个术语或系列 术语,可能由二进制运算符连接或组合成 短语,全文查询系统会找到最佳的文档集 考虑到用户的运算符和分组,匹配这些术语 明确规定。 本文描述了FTS3和FTS4的部署和使用。
FTS1和FTS2是SQLite的过时全文搜索模块。 有已知的 应该避免这些旧模块及其使用的问题。 原始FTS3代码的一部分被贡献给SQLite项目 作者:Scott Hess 谷歌 。现在是 作为SQLite的一部分进行开发和维护。
1 FTS3和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包含查询性能优化 提高包含以下术语的全文查询的性能 非常常见(出现在很大比例的表行中)。
FTS4支持一些可与 匹配信息() 功能。
因为它在磁盘上存储了两个新的 影子表格 为了支持性能 优化和额外的matchinfo()选项,FTS4表可能会消耗更多 磁盘空间大于使用FTS3创建的等效表。 通常是开销 为1-2%或更低,但如果存储在 FTS桌子很小。 通过指定 指令 “matchinfo=fts3” 作为FTS4表格的一部分 声明,但这是以牺牲一些 额外支持的matchinfo()选项。
FTS4提供挂钩(压缩和解压缩 选项 )允许将数据存储在压缩的 形式,减少磁盘使用和IO。
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之间的差异 和普通表格:
与所有虚拟表类型一样,不可能创建索引或 附加到FTS表的触发器。 也不可能使用ALTER TABLE 命令向FTS表添加额外的列(尽管可以使用 ALTER TABLE以重命名FTS表)。
作为“CREATE VIRTUAL TABLE”语句的一部分指定的数据类型 完全忽略用于创建FTS表的。 而不是 应用类型的常规规则 密切关系 到插入的值,所有 插入到FTS表列中的值(特殊行ID除外 列)在存储之前转换为TEXT类型。
FTS表允许使用特殊别名“docid”来引用 所有支持的rowid列 虚拟表 .
这个 FTS匹配 运算符支持基于内置 全文索引。
这个 FTS辅助功能 , 片段() , 偏移() 、和 匹配信息() 是 可用于支持全文查询。
每个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库将文档拆分为术语 (单词)使用指定语言和地区的约定。
三。 全文索引查询
关于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查询表达式语言,可以执行各种设置 对基本查询结果的操作。 目前有三个 支持的操作:
AND运算符确定 交叉 两套文件。 OR运算符计算 联盟 两套文件。 NOT运算符(如果使用标准语法,则为一元“-”运算符) 可用于计算 相对补语 一套 与另一个相关的文档。
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查询集操作类似,但 不同,使用增强的查询语法设置操作。 那里 有四个不同点,如下所示:
仅支持AND运算符的隐式版本。 将字符串“AND”指定为标准查询语法查询的一部分是 解释为对包含术语的文档集的术语查询 “和”。
不支持括号。
不支持NOT运算符。 而不是NOT 运算符,标准查询语法支持一元“-”运算符 可应用于基本术语和术语前缀查询(但不适用于短语 或NEAR查询)。 具有一元“-”运算符的术语或术语前缀 附加到它的操作数可能不会显示为OR运算符的操作数。 FTS公司 查询不能完全由一元术语或术语前缀查询组成 附加的“-”运算符。
--搜索包含术语“sqlite”但确实包含的文档集
--不包含术语“数据库”。 SELECT*FROM docs WHERE docs MATCH'sqlite-database';从文档中选择*FROM文档,其中文档匹配'sqlite-database';
集合操作的相对优先级不同。 特别是,使用标准查询语法,“OR”操作符有一个 优先级高于“AND”。 使用时运算符的优先级 标准查询语法为:
操作员 标准查询语法优先级 一元“-” 最高优先级(最紧密的分组)。 或
和 最低优先级(最松散的分组)。
以下示例说明了使用标准的运算符的优先级 查询语法:
--搜索至少包含一个术语“数据库”的文档
--和“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表中的每个系列代币 匹配查询表达式中的一个匹配短语,称为 “短语匹配”:
如果匹配短语是由连接的一系列短语的一部分 FTS查询表达式中的NEAR运算符,然后每个短语匹配 必须与相关的其他短语匹配足够接近 类型以满足NEAR条件。 如果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) 1 FTS中用户定义的列数 表(即不包括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个 1 FTS4表中的行数。 此值为 仅在查询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()函数。
5 Fts4aux-直接访问全文索引
截止日期 版本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表已修改,查询结果可能会反映 只有所做更改的子集(可能为空)。
6 FTS4选项
如果“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 BY docid 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命令如下所示:
此命令也可用于普通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*”;
7 FTS3和FTS4的特殊命令
特殊的INSERT操作可用于向FTS3和FTS4表发出命令。 每个FTS3和FTS4都有一个隐藏的只读列,其名称与 桌子本身。 此隐藏列中的INSERT被解释为命令 至FTS3/4表。 对于名为“xyz”的表,使用以下命令 支持:
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全文查询,如下所示 规则:
例如,当一个文档包含文本“现在,他们非常 沮丧的。 “,从文档中提取并添加到 全文索引“现在他们非常沮丧”。 这样的 文档将匹配全文查询,例如“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标记、开始、结束、位置 来自tok1 WHERE input=“这是一个测试句子。”;
虚拟表将为 输入字符串。“token”列是token的文本。 “开始” 和“end”列是到 原始输入字符串中的标记。 “位置”列是序列号 原始输入字符串中的标记。还有一个“input” 列,它只是中指定的输入字符串的副本 WHERE子句。 请注意,“input=?”形式的约束必须 出现在WHERE子句中,否则虚拟表将没有输入 标记化并不会返回任何行。 上述示例生成 以下输出:
thi|0|4|0 是|5|7|1 a|8|9|2 测试|10|14|3 句子|15|23|4
注意,来自fts3tokenize虚拟的结果集中的标记 表已根据标记器的规则进行了转换。 由于本例使用了“porter”标记器,因此“this”标记为 转换为“thi”。 如果需要标记的原始文本, 可以使用带有 substr() 功能。 例如:
SELECT substr(输入,开始+1,结束-开始),标记,位置 来自tok1 WHERE 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每列一列 用户声明的虚拟表,通过在列名前面加前缀来命名 由用户提供“c N个 “,其中 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由 无+1 FTS变量,其中 N个 也是FTS表中用户定义的列数。 第一个 blob中的varint设置为FTS表中的总行数。 这个 第二个变量和后续变量包含存储在 FTS表中所有行的对应列。
剩下的两个表%_segments和%_segdir用于存储 全文索引。 从概念上讲,该索引是映射每个索引的查找表 对应于 %_包含一个或多个术语出现的内容表。 收件人 检索包含指定术语的所有文档,即FTS模块 查询此索引以确定以下记录的docid值集 包含术语,然后从%content中检索所需的文档 表。 无论FTS虚拟表的架构如何,%_segments 和%_segdir表始终按如下方式创建:
创建表格%_segments( 块整数主键, --B树节点id 块blob --B树节点数据 ); 创建表格%_segdir( 整数级, idx整数, start_block整数, --%_segments中第一个节点的块ID leaves_end_block整数, --%_segments中最后一个叶节点的块ID end_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并合并结果),但在实践中发现 开销通常可以忽略不计。
存储为段b-tree节点一部分的整数值使用 FTS不同格式。 此编码类似,但 不完全相同 ,至 这个 SQLite变量格式 .
编码的FTS变量消耗1到10个字节的空间。 这个 所需的字节数由 整数值编码。 更准确地说,用于存储的字节数 编码的整数取决于最高有效集位的位置 在整数值的64位二进制补码表示中。 否定 值总是具有最高有效位集(符号位) 始终使用完整的十个字节进行存储。 正整数值可以是 使用更少的空间存储。
编码FTS变量的最后一个字节具有其最高有效位 变明朗。 所有前面的字节都有最高有效位集。 数据 存储在每个字节的其余七个最低有效位中。 编码表示的第一个字节包含最低有效值 编码整数值的七位。 编码的第二个字节 表示法(如果存在)包含接下来的七个最不重要的 整数值的位,依此类推。下表包含示例 编码整数值的:
十进制的 十六进制 编码表示法 43 0x0000000000002B 0x2B型 200815 0x000000000003106F 0xEF 0xA0 0x0C -1 0xFFFFFFFFFFFFFFFF 0xFF 0xFF xFF 0xFF 0xFF 0x%FF 0xOFF 0xFF×FF 0x01
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段树内部节点格式
doclist由64位有符号整数数组组成,使用 FTS变量格式。 每个doclist条目由两个系列组成 或多个整数,如下所示:
docid值。 doclist中的第一个条目包含文本docid 值。 每个后续文档列表条目的第一个字段包含 新docid和前一个docid之间的差异(始终为正数 编号)。 零个或多个条款集列表。 每个都有一个条款集列表 包含术语的FTS虚拟表的列。 定期抵销 列表包括以下内容:
常量值1。 任何条款集列表都省略此字段 与列0关联。 列号(1表示第二个最左边的列,等等)。 这个 对于与列0关联的任何术语集列表,该字段被省略。 从最小到最大排序的术语集列表。 相反 逐字存储term-offset值,存储每个整数 是当前条款集和上一个条款集之间的差异 1(如果当前条款集是第一个,则为零),加上2。
常量值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'作为文本));
如果以下任何一项属实,则一切正常: 为了解决问题,上述所有条件都必须为假 发生。 即使上面的条件都是假的, 大多数事情仍然会正常运行。 只有 完整性检查 命令和 辅助功能 可能给出的 意外结果。
附录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开发搜索应用程序时可能出现的问题:
这个 片段 函数不能用于上述查询。 因为 外部查询不包含“WHERE…MATCH”子句 函数不能与它一起使用。一个解决方案是复制WHERE 子查询在外部查询中使用的子句。 相关间接费用 这通常可以忽略不计。
文档的相关性可能取决于其他因素,而不仅仅是 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中实现。它允许权重 外部分配给每个文档的每一列。 可以注册 与其他任何用户函数一样,使用SQLite sqlite3_创建_函数 .
安全警告: 因为它只是一个普通的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 联合技术公司