Elasticsearch 入门教程 – 近似搜索

在开发场景中,我们需要搜索文章"java spark" 的短语, 也就是term下的java spark(不分词),我们可以用phrase match来搜索;另一方面,如果我们想让java和spark距离很近的doc优先返回,距离越近对应的relevance score能够更高,我们可以使用proximity match来搜索。

match query,搜索java spark

{
	"match": {
		"content": "java spark"
	}
}

这样的结果是:包含Java和spark的文章都会搜索出来;

GET /forum/article/_search
{
  "query": {
    "match_phrase": {
      "content": "java spark"
    }
  }
}

这样的搜索结果是:必须都包含java spark才会搜索出来,短语搜索;   如果我们想搜索,java与spark 两词相近的文章搜索出来那么怎么实现;

slop关键字:

      意思:要经过几次移动才能与一个document匹配,这个移动的次数;

     举例:

      短语:"我爱北京新中国",搜索"我北" slop=1 搜索结果一样可以查询到;

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

    slop搜索下,关键词离的越近,relevance score就会越高,排序就会越靠前;

     在开发中,直接用match_phrase短语搜索,会导致必须所有term都在doc field中出现,而且距离在slop限定范围内,才能匹配上,

match phrase,proximity match,要求doc必须包含所有的term,才能作为结果返回;如果某一个doc可能就是有某个term没有包含,那么就无法作为结果返回

java spark –> hello world java –> 就不能返回了

java spark –> hello world, java spark –> 才可以返回

近似匹配的时候,召回率比较低,精准度太高了! 有什么样的方式可以提高match_phrase的召回率和精准度??????

解决方式:

可以用bool组合match query和match_phrase query 来实现;

GET /forum/article/_search
{
  "query": {
    "bool": {
      "must": {
        "match": { 
          "title": {
            "query":                "java spark" --> java或spark或java spark,java和spark靠前,但是没法区分java和spark的距离,也许java和spark靠的很近,但是没法排在最前面
          }
        }
      },
      "should": {
        "match_phrase": { --> 在slop以内,如果java spark能匹配上一个doc,那么就会对doc贡献自己的relevance score,如果java和spark靠的越近,那么就分数越高
          "title": {
            "query": "java spark",
            "slop":  50
          }
        }
      }
    }
  }
}

image.png

近似匹配上的性能优化:

       match query的性能比phrase match和proximity match(有slop)要高很多,因为后两者要计算position的距离。match query比phrase match的性能要高10倍,比proximity match的性能要高20倍。但是es的性能一般在毫秒级别,这些近似操作也是可以接受的。

对于proximity query的优化,一般就是减少要进行proximity match搜索的doc数量。主要思路就是用match query先过滤出所需要的数据,然后再用proximity match来根据term距离提高doc的分数,但是我们可以控制proximity match对doc有影响的doc数量,因为用户一般会分页查询只会查询前几页的数据。

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

重打分机制:

       match:1000个doc,其实这时候每个doc都有一个分数了; proximity match,前50个doc,进行rescore,重打分,即可; 让前50个doc,term举例越近的,排在越前面

      默认情况下,match也许匹配了1000个doc,proximity match全都需要对每个doc进行一遍运算,判断能否slop移动匹配上,然后去贡献自己的分数

但是很多情况下,match出来也许1000个doc,其实用户大部分情况下是分页查询的,所以可能最多只会看前几页,比如一页是10条,最多也许就看5页,就是50条

     proximity match只要对前50个doc进行slop移动去匹配,去贡献自己的分数即可,不需要对全部1000个doc都去进行计算和贡献分数