本教程主要介绍WCDB-iOS/macOS中全文搜索的用法。
阅读本教程前,建议先阅读 iOS/macOS使用教程 、 ORM使用教程 和 基础类、CRUD与Transaction 。
介绍
全文搜索 ( Full-Text Serach ),是SQLite提供的功能之一,支持更快速、更便捷地搜索数据库内的信息。更多信息可参考SQLite的官方文档。
WCDB内建FTS的支持,使用更简便,搜索更智能,分词更适合中文、日文等非空格分割的语言搜索。
基本用法
ORM
- //WCTSampleFTSData.h
- @interface WCTSampleFTSData : NSObject
- @property(nonatomic, retain) NSString *name;
- @property(nonatomic, retain) NSString *content;
- @end
- //WCTSampleFTSData+WCTTableCoding.h
- @interface WCTSampleFTSData (WCTTableCoding) <WCTTableCoding>
- WCDB_PROPERTY(name)
- WCDB_PROPERTY(content)
- @end
- //WCTSampleFTSData.mm
- @implementation WCTSampleFTSData
- WCDB_IMPLEMENTATION(WCTSampleFTSData)
- WCDB_SYNTHESIZE(WCTSampleFTSData, name)
- WCDB_SYNTHESIZE(WCTSampleFTSData, content)
- WCDB_VIRTUAL_TABLE_MODULE(WCTSampleFTSData, WCTModuleNameFTS3)
- WCDB_VIRTUAL_TABLE_TOKENIZE(WCTSampleFTSData, WCTTokenizerNameWCDB)
- @end
FTS的ORM与普通表的ORM很类似。
将一个已有的ObjC类进行FTS的ORM绑定的过程如下:
- 使用
WCDB_PROPERTY
宏在头文件声明需要绑定到数据库表的字段。 - 使用
WCDB_IMPLEMENTATIO
宏在类文件定义绑定到数据库表的类。 - 使用
WCDB_SYNTHESIZE
宏在类文件定义需要绑定到数据库表的字段。 - 使用
WCDB_VIRTUAL_TABLE_MODULE
定义使用的虚拟表的模块。 - 使用
WCDB_VIRTUAL_TABLE_TOKENIZE
定义使用搜索使用的分词器。
如无特殊需求,模块和分词器可使用默认的WCTModuleNameFTS3
和WCTTokenizerNameWCDB
即可。后面会进一步讨论模块和分词器。
ORM的限制
SQLite的FTS是使用虚拟表实现的,因此其与虚拟表有同样的限制
- 不支持创建触发器
- 不支持、也不需要创建索引
- 不支持通过
ALTER TABLE
为虚拟表添加新的字段
建表
- WCTDatabase *databaseFTS = [[WCTDatabase alloc] initWithPath:pathFTS];
- [databaseFTS setTokenizer:WCTTokenizerNameWCDB];
- [databaseFTS createVirtualTableOfName:tableNameFTS withClass:WCTSampleFTSData.class];
建FTS表与建普通表也很类似。对于已经定义FTS-ORM的类WCTSampleFTSData
和已创建的数据databaseFTS
- 通过
- setTokenizer:
接口对当前数据库注册分词器,这里需与ORM定义的分词器一致。开发者也可以通过- setTokenizers:
注册多个分词器。 - 通过
- createVirtualTableOfName:withClass:
接口创建FTS表。
建立FTS索引
- WCTSampleFTSData *object = [[WCTSampleFTSData alloc] init];
- object.name = @"English";
- object.content = @"This is sample content";
- object.isAutoIncrement = YES;
- [databaseFTS insertObject:object into:tableNameFTS];
开发者只需要将数据插入到FTS表中,即可建立FTS索引。插入方式与普通表没有区别。
FTS搜索
- NSArray<WCTSampleFTSData *> *ftsDatas = [databaseFTS getObjectsOfClass:WCTSampleFTSData.class fromTable:tableNameFTS where:WCTSampleFTSData.name.match("Eng*")];
- for (WCTSampleFTSData *ftsData in ftsDatas) {
- NSLog(@"Match name:%@ content:%@", ftsData.name, ftsData.content);
- }
FTS搜索与普通表的方式类似,只是在where
语句中,需要用match
来搜索。
全表搜索
FTS表不仅可以针对某个字段进行搜索,也可以使用内置的隐藏字段进行全表搜索。隐藏字段与表名一致,可以通过className.PropertyNamed(tableName)
获取。
- NSArray<WCTSampleFTSData *> *ftsDatas = [databaseFTS getObjectsOfClass:WCTSampleFTSData.class fromTable:tableNameFTS where:WCTSampleFTSData.PropertyNamed(tableNameFTS).match("Eng*")];
- for (WCTSampleFTSData *ftsData in ftsDatas) {
- NSLog(@"Match name:%@ content:%@", ftsData.name, ftsData.content);
- }
FTS搜索的示例代码请参考:sample_fts_main
FTS模块
SQLite支持FTS3
、FTS5
两种搜索模块,可通过WCDB_VIRTUAL_TABLE_MODULE(WCTSampleFTSData, WCTModuleNameFTS3)
或WCDB_VIRTUAL_TABLE_MODULE(WCTSampleFTSData, @"FTS5")
进行定义。WCDB默认使用FTS3
。
关于 FTS3
和 FTS5
的差异,可参考SQLite的官方文档 SQLite FTS3 and FTS4 Extensions 和 SQLite FTS5 Extension
FTS分词器
若只用一句话概括,FTS搜索的原理是将文字信息通过分词器切断为字词组成的数组,并以此建立搜索树。因此,分词器是搜索效率、准确度的关键。
SQLite内置了simple
、porter
、unicode61
等多个分词器,但其适合场景有限,这里不做深入讨论,开发者可自行搜索。
这里重点讨论WCDB内置的分词器, WCTTokenizerNameApple
和 WCTTokenizerNameWCDB
。
Apple分词器
WCTTokenizerNameApple
是WCDB内置的一个分词器,它使用 CoreFoundation
内置的 CFStringTokenizer
对语句进行分割。
CFStringTokenizer
会过滤符号,并根据语言、语义对语句进行分割。
在iOS的任一输入框的文字中长按,并在弹出菜单中点击 "选择",iOS会智能地根据当前游标选择附近的一个词组。这个就是 CFStringTokenizer
的分词效果。
以一个词组 "苹果树" 为例,CFStringTokenizer
根据语义,会将其分割为 "苹果" 和 "树" 两个词组。
因此,使用 Apple分词器 的 FTS 表内会有该两个字段。开发者只需使用 className.PropertyNamed(tableName).match("苹果")
即可搜索到对应的数据。
Apple分词器的局限性
上面提到,FTS搜索是将字段切断后组成B树,也就是说,搜索是根据切割后词组的首字符逐个匹配过去的。
因此,以"苹果树"的例子来说,"果树" 这一关键词因为无法首字匹配 "苹果" 和 "树",它无法被搜索到。而在中文中,这一例子里有"苹果"、"果树"、"树"、"苹果树"等多个有意义的词组。因此,虽然Apple分词器可以智能地基于语义进行分词,但其并不符合部分场景和部分语言。
同时,FTS通常用于app内搜索,其使用场景与搜索引擎不同。搜索引擎只要求将最符合条件的前几页数据搜索出来,因此搜索"果树"但结果中没有"苹果树"是符合场景的。而FTS要求的触达率要求是100%,即只要app内有这个数据,就应该被搜索出来。
WCDB分词器
WCTTokenizerNameWCDB
是WCDB内置分词器,也是我们优先推荐的分词器。
符号
WCDB分词器会过滤所有Unicode编码中符号、空格、制表符、不可见和非法字符等。即Unicode编码字集中的 [ Cc, Cf, Z, U000A ~ U000D, U0085, M, P, S ],详情可参考 Unicode Character Categories
英文
WCDB分词器会将英文按照单词进行词形还原。
以句子"WCDB is a cross-platform database framework developed by WeChat."为例,由于英文存在多种变形和时态,传统的分词器无法通过"are"搜索到"is",无法通过"develop"搜索到"developed"。而词形还原会将单词还原为一般形态,从而使得WCDB分词器有更强的英文搜索能力。
中文
包括中文、日文在内的多种语言,都是通过语义,而不是空格或符号进行分割。WCDB分词器会将他们按照单双字符的逻辑进行分词。
同样以"苹果树"的逻辑为例,WCDB分词器会按照顺序,将其分割为:"苹果"、"苹"、"果树"、"果"、"树"。因此其可以确保各种组合都能正确匹配到。
自定义分词器
若开发者对已有的分词器不满意,也可以自定义新的分词器。