• 介绍
  • 基本用法
    • ORM
      • ORM的限制
    • 建表
    • 建立FTS索引
    • FTS搜索
      • 全表搜索
  • FTS模块
  • FTS分词器
    • Apple分词器
    • Apple分词器的局限性
    • WCDB分词器
      • 符号
      • 英文
      • 中文
  • 自定义分词器

    本教程主要介绍WCDB-iOS/macOS中全文搜索的用法。

    阅读本教程前,建议先阅读 iOS/macOS使用教程 、 ORM使用教程 和 基础类、CRUD与Transaction 。

    介绍

    全文搜索 ( Full-Text Serach ),是SQLite提供的功能之一,支持更快速、更便捷地搜索数据库内的信息。更多信息可参考SQLite的官方文档。

    WCDB内建FTS的支持,使用更简便,搜索更智能,分词更适合中文、日文等非空格分割的语言搜索。

    基本用法

    ORM

    1. //WCTSampleFTSData.h
    2. @interface WCTSampleFTSData : NSObject
    3.  
    4. @property(nonatomic, retain) NSString *name;
    5. @property(nonatomic, retain) NSString *content;
    6.  
    7. @end
    8.  
    9. //WCTSampleFTSData+WCTTableCoding.h
    10. @interface WCTSampleFTSData (WCTTableCoding) <WCTTableCoding>
    11.  
    12. WCDB_PROPERTY(name)
    13. WCDB_PROPERTY(content)
    14.  
    15. @end
    16.  
    17. //WCTSampleFTSData.mm
    18. @implementation WCTSampleFTSData
    19.  
    20. WCDB_IMPLEMENTATION(WCTSampleFTSData)
    21. WCDB_SYNTHESIZE(WCTSampleFTSData, name)
    22. WCDB_SYNTHESIZE(WCTSampleFTSData, content)
    23.  
    24. WCDB_VIRTUAL_TABLE_MODULE(WCTSampleFTSData, WCTModuleNameFTS3)
    25. WCDB_VIRTUAL_TABLE_TOKENIZE(WCTSampleFTSData, WCTTokenizerNameWCDB)
    26.  
    27. @end

    FTS的ORM与普通表的ORM很类似。

    将一个已有的ObjC类进行FTS的ORM绑定的过程如下:

    • 使用WCDB_PROPERTY宏在头文件声明需要绑定到数据库表的字段。
    • 使用WCDB_IMPLEMENTATIO宏在类文件定义绑定到数据库表的类。
    • 使用WCDB_SYNTHESIZE宏在类文件定义需要绑定到数据库表的字段。
    • 使用WCDB_VIRTUAL_TABLE_MODULE定义使用的虚拟表的模块。
    • 使用WCDB_VIRTUAL_TABLE_TOKENIZE定义使用搜索使用的分词器。
      如无特殊需求,模块和分词器可使用默认的WCTModuleNameFTS3WCTTokenizerNameWCDB即可。后面会进一步讨论模块和分词器。

    ORM的限制

    SQLite的FTS是使用虚拟表实现的,因此其与虚拟表有同样的限制

    • 不支持创建触发器
    • 不支持、也不需要创建索引
    • 不支持通过ALTER TABLE为虚拟表添加新的字段

    建表

    1. WCTDatabase *databaseFTS = [[WCTDatabase alloc] initWithPath:pathFTS];
    2. [databaseFTS setTokenizer:WCTTokenizerNameWCDB];
    3. [databaseFTS createVirtualTableOfName:tableNameFTS withClass:WCTSampleFTSData.class];

    建FTS表与建普通表也很类似。对于已经定义FTS-ORM的类WCTSampleFTSData和已创建的数据databaseFTS

    • 通过- setTokenizer:接口对当前数据库注册分词器,这里需与ORM定义的分词器一致。开发者也可以通过- setTokenizers:注册多个分词器。
    • 通过- createVirtualTableOfName:withClass:接口创建FTS表。

    建立FTS索引

    1. WCTSampleFTSData *object = [[WCTSampleFTSData alloc] init];
    2. object.name = @"English";
    3. object.content = @"This is sample content";
    4. object.isAutoIncrement = YES;
    5. [databaseFTS insertObject:object into:tableNameFTS];

    开发者只需要将数据插入到FTS表中,即可建立FTS索引。插入方式与普通表没有区别。

    FTS搜索

    1. NSArray<WCTSampleFTSData *> *ftsDatas = [databaseFTS getObjectsOfClass:WCTSampleFTSData.class fromTable:tableNameFTS where:WCTSampleFTSData.name.match("Eng*")];
    2. for (WCTSampleFTSData *ftsData in ftsDatas) {
    3. NSLog(@"Match name:%@ content:%@", ftsData.name, ftsData.content);
    4. }

    FTS搜索与普通表的方式类似,只是在where语句中,需要用match来搜索。

    全表搜索

    FTS表不仅可以针对某个字段进行搜索,也可以使用内置的隐藏字段进行全表搜索。隐藏字段与表名一致,可以通过className.PropertyNamed(tableName)获取。

    1. NSArray<WCTSampleFTSData *> *ftsDatas = [databaseFTS getObjectsOfClass:WCTSampleFTSData.class fromTable:tableNameFTS where:WCTSampleFTSData.PropertyNamed(tableNameFTS).match("Eng*")];
    2. for (WCTSampleFTSData *ftsData in ftsDatas) {
    3. NSLog(@"Match name:%@ content:%@", ftsData.name, ftsData.content);
    4. }

    FTS搜索的示例代码请参考:sample_fts_main

    FTS模块

    SQLite支持FTS3FTS5两种搜索模块,可通过WCDB_VIRTUAL_TABLE_MODULE(WCTSampleFTSData, WCTModuleNameFTS3)WCDB_VIRTUAL_TABLE_MODULE(WCTSampleFTSData, @"FTS5")进行定义。WCDB默认使用FTS3

    关于 FTS3FTS5 的差异,可参考SQLite的官方文档 SQLite FTS3 and FTS4 Extensions 和 SQLite FTS5 Extension

    FTS分词器

    若只用一句话概括,FTS搜索的原理是将文字信息通过分词器切断为字词组成的数组,并以此建立搜索树。因此,分词器是搜索效率、准确度的关键。

    SQLite内置了simpleporterunicode61等多个分词器,但其适合场景有限,这里不做深入讨论,开发者可自行搜索。

    这里重点讨论WCDB内置的分词器, WCTTokenizerNameAppleWCTTokenizerNameWCDB

    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分词器会按照顺序,将其分割为:"苹果"、"苹"、"果树"、"果"、"树"。因此其可以确保各种组合都能正确匹配到。

    自定义分词器

    若开发者对已有的分词器不满意,也可以自定义新的分词器。

    WCDB提供了文件模版用来定义分词器,开发者可参考 Apple分词器 和 WCDB分词器 实现。

    FTS全文搜索使用教程 - 图1

    FTS全文搜索使用教程 - 图2