ElasticSearch 之 dis_max tie_break的应用

1. 插入数据

PUT /my_index/my_type/1
{
    "title": "Quick brown rabbits",
    "body":  "Brown rabbits are commonly seen."
}

PUT /my_index/my_type/2
{
    "title": "Keeping pets healthy",
    "body":  "My quick brown fox eats rabbits on a regular basis."
}

2. 搜索

{
    "query": {
        "bool": {
            "should": [
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}

3. 结果

{
  "hits": [
     {
        "_id":      "1",
        "_score":   0.14809652,
        "_source": {
           "title": "Quick brown rabbits",
           "body":  "Brown rabbits are commonly seen."
        }
     },
     {
        "_id":      "2",
        "_score":   0.09256032,
        "_source": {
           "title": "Keeping pets healthy",
           "body":  "My quick brown fox eats rabbits on a regular basis."
        }
     }
  ]
}

  理论上 2 的文档实际上是咱们须要的,可是搜索的结果不是。要理解缘由,须要知道 bool 查询时如何计算获得其分值的:ui

  1. 运行should子句中的两个查询
  2. 相加查询返回的分值
  3. 将相加获得的分值乘以匹配的查询子句的数量
  4. 除以总的查询子句的数量

  文档1 在两个字段中都包含了 brown,所以两个 match 查询都匹配成功并拥有了一个分值。文档2 在 body 字段中包含了 brown 以及 fox,可是在 title 字段中没有出现任何搜索的单词。所以对 body 字段查询获得的高分加上对 title字段查询获得的零分,而后再乘以匹配的查询子句数量1,最后除以总的查询子句数量2,致使总体分值比文档1 的低。具体计算过程以下:spa

score_1 = (1 + 1) * 2 / 2 = 2
score_2 = (0 + 2) * 1 / 2 = 1

  在这个例子中,title 和 body 字段是互相竞争的。咱们想要找到一个最佳匹配(Best-matching)的字段。

  若是咱们不是合并来自每一个字段的分值,而是使用最佳匹配字段的分值做为整个查询的总体分值呢?这就会让包含有咱们寻找的两个单词的字段有更高的权重,而不是在不一样的字段中重复出现的相同单词。code

4. dis_max 查询

  相比使用bool查询,咱们能够使用dis_max查询(Disjuction Max Query)。Disjuction的意思"OR"(而Conjunction的意思是"AND"),所以Disjuction Max Query的意思就是返回匹配了任何查询的文档,而且分值是产生了最佳匹配的查询所对应的分值:blog

{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Brown fox" }},
                { "match": { "body":  "Brown fox" }}
            ]
        }
    }
}

  它会产生咱们指望的结果:文档

{
  "hits": [
     {
        "_id":      "2",
        "_score":   0.21509302,
        "_source": {
           "title": "Keeping pets healthy",
           "body":  "My quick brown fox eats rabbits on a regular basis."
        }
     },
     {
        "_id":      "1",
        "_score":   0.12713557,
        "_source": {
           "title": "Quick brown rabbits",
           "body":  "Brown rabbits are commonly seen."
        }
     }
  ]
}

5. 最佳字段查询调优

  若是用户搜索的是"quick pets",那么会发生什么呢?两份文档都包含了单词 quick,可是只有文档2 包含了单词 pets。两份文档都没能在一个字段中同时包含搜索的两个单词。it

一个像下面那样的简单 dis_max 查询会选择出拥有最佳匹配字段的查询子句,而忽略其余的查询子句:io

查询语句:
{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Quick pets" }},
                { "match": { "body":  "Quick pets" }}
            ]
        }
    }
}
获得的结果:
{
  "hits": [
     {
        "_id": "1",
        "_score": 0.12713557, 
        "_source": {
           "title": "Quick brown rabbits",
           "body": "Brown rabbits are commonly seen."
        }
     },
     {
        "_id": "2",
        "_score": 0.12713557, 
        "_source": {
           "title": "Keeping pets healthy",
           "body": "My quick brown fox eats rabbits on a regular basis."
        }
     }
   ]
}

  能够发现,两份文档的分值是如出一辙的。class

  咱们指望的是同时匹配了title字段 和 body字段 的文档可以拥有更高的排名,可是结果并不是如此。须要记住:dis_max 查询只是简单的使用最佳匹配查询子句获得的_score搜索

6. tie_breaker

可是,将其它匹配的查询子句考虑进来也是可能的。经过指定tie_breaker参数:数据

查询语句:
{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "Quick pets" }},
                { "match": { "body":  "Quick pets" }}
            ],
            "tie_breaker": 0.3
        }
    }
}
它会返回如下结果:
{
  "hits": [
     {
        "_id": "2",
        "_score": 0.14757764, 
        "_source": {
           "title": "Keeping pets healthy",
           "body": "My quick brown fox eats rabbits on a regular basis."
        }
     },
     {
        "_id": "1",
        "_score": 0.124275915, 
        "_source": {
           "title": "Quick brown rabbits",
           "body": "Brown rabbits are commonly seen."
        }
     }
   ]
}

如今文档2 的分值比文档1 稍高一些。
  tie_breaker参数会让dis_max查询的行为更像是dis_max和bool的一种折中。它会经过下面的方式改变分值计算过程:

  1. 取得最佳匹配查询子句的_score。

  2. 将其它每一个匹配的子句的分值乘以tie_breaker。

  3. 将以上获得的分值进行累加并规范化。

经过tie_breaker参数,全部匹配的子句都会起做用,只不过最佳匹配子句的做用更大。

相关文章
相关标签/搜索