zoukankan      html  css  js  c++  java
  • Elasticsearch系列---近似匹配

    概要

    前面的match查询只能告诉我们,搜索的文档里有这些关键词,但无法告知词语之间的顺序,而不同的词语顺序表达的意思可能完全相反。我们想要的,是跟我们期望搜索的语义要相似,这就需要短语匹配和近似匹配来控制了。

    短语搜索

    短语搜索即把一小段话完完整整地进行搜索,必须保证被搜索的文档内有一模一样的才行,如下:

    GET /music/children/_search
    {
      "query": {
        "match_phrase": {
          "content": "in the morning"
        }
      }
    }
    

    Elasticsearch对短语搜索必须要满足如下要求:

    1. in the morning 三个单词必须要全部出现
    2. the的位置比in大1
    3. morning的位置比the大1

    任何一个不成立,则搜索不到匹配的结果。意思上是说,短语搜索除了关注关键词是否出现,还关心被搜索文档中这几个关键词的位置,我们可以用调度命令看一下词条的位置:

    GET /_analyze
    {
      "analyzer":"standard",
      "text": "in the morning"
    }
    

    响应结果:

    {
      "tokens": [
        {
          "token": "in",
          "start_offset": 0,
          "end_offset": 2,
          "type": "<ALPHANUM>",
          "position": 0
        },
        {
          "token": "the",
          "start_offset": 3,
          "end_offset": 6,
          "type": "<ALPHANUM>",
          "position": 1
        },
        {
          "token": "morning",
          "start_offset": 7,
          "end_offset": 14,
          "type": "<ALPHANUM>",
          "position": 2
        }
      ]
    }
    

    留意一下tokens显示的position信息,position连续说明这三个词紧靠在一起,搜索文档时,也只有命中这三个词的文档,position按次序连接才能匹配得上。

    近似匹配

    近似匹配是在短语匹配的基础上,短语匹配有些严格,要求位置必须按照搜索字符串来,近似匹配则有一些变通,允许短语之间的位置有变化,变化的程度由参数slop决定。

    例如:

    GET /music/children/_search
    {
      "query": {
        "match_phrase": {
          "content": {
            "query": "you me",
            "slop": 1
          }
        }
      }
    }
    

    slop含义

    • query string搜索文本中的几个term,要经过n次移动才能与一个document匹配,这个移动的次数,就是slop。
    • slop表示移动的最大次数,离得越近的,分数就会越高。
    • term之间交换位置也行的,但是slop得大一些。

    我们以字符串"you make me happy"举例,画个移动表格:

    pos 1 pos 2 pos 3 pos 4
    DOC you make me happy
    query you me
    slop 1 you -> me

    me只需要移动一步,就能匹配上,所以slop 1能查询到结果。

    演示样例有限,我们把搜索串改成"me you",模拟颠倒次序的slop,但slop至少要是3才行:

    GET /music/children/_search
    {
      "query": {
        "match_phrase": {
          "content": {
            "query": "me you",
            "slop": 3
          }
        }
      }
    }
    

    为什么是3,我们以字符串"you make me happy"再画个移动表格

    pos 1 pos 2 pos 3 pos 4
    DOC you make me happy
    query you me
    slop 1 me/you <-
    slop 2 you -> me
    slop 3 you -> me

    注意slop 1时,me和you共占用同一个位置,二者交换一下顺序,就需要slop为2。

    近似匹配,就是使用了slop参数的短语匹配。

    数组类型的slop

    我们music索引中的tags字段,设计时是数组类型的,如果我们对这个字段进行近似匹配,结果会是怎么样:

    _id为1的文档数据,tags是这样的:
    "tags": ["enlighten","gymbo","friend"]

    按照slop的偏移量,slop为1应该是可以匹配上

    GET /music/children/_search
    {
      "query": {
        "match_phrase": {
          "tags": {
            "query": "enlighten friend",
            "slop": 1
          }
        }
      }
    }
    

    结果竟然是空,怎么回事呢?我们分析一下该field的tokens信息:

    GET /music/_analyze
    {
      "field": "tags",
      "text": ["enlighten","gymbo","friend"]
    }
    
    

    响应

    {
      "tokens": [
        {
          "token": "enlighten",
          "start_offset": 0,
          "end_offset": 9,
          "type": "<ALPHANUM>",
          "position": 0
        },
        {
          "token": "gymbo",
          "start_offset": 10,
          "end_offset": 15,
          "type": "<ALPHANUM>",
          "position": 101
        },
        {
          "token": "friend",
          "start_offset": 16,
          "end_offset": 22,
          "type": "<ALPHANUM>",
          "position": 202
        }
      ]
    }
    

    注意一下position的值,数组元素之间,position间隔都是100。

    6.x的版本,position_increment_gap参数值默认是100,表示元素之间,步长为100,毕竟没有人近似查询时会关系slop大于100的结果。之前老版本这个值默认是1,出现了很多意外的问题,6.x后算是对此问题的修复。

    召回率与精准度的平衡

    召回率:假设有100个doc,你搜索一段文本,能返回多个doc,与总doc的比例,就是召回率,recall。

    精准度:你搜索一段文本love me,能不能尽可能让包含这两个关键字的doc,或者离得近的doc先返回,排在前面,就是精准度,precision。

    这二者看似有些矛盾,想要召回率高,精准度可能就低,反过来也是如此,精准度越是高的,召回率就越低,如何找一个平衡点?

    我们一般的原则是优先满足召回率,同时兼顾精准度。比如match和match_phrase同时使用:

    GET /music/children/_search
    {
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "content": "you gymbo"
              }
            }
          ],
          "should": [
            {
              "match_phrase": {
                "content": {
                  "query": "loves gymbo",
                  "slop": 2
                }
              }
            }
          ]
        }
      }
    }
    

    我们可以看到加了match_phrase条件后,_id为3的_score由0.39556286上升到0.65997654。

    rescoring优化性能

    match查询和短语搜索(近似搜索)区别

    match查询:只要简单的匹配到了一个term,就可以理解将term对应的doc作为结果返回,扫描倒排索引,扫描到了就表示有结果匹配了。

    短语搜索(phrase match):先扫描所有term的doc list,找到包含所有term的doc list,然后对每个doc都计算每个term的position,是否符合指定的范围。

    近似搜索(proxmity match):slop需要进行复杂的运算,来判断能否通过slop移动,匹配一个doc

    match query性能要高一些,比phrase match高10倍,比proximity match高20倍。不过Elasticsearch内搜索的效率基本控制在几毫秒内,10、20倍不过也百十来毫秒,哪怕是繁忙的ES集群,也不过一两百毫秒,实际上完全可用。

    如何优化proximity match?

    一个查询可能会匹配成千上万的结果,但我们的用户很可能只对结果的前几页感兴趣,所以优化的思路就是proximity只要符合match条件的前几十个文档进行评分,而不是全部数据,速度自然能大大加快。

    resocre重打分: proximity match,前20个doc进行rescore即可。

    语法示例:

    GET /music/children/_search
    {
      "query": {
        "match": {
          "content": "gymbo you"
        }
      },
      "rescore": {
        "window_size": 20,
        "query": {
          "rescore_query": {
            "match_phrase": {
              "content": {
                "query": "gymbo you",
                "slop": 1
              }
            }
          }
        }
      }
    }
    
    1. match 查询决定哪些文档将包含在最终结果集中,并通过TF/IDF排序。
    2. window_size 是每一分片进行重新评分的顶部文档数量,例子中取20个。

    小结

    本篇主要介绍近似匹配的常规玩法,以及rescoring优化性能的思路。

    专注Java高并发、分布式架构,更多技术干货分享与心得,请关注公众号:Java架构社区
    可以扫左边二维码添加好友,邀请你加入Java架构社区微信群共同探讨技术
    Java架构社区

  • 相关阅读:
    C#多线程编程实战(二)
    C#为什么要多线程开发(一)
    海康威视实时预览回调PS流用EasyRTMP向RTMP服务器推流中视频数据处理的代码
    CentOS "libc.so.6: version 'GLIBC_2.14' not found"解决方法,同理'GLIBC_2.15' not found"
    EasyRTMP结合海康HCNetSDK获取海康摄像机H.264实时流并转化成为RTMP直播推流(附源码)
    基于EasyDSS流媒体解决方案创建视频点播、短视频、视频资源库等视频播放系统
    EasyNVR是怎么做到Web浏览器播放RTSP摄像机直播视频延时控制在一秒内的
    EasyNVR depends on ffmpeg,yasm/nasm not found or too old. Use --disable-yasm for a crippledbuild
    EasyDSS流媒体服务器软件支持HTTPS-启用https服务申请免费证书
    EasyDSS流媒体服务器软件(支持RTMP/HLS/HTTP-FLV/视频点播/视频直播)-正式环境安装部署攻略
  • 原文地址:https://www.cnblogs.com/huangying2124/p/12544095.html
Copyright © 2011-2022 走看看