标题:【迅搜10】索引管理(三)同义词及其它属性方法

文章目录
    分类:存储运维 标签:迅搜

    索引管理(三)同义词及其它属性方法

    学习完索引操作最核心的增、删、改操作之后,我们再来学习它的其它一些功能。其中,比较有意思的是一个同义词操作的功能,我们先来看看这个功能的操作。

    同义词操作

    同义词,不用多解释了吧。小学一二年级,我们就会在语文课上学习到同义词的概念,不过在上学阶段,通常是同级同义词的练习,比如说“早上”、“早晨”、“清晨”。但是在日常,同义词的形式就复杂许多,比如说之前流行的 “PUA” ,还有更新潮的 “CPU” ,它们其实又都是单词 “洗脑”、名词“精神控制”的意思。


    现在,我们期望在搜索 “PUA” 时,顺带着其它三种词,也就是与 “CPU”、“洗脑”、“精神控制” 有关的内容都可以被搜索到。这就是 同义词搜索 的概念。在这个例子中,“PUA” 是我们搜索用的原词,也可以叫做标准词,因为我们的主查询语句或者单词就是它。而另外三个词就是“同义词”。同样的,“长沙”=“星城”,“湖大”=“湖南大学”,“湖南”=“湘”,类似的这类都是同义词搜索的典型词汇。


    在大部分的搜索引擎中,目前都是通过预先设置好的 同义词库 来进行搜索查询的。词库内每条记录由“标准词(原词)”和“同义词”组成, 它们都必须是独立的词汇,也就是最小的索引单位,不可以是多个词组成的短语。这个最小索引单位,就是我们之前一直强调的分词后的单个词项,比如“卡车”,但不能是“大卡车”(会拆成“大”和“卡车”)或“卡”。而对于英文来说,独立词汇就是一个单词,我们后面也会看到英文词汇的效果。另外 XS 中的同义词和 Xapian 中的不同,XS 会进行智能匹配和转换,我们只需要关注通用词库就好了。


    这个通用词库就是 SCWS 默认自带的那个词库,后面在学习分词相关的内容时,我们还会再讲。现在,我们先来看看怎么操作同义词库。其实只有几个方法,非常简单。不过需要注意的是,同义词针对的是一个索引项目,不同的索引项目的同义词不能通用。

    $xs->index->add(new XSDocument([
      'id'=>uniqid(),
      'title'=>'PHP是最好的Web编程语言',
      'content'=>'你敢信?',
    ]));
    $xs->index->add(new XSDocument([
      'id'=>uniqid(),
      'title'=>'PHP是最强的Web编程语言',
      'content'=>'你敢信?',
    ]));
    $xs->index->add(new XSDocument([
      'id'=>uniqid(),
      'title'=>'PHP是最棒的Web编程语言',
      'content'=>'你敢信?',
    ]));
    
    
    $xs->index->addSynonym("最好","最强");
    $xs->index->addSynonym("最好","最棒");
    
    $xs->index->addSynonym("最棒","最强");

    首先,添加了三条数据,然后,我们使用 addSynonym() 添加同义词,其中,第一个参数是原词,第二个参数是同义词。上面三段添加的结果就是,“最好”=“最强”和“最棒”,而“最棒”=“最强”。词库添加也是异步执行的过程,所以我们要等一会之后再测试。

    print_r($xs->search->setAutoSynonyms()->search('最好'));
    // 三条数据
    
    print_r($xs->search->search('最棒'));
    // 两条数据
    
    print_r($xs->search->search('最强'));
    // 只有最后一条

    很明显,搜索关键词“最好”的时候,三条数据都出来了,因为它的同义词“最强”和“最棒”都会同时搜索,后面我们会看到具体的搜索语句的效果。搜索“最棒”时会同时也搜索到“最强”的那条数据。而“最强”因为没有设置同义词,所以搜索这个词只有那一条数据。


    在这段搜索代码中,第一行代码我们使用了一个 setAutoSynonyms() 方法。这个方法的意思是打开同义词搜索功能。而后面两条不需要再使用这个方法了。其实,setAutoSynonyms() 是直接向服务端发送一个使用同义词搜索的命令的,所以在当前这个服务端连接未中断的情况下,后续的查询是不需要再添加这个方法的。当然,所有的查询都带上这个方法也没什么问题。


    接下来,我们要分析一下查询语句,看看为什么会查到同义词相关的数据。具体的搜索方法我们到后面学习搜索部分的时候会详细的说明,现在大家只需要知道怎么用就好了。

    print_r($xs->search->setQuery('最好')->getQuery());
    // Query((最好@1 SYNONYM 最强@78 SYNONYM 最棒@79))

    setQuery() 方法是 XSSearch 对象设置查询条件的方法,而 getQuery() 方法则是返回分词查询语句。这就有点像在使用 TP 之类的框架时,通过 getLastSql() 之类的方法返回最后的查询语句一样的效果。通过这个返回的内容,我们可以看到查询单词 “最好” 的后面跟着 SYNONYM 最强 和 SYNONYM 最棒 。SYNONYM 表示的就是同义词的意思,剩下的就不用多解释了吧。SYNONYM 是自动通过同义词库获得对应词项的同义词,然后以类似于 OR 的形式进行实际的查询。也就是说,查询包含 “最好” 或者(OR)“最强” 或者 “最棒” 任意一个单词存在的文档。关于这里的条件分析及布尔查询问题,更具体的内容我们在后面学习搜索相关知识的时候再详细说明,不过只要有一点 MySQL 基础,相信你也知道同义词这一块是怎么查询的了吧。


    XSSearch 对象的 setAutoSynonyms() 还有一个布尔类型的参数,如果是设置为 false ,就表示在查询时关闭同义词查询功能。

    $xs->search->setAutoSynonyms(false);
    print_r($xs->search->search('最好')); // 恢复成一条了
    
    print_r($xs->search->setQuery('最好')->getQuery());
    // Query(最好@1)

    除了 setAutoSynonyms() 之外,XSSearch 对象还有一个 getAllSynonyms() 方法,用于返回同义词库中所有的数据。

    print_r($xs->search->getAllSynonyms());
    // Array
    // (
    //     [最好] => Array
    //         (
    //             [0] => 最强
    //             [1] => 最棒
    //         )
    
    //     [最棒] => Array
    //         (
    //             [0] => 最强
    //         )
    
    // )
    
    
    print_r($xs->search->getAllSynonyms(0,0,true));
    // Array
    // (
    //     [最好] => Array
    //         (
    //             [0] => 最强
    //             [1] => 最棒
    //         )
    
    //     [最棒] => Array
    //         (
    //             [0] => 最强
    //         )
    
    // )

    这个方法有三个参数,前两个参数是分页用的,第一个是 limit ,默认 100 条,第二个是偏移从 0 开始,这两个不多解释了。第三个参数是一个布尔值,用于指定是否显示词根词。这个东西对中文没效果的,下一小节我们看英文的同义词效果时,会看到它的效果。


    好了,最后还剩一个删除同义词,没什么多说的了。

    $xs->index->delSynonym('最好', '最强');
    
    // 等一会再试
    print_r($xs->search->getAllSynonyms(0,0,true));
    // Array
    // (
    //     [最好] => Array
    //         (
    //             [0] => 最棒
    //         )
    
    //     [最棒] => Array
    //         (
    //             [0] => 最强
    //         )
    
    // )
    print_r($xs->search->setAutoSynonyms()->search('最好'));
    // 只能查到两条了

    大家自己试试效果吧。注意,同义词相关的方法都是和增、删、改索引数据一样走异步的。同时,它们也可以走缓冲区的,就是可以进入 openBuffer() 中走批量操作。


    同义词相关的操作中有一些是 XSSearch 中的,因此在后面学习搜索相关的内容时,关于同义词部分的内容就不会再重复讲解了。

    索引工具操作同义词及英文词根效果

    除了在代码中操作同义词之外,PHP SDK 中提供的 Indexer.php 索引工具也可以方便地操作索引。直接使用 --add-synonym 就可以添加同义词,使用 --del-synonym 可以删除同义词。

    > php vendor/hightman/xunsearch/util/Indexer.php ./config/5-zyarticle-test1.ini --add-synonym=好看:漂亮,好看:美丽,好看:靓仔,懵圈:懵逼,懵圈:傻了
    报告:开始添加同义词记录 5 条...
    刷新索引提交 ...

    参数格式还是比较好懂的吧,原词:同义词,然后使用逗号进行分隔。接着,使用 Quest.php 查询工具的 --list-synonyms 就可以查看当前索引项目下的所有同义词库。

    > php vendor/hightman/xunsearch/util/Quest.php --show-query ./config/5-zyarticle-test1.ini --list-synonyms
    报告:开始添加同义词记录 5 条....ini --list-synonyms   
       原词                             同义词
    --------------------------------------------------------
       1. 好看                          漂亮, 美丽, 靓仔
       2. 懵圈                          傻了, 懵逼
       3. 最好                          最强, 最棒
       4. 最棒                          最强

    接下来再试试删除的效果,前面加同义词的时候大意了,“好看”和“靓仔”的关系貌似不是很合适,另外整个懵圈都不想要了。

    > php vendor/hightman/xunsearch/util/Indexer.php ./config/5-zyarticle-test1.ini --del-synonym=懵圈,好看:靓仔
    报告:开始删除同义词记录 2 条...
    刷新索引提交 ...

    嗯,这下就没什么问题了。

    > php vendor/hightman/xunsearch/util/Quest.php --show-query ./config/5-zyarticle-test1.ini --list-synonyms
       原词                             同义词
    --------------------------------------------------------
       1. 好看                          漂亮, 美丽
       2. 最好                          最强, 最棒
       3. 最棒                          最强

    小伙伴们在自己测试的时候应该会发现,使用 SDK 工具添加或者删除同义词是马上生效的,这个其实是通过刷新服务端缓冲区来实现的,后面我们也会学到。


    接下来,我们测试一下英文同义词。

    > php vendor/hightman/xunsearch/util/Indexer.php ./config/5-zyarticle-test1.ini --add-synonym=find:search 
    报告:开始添加同义词记录 1 条...
    刷新索引提交 ...
    > php vendor/hightman/xunsearch/util/Quest.php --show-query ./config/5-zyarticle-test1.ini --synonym "finding"
    --------------------
    解析后的 QUERY 语句:Query((Zfind@1 SYNONYM Zsearch@67))
    --------------------

    这回在查看的时候,我们给 --list-synonyms 加上一个等于 stemmed 的值,然后结果会多出来一些内容。

    > php vendor/hightman/xunsearch/util/Quest.php --show-query ./config/5-zyarticle-test1.ini --list-synonyms=stemmed
       原词                             同义词
    --------------------------------------------------------
       1. Zfind                         Zsearch
       2. Zwww                          Zweb
       3. find                          search
       4. www                           web
       5. 好看                          漂亮, 美丽
       6. 最好                          最强, 最棒
       7. 最棒                          最强
       8. 网络                          web

    怎么有了一个 Z 开头的 find ?其实呀,这就是我们前面说过的那个词根词的显示效果。词根词的作用就是在英文中,会有时态、比较级这些语法,比如搜索 “finding” ,默认英文分词和进行词法分析时,会转换成词根 “find” 。同理,对于同义词来说,也会直接将对应同义词的词根加上,搜索 “finding” 时,同时搜索的是 “find” 和 “search” 这两个词根。

    > php vendor/hightman/xunsearch/util/Quest.php --show-query ./config/5-zyarticle-test1.ini "finding" --synonym
    --------------------
    解析后的 QUERY 语句:Query((Zfind@1 SYNONYM Zsearch@67))
    --------------------

    这下明白词根的含义了吧,同时我们也顺便说明了,在英文搜索时,XS 已经帮我们处理好了英文的词根问题。

    默认同义词

    除了我们添加的同义词库以外,XS 还有一些默认的同义词库,比如搜索下面这个词,它的同义词是两个拆开的词。

    > php vendor/hightman/xunsearch/util/Quest.php --show-query ./config/5-zyarticle-test1.ini "项目管理"
    --------------------
    解析后的 QUERY 语句:Query((项目管理@1 SYNONYM (项目@78 AND 管理@79)))

    注意,“项目管理” 是 SCWS 中的一个词项,不是短语,不会再次分词的,它的默认同义词是同时包含 “项目” 和 “管理” 的,注意中间的 “AND” 。这个 “AND” 表示的就是同时包含 “项目” 和 “管理” 这两个词的内容,等同于 “项目管理” ,一样可以被搜索到。如果是短语使用同义词搜索,也会有特殊的效果,比如:

    > php vendor/hightman/xunsearch/util/Quest.php --show-query ./config/5-zyarticle-test1.ini "印度人爱吃咖喱" --synonym
    --------------------
    解析后的 QUERY 语句:Query(((印度人@1 SYNONYM (印度@78 AND 度人@79)) AND 爱吃@2 AND 咖喱@3))
    
    > php vendor/hightman/xunsearch/util/Quest.php --show-query ./config/5-zyarticle-test1.ini "印度人爱吃咖喱"
    --------------------
    解析后的 QUERY 语句:Query(((印度人@1 SYNONYM (印度@78 AND 度人@79)) AND 爱吃@2 AND 咖喱@3))
    --------------------

    看出效果了吧,即使我们不加 --synonym ,在 XS 进行智能词法分析的时候,也会对一些特殊词或者短语词进行同义词拆分。那么,我们可以自己定义这样带 “AND” 效果的同义词吗?可以,但必须还是针对词项,不能是短语。

    > php vendor/hightman/xunsearch/util/Indexer.php ./config/5-zyarticle-test1.ini --add-synonym=牛顿:"物理 名人 先驱 运动 伟大 "
    
    > php vendor/hightman/xunsearch/util/Quest.php --show-query ./config/5-zyarticle-test1.ini "牛顿" --synonym
    --------------------
    解析后的 QUERY 语句:Query((牛顿@1 SYNONYM (物理@78 AND 名人@78 AND 先驱@78 AND 运动@78 AND 伟大@78)))
    --------------------

    其它属性方法

    关于索引对象 XSIndex 剩余部分的内容就不多了,我们就快速的看一下。继承自 XSServer 的部分我们就不多说了,之前已经详细的学习过了。只学它自己拥有的一些属性和方法。

    公共属性

    首先是它的 public 属性,在 XSIndex 对象中,只有两个公共属性。

    var_dump($xs->index->customDict); // string(0) ""
    var_dump($xs->index->scwsMulti); // int(3)

    customDict 属性是自定义字典,它有对应的 setCustomDict() 和 getCustomDict() 方法,因此这个属性就是可读写的。


    scwsMulti 属性表示当前索引库的 SCWS 复合分词等级,默认是 3 ,无特殊情况的话不用改它。


    这两个属性都是和分词相关的,后面在详细学习分词相关的内容时再进行详细的说明,这里大家先看一下就好。

    addExdata() 批量提交数据

    addExdata() ,是一个公共方法,但平常我们用不上。它用于批量提交索引命令封包数据,把多个命令封包内容连续保存为文件或变量,然后一次性提交以减少网络开销提升性能。它是 openBuffer() 和 closeBuffer() 这两个批量提交命令的基础函数。


    具体来说整个流程是:openBuffer() 的参数会设置一个 _bufSize 变量,当增、删、改操作看到 _bufSize 变量大于 0 时,就会调用 appendBuffer() 方法,在这个方法中,将操作命令,也就是序列化(字符串化)之后的 XSCommand 对象放到 _buf 变量中。同时,它还会判断 _buf 长度是否大于 _bufSize 设定的长度。 如果超过了,就直接提交了,否则,继续向 _buf 中添加内容。


    如果我们调用了 closeBuffer() ,或者 _buf 变量长度超过了 _bufSize ,那么,就会执行 addExdata() 进行提交了。实际上就是去执行 execCommand() 提交整个 _buf 变量里的所有命令内容。


    这下,是不是对整个批量提交的过程更清晰了?这个方法我们就不演示了,自己在外面拼接组合 XSCommand 对象没什么必要,大家可以自己看下源码哦。

    addServer() 增加同步索引服务器

    这个方法是用于为当前索引项目增加服务器信息的,就是我们在讲索引配置时说过的,可以通过配置文件直接配置多台索引服务器,然后在添加数据的时候,会同时向这些索引服务器写入数据。而这个 addServer() 方法就是可以动态地添加。具体的用法我就不演示了,它就是需要一个配置参数,格式是 “服务器:端口号” 。


    索引服务器信息添加后会保存在 XSIndex 类的静态变量 $_adds 中。所有的操作在最后调用 XSIndex 的 execCommand() 时,都会遍历这个静态变量,从而向每一个索引服务器都发送相同的指令数据。这样就实现了同步向所有的索引服务进行写操作的功能。


    有移除的方法吗?抱歉,真没有。说实话,大部分情况下,还是直接使用配置文件的配置会好一些。仅有一些特殊情况下,可能需要从代码层面进行动态地添加,比如说临时的加备份服务器之类的。

    flashIndex() 强制刷新服务端索引

    前面我们就看到过了,SDK 提供的 Indexer.php 工具添加同义词时,它会提示一个“刷新索引提交”,然后我们马上就可以查到新添加的同义词内容。而我们自己在 PHP 代码中的操作则是异步的要等一会才能生效。其实呀,SDK 工具就是通过强制刷新服务端索引的方式来让索引快速生效的。我们可以尝试先正常添加一条数据,然后使用 SDK 工具查看一下服务端索引的运行信息,使用 Indexer.php 工具的 --info 参数。

    > php vendor/hightman/xunsearch/util/Indexer.php ./config/5-zyarticle-test1.ini --info
    ---------- SERVER INFO BEGIN ----------
    {
      id:"indexd", uptime:108237, num_burst:1, max_burst:1,
      num_accept:47, aps:0.0, num_task:720759, tps:6.7,
      sock:7, name:"zyarticle", home:"data/zyarticle", rcv_size:8,
      flag:0x0000, version:"1.4.17"
    }
    DBS: [db] -> [NULL]
    CMDS:
      -> {NULL}
    ---------- SERVER INFO END ----------
    数据库名:db[0x0000]
    队列数据:1条
    导入进程:无

    在这其中,最重要的是看到后面的队列数据有 1 条,表示我们提交的数据在服务端也是进入到了一个队列缓冲区中等待消费的,消费之后才能在前台搜索到。还有一种情况,就是缓冲区满了,也会直接写入,另外就是在一定时间内,没有新数据过来,也会开始消费。最后,就是通过我们的手动刷新,直接消费。那么我们就来试一下手动刷新,使用 flushIndex() 方法。

     $id = uniqid();
     $xs->index->add(new XSDocument([
     'id'=>$id,
     'title'=>'JavaScript才是最牛X的',
     'content'=>'服不服?',
     ]));
     $xs->index->flushIndex();

    马上使用 SDK 的 Quest.php 进行搜索,现在一下就出现数据了。同时查看服务端索引状态,队列数据也是 0 条了。

    > php vendor/hightman/xunsearch/util/Indexer.php ./config/5-zyarticle-test1.ini --info
    ……………………
    队列数据:0条
    ……………………

    强制刷新索引很好呀,为啥不每次都直接刷新了,这样我们不就可以实时查到数据了嘛。这个呀,还是性能的取舍问题。XS 使用异步的缓冲队列,目的应该也是尽量减少大量数据写入时的磁盘性能问题。先入队,再消费,通过内存队列的方式来异步实现数据的落盘,避免落盘时间长导致长时间的 Socket 占用。我猜的哈,真实情况是不是这样希望有大佬能去看一下 XS 服务端的源码然后再回来好好跟我们这帮小菜鸡讲讲哦。

    flushLogging() 强制刷新服务端搜索日志

    搜索日志又是什么鬼?这个东西和我们后面在搜索技巧中要学习到的 热门推荐、相关搜索、拼音搜索、纠错建议 等功能有关的。在这里我们先看下在索引这边它有什么功能,其它具体的内容我们后面再细说。


    先看一下目录的相关搜索词库。

    > php vendor/hightman/xunsearch/util/Logger.php ./config/5-zyarticle-test1.ini "最强"
    序 相关搜索词(最强)                          次数      
    --------------------------------------------------
     1. 最强                                     5
    

    就是我们上面测试过的最强,搜了好几次,然后我们再搜索两次最强,查看次数还是 5 ,接着就使用下面的函数刷新一下。

    var_dump($xs->index->flushLogging()); // true

    现在结果变成了 7 。这下明白啥意思了吧,也就是刷新一下对应的搜索日志库。

    > php vendor/hightman/xunsearch/util/Logger.php ./config/5-zyarticle-test1.ini "最强"
    序 相关搜索词(最强)                          次数      
    --------------------------------------------------
     1. 最强                                     7

    好了,点到为止,这个搜索日志库是我们下一大章节的内容了。现在先别急,后面学到的时候,能想起来刷新搜索日志库是在索引对象这边的这个 flushLogging() 方法就好啦。

    总结

    说是没什么东西了,结果随便一写又是一大长篇的内容。不过到此为止,咱们对于索引管理对象,也就是 XSIndex 对象(类)的学习也就结束了。接下来要进入到的,就是另一块大的章节,搜索技巧的学习。


    测试代码:


    https://github.com/zhangyue0503/dev-blog/blob/master/xunsearch/source/10.php


    参考文档:


    http://www.xunsearch.com/doc/php/api/XSIndex


    http://www.xunsearch.com/doc/php/guide/special.synonym

    视频链接

    微信文章地址:https://mp.weixin.qq.com/s/2TK08uXrwqTqvrsewcXpOw

    B站视频地址:https://www.bilibili.com/video/BV1HU411F78Z/

    搜索
    关注