22、基于boost的权重控制以及基于dis_max实现best fields策略进行多字段搜索

主要内容:基于boost的权重控制以及基于dis_max实现best fields策略进行多字段搜索

1、基于boost的细粒度搜索条件权重控制

需求:搜索标题中包含java的帖子,同时呢,如果标题中包含hadoop或elasticsearch就优先搜索出来,同时呢,如果一个帖子包含java hadoop,一个帖子包含java elasticsearch,包含hadoop的帖子要比elasticsearch优先搜索出来

知识点,搜索条件的权重,boost,可以将某个搜索条件的权重加大,此时当匹配这个搜索条件和匹配另一个搜索条件的document,计算relevance score时,匹配权重更大的搜索条件的document,relevance score会更高,当然也就会优先被返回来

默认情况下,搜索条件的权重都是一样的,都是1

GET /forum/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "blog"
          }
        }
      ],
      "should": [
        {
          "match": {
            "title": {
              "query": "spark",
              "boost": 5  ##提升spark的权重
            }
          }
        }
      ]
    }
  }
}

2、多shard场景下relevance score不准确问题

如果你的一个index有多个shard的话,可能搜索结果会不准确

不准确的原因就是因为ES默认只在Shard local本地计算IDF,所以有的shard上document包含的关键字多,有的关键字少,进而导致结果不准确。

2、如何解决该问题?

(1)生产环境下,数据量大,尽可能实现均匀分配

数据量很大的话,其实一般情况下,在概率学的背景下,es都是在多个shard中均匀路由数据的,路由的时候根据_id,负载均衡
比如说有10个document,title都包含java,一共有5个shard,那么在概率学的背景下,如果负载均衡的话,其实每个shard都应该有2个doc,title包含java
如果说数据分布均匀的话,其实就没有刚才说的那个问题了

(2)测试环境下,将索引的primary shard设置为1个,number_of_shards=1,index settings

如果说只有一个shard,那么当然,所有的document都在这个shard里面,就没有这个问题了

(3)测试环境下,搜索附带search_type=dfs_query_then_fetch参数,会将local IDF取出来计算global IDF

计算一个doc的相关度分数的时候,就会将所有shard对的local IDF计算一下,获取出来,在本地进行global IDF分数的计算,会将所有shard的doc作为上下文来进行计算,也能确保准确性。但是production生产环境下,不推荐这个参数,因为性能很差。

3、基于dis_max实现best fields策略进行多字段搜索

3.1、添加数据

为帖子数据增加content字段

POST /forum/_bulk
{ "update": { "_id": "1"} }
{ "doc" : {"content" : "i like to write best elasticsearch article"} }
{ "update": { "_id": "2"} }
{ "doc" : {"content" : "i think java is the best programming language"} }
{ "update": { "_id": "3"} }
{ "doc" : {"content" : "i am only an elasticsearch beginner"} }
{ "update": { "_id": "4"} }
{ "doc" : {"content" : "elasticsearch and hadoop are all very good solution, i am a beginner"} }
{ "update": { "_id": "5"} }
{ "doc" : {"content" : "spark is best big data solution based on scala ,an programming language similar to java"} }
3.2、搜索title或content中包含java或solution的帖子

下面这个就是multi-field搜索,多字段搜索

GET /forum/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": "java solution"
          }
        },
        {
          "match": {
            "content": "java solution"
          }
        }
      ]
    }
  }
}
3.3、结果分析

期望的是doc5,结果是doc2,doc4排在了前面

计算每个document的relevance score:每个query的分数,乘以matched query数量,除以总query数量

doc2:

{ "match": { "title": "java solution" }},有分数
{ "match": { "content": "java solution" }},有分数

doc5:

{ "match": { "title": "java solution" }},没有分数
{ "match": { "content": "java solution" }},有分数

doc5匹配的match query数量要少,但是总的query数量是相同的,所以doc5的分数< doc2的分数

3.4、best fields策略,dis_max

best fields策略,就是说,搜索到的结果,应该是某一个field中匹配到了尽可能多的关键词,被排在前面;而不是尽可能多的field匹配到了少数的关键词,排在了前面

dis_max语法,直接取多个query中,分数最高的那一个query的分数即可

{ "match": { "title": "java solution" }},针对doc4,是有一个分数的,1.1
{ "match": { "content": "java solution" }},针对doc4,也是有一个分数的,1.2
取最大分数,1.2

{ "match": { "title": "java solution" }},针对doc5,是没有分数的
{ "match": { "content": "java solution" }},针对doc5,是有一个分数的,2.3
取最大分数,2.3

然后doc4的分数 = 1.2 < doc5的分数 = 2.3,所以doc5就可以排在更前面的地方,符合我们的需要

GET /forum/_search
{
  "query": {
    "dis_max": {            
      "queries": [         
        {
          "match": {
            "title": "java solution"
          }
        },
        {
          "match": {
            "content": "java solution"
          }
        }
      ]
    }
  }
}

使用tie_breaker参数优化dis_max搜索

1、dis_max只取某一个query最大的分数,完全不考虑其他query的分数

2、使用tie_breaker将其他query的分数也考虑进去

3、tie_breaker的值,在0~1之间,是个小数

实战示例:

GET /forum/_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "match": {
            "title": "java beginner"
          }
        },
        {
          "match": {
            "content": "java beginner"
          }
        }
      ],
      "tie_breaker": 0.3
    }
  }
}

你可能感兴趣的:(22、基于boost的权重控制以及基于dis_max实现best fields策略进行多字段搜索)