2023-08-08
原文作者:Ressmix 原文地址:https://www.tpvlog.com/article/146

Proximity Match(近似匹配),包含两种类型: phrase matchproximity match 。什么情况下需要使用近似匹配?我们先看下下面的检索需求:

    # doc1
    java is my favourite programming language, and I also think spark is a very good big data system.
    # doc2
    java spark are very related, because scala is spark's programming language and scala is also based on jvm like java.

假设我们有上述两条document,希望检索出包含“java spark”关键字的doc,但是必须满足以下任一条件:

  • java spark,就靠在一起,中间不能插入任何其他字符;
  • java和spark两个单词靠的越近,doc的分数越高,排名越靠前。

思考一下,如果使用全文检索中的math query,能否满足我们的需求?答案是不能,此时就要用到近似匹配了。

一、phrase match

Phrase Match(短语匹配),就是将搜索词中的多个term作为一个短语,一起去搜索,只有包含这个短语的doc才会作为结果返回,不像是match query,任何一个term匹配就会返回结果。

Phrase Match的基本语法如下,只有包含“java spark”这个短语的doc才会被返回,对于本章开头的那两条doc,只有doc2满足条件:

    GET /forum/_search
    {
        "query": {
            "match_phrase": {
                "title": {
                    "query": "java spark",
                    "slop":  1
                }
            }
        }
    }

1.1 基本原理

Elasticsearch在建立倒排索引时,会记录每个term在文本内容中的位置。比如我们有下面两条doc:

    doc1: hello world, java spark        
    doc2: hi, spark java

建立完的倒排索引包含以下内容,hello这个term出现在doc1的position 0,以此类推:

Term Doc1中的位置 Doc2中的位置
hello doc1(0) N
wolrd doc1(1) N
java doc1(2) doc2(2)
spark doc1(3) doc2(1)

当使用Phrase Match(短语匹配)时,步骤如下:

  1. Elasticsearch会首先对短语分词,"java spark"拆分为"java"和"spark" ;
  2. 筛选出“java”和“spark”都存在的doc,也就是说doc必须包含短语中的所有term,那doc1和doc2都满足;
  3. 后一个短语的position必须比前一个大1,即“spark”的position要比java的position大1,那只有doc1满足条件。

1.2 slop参数

Phrase Match有一个很重要的参数——slop,表示短语中的term,最多经过几次移动才能与一个document匹配,这个移动次数,就是slop。

举个例子,假如有下面这样一条doc,搜索的短语是"spark data":

    spark is best big data solution based on scala.

那么slop=3时,就可以匹配到,可以看下面的移动步骤:

    spark         is         best         big         data...
    spark        data
                     -->data
                                 -->data
                                              -->data

注意,移动的方向可以是双向的,比如搜索的短语是"data spark",那么slop=5时,也可以匹配到:

    spark         is         best         big         data...
    data        spark
    spark  <-->    data
    spark             -->data
    spark                          -->data
    spark                                       -->data

当使用Phrase Match时,term靠的越近,相关度分数会越高。

1.3 rescore

rescore(重打分),通常与match query配合使用。比如,我们有以下请求:

    GET /forum/_search 
    {
      "query": {
        "match": {
          "content": "java spark"
        }
      },
      "rescore": {
        "window_size": 50,
        "query": {
          "rescore_query": {
            "match_phrase": {
              "content": {
                "query": "java spark",
                "slop": 50
              }
            }
          }
        }
      }
    }

使用rescore参数后,会对math query匹配到的结果重新打分,上述window_size表示取前50条记录进行打分,配合phrase match可以使term越接近的短语分数更高,从而既提供了精确度,又提升了召回率。

所谓召回率,就是进行检索时,返回的document的数量,数量越多,召回率越高。近似匹配通常会和match query搭配使用以提升召回率。

1.4 搜索推荐

我们还可以利用match_phrase_prefix进行搜索推荐。搜索推荐的原理跟match_phrase类似,唯一的区别就是把检索词中的最后一个term作为前缀去搜索。

    GET /forum/_search 
    {
      "query": {
        "match_phrase_prefix": {
          "content": {
              "query": "java s",
              "slop": 10,
              "max_expansions": 5
          }
        }
      }
    }

默认情况下,前缀要扫描所有的倒排索引中的term,去查找"s"打头的单词,但是这样性能太差,所以可以用max_expansions限定,"s"前缀最多匹配多少个term,就不再继续搜索倒排索引了。

一般来讲,不推荐使用match_phrase_prefix来实现搜索推荐,因为Elasticsearch会在扫描倒排索引时实时进行前缀匹配,性能很差。如果要实现搜索推荐功能,建议使用ngram分词机制

二、proximity match

讲解完了phrase match,proximity match其实就没什么可讲了,底层原理都是一样的,proximity match可以看成是加了slop参数的phrase match。

三、总结

本章,主要介绍了Elasticsearch中的近似匹配搜索,近似匹配搜索分为 phrase matchproximity match ,底层的原理是一样的,都是通过倒排索引去匹配搜索关键词中的各个term的位置。

阅读全文