简介

disjunction max query 简称 dis_max,是分离最大化查询的意思。注意这个名字中的两个重点:分离、最大化。

  • disjunction(分离)的含义是:表示把同一个文档中的每个字段上的查询都分开,分别进行算分操作。
  • max(最大化): 是将多个字段查询的得分的最大值作为最终评分返回。

所以 disjunction max query 的效果是:将所有与任一查询匹配的文档作为结果返回,但是只将最佳匹配的得分作为查询的算分结果进行返回。不过其他匹配的字段可以使用 “tie_breaker” 参数来进行“维权”。

准备数据

  1. 为帖子数据增加content字段 ```json DELETE /article

POST /article/_bulk { “create”: { “_id”: “1”} } {“title” : “elasticsearch” } { “create”: { “_id”: “2”} } {“title” : “java”} { “create”: { “_id”: “3”} } {“title” : “elasticsearch”} { “create”: { “_id”: “4”} } {“title” : “hadoop”} { “create”: { “_id”: “5”} } {“title” : “spark”}

POST /article/_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”} }

  1. 2. 搜索titlecontent中包含javasolution的帖子
  2. 下面这个就是multi-field搜索,多字段搜索
  3. ```json
  4. GET /article/_search
  5. {
  6. "query": {
  7. "bool": {
  8. "should": [
  9. {
  10. "match": {
  11. "title": "java solution"
  12. }
  13. },
  14. {
  15. "match": {
  16. "content": "java solution"
  17. }
  18. }
  19. ]
  20. }
  21. }
  22. }
  23. # 输出
  24. {
  25. "took" : 35,
  26. "timed_out" : false,
  27. "_shards" : {
  28. "total" : 1,
  29. "successful" : 1,
  30. "skipped" : 0,
  31. "failed" : 0
  32. },
  33. "hits" : {
  34. "total" : {
  35. "value" : 3,
  36. "relation" : "eq"
  37. },
  38. "max_score" : 2.4211318,
  39. "hits" : [
  40. {
  41. "_index" : "article",
  42. "_type" : "_doc",
  43. "_id" : "2",
  44. "_score" : 2.4211318,
  45. "_source" : {
  46. "title" : "java",
  47. "content" : "i think java is the best programming language"
  48. }
  49. },
  50. {
  51. "_index" : "article",
  52. "_type" : "_doc",
  53. "_id" : "5",
  54. "_score" : 1.4233949,
  55. "_source" : {
  56. "title" : "spark",
  57. "content" : "spark is best big data solution based on scala ,an programming language similar to java"
  58. }
  59. },
  60. {
  61. "_index" : "article",
  62. "_type" : "_doc",
  63. "_id" : "4",
  64. "_score" : 0.79423964,
  65. "_source" : {
  66. "title" : "hadoop",
  67. "content" : "elasticsearch and hadoop are all very good solution, i am a beginner"
  68. }
  69. }
  70. ]
  71. }
  72. }

分析

  1. 结果分析

期望的是doc5排在了前面,结果是doc2排在了前面
算一下doc2的分数

  1. { match”: { title”: java solution }},针对doc2,是有一个分数的
  2. { match”: { content”: java solution }},针对doc2,也是有一个分数的

所以是两个分数加起来,比如说,1.0 + 1.3 = 2.3

算一下doc5的分数

  1. { match”: { title”: java solution }},针对doc5,是没有分数的
  2. { match”: { content”: java solution }},针对doc5,是有一个分数的

所以说,只有一个query是有分数的,比如1.4

处理

  1. 解决

如果不是简单将每个字段的评分结果加在一起, 最佳匹配 字段的评分作为查询的整体评分,结果会怎样?这样返回的结果可能是: 同时 包含 而是将 java 和 solution 的单个字段比反复出现相同词语的 多个不同字段有更高的相关度。

  • best_fields策略,就是说,搜索到的结果,应该是某一个field中匹配到了尽可能多的关键词,被排 在前面;而不是尽可能多的field匹配到了少数的关键词,排在了前面
  • dis_max语法,直接取多个query中,分数最高的那一个query的分数即可 ```json { “match”: { “title”: “java solution” }},针对doc2,是有一个分数的,1.0 { “match”: { “content”: “java solution” }},针对doc2,也是有一个分数的,1.3 取最大分数,1.3

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

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

  1. ```json
  2. GET /article/_search
  3. {
  4. "query": {
  5. "dis_max": {
  6. "queries": [
  7. {
  8. "match": {
  9. "title": "java solution"
  10. }
  11. },
  12. {
  13. "match": {
  14. "content": "java solution"
  15. }
  16. }
  17. ]
  18. }
  19. }
  20. }

其他字段作为辅助

  1. GET /article/_search
  2. {
  3. "query": {
  4. "dis_max": {
  5. "queries": [
  6. {
  7. "match": {
  8. "title": "java solution"
  9. }
  10. },
  11. {
  12. "match": {
  13. "content": "java solution"
  14. }
  15. }
  16. ],
  17. "tie_breaker": 0.2
  18. }
  19. }
  20. }
  1. 获得最佳匹配语句的评分 score。
  2. 将其他匹配语句的评分与 tie breaker相乘
  3. 对以上评分求和并规范化

tier breaker是一个介于0-1之间的浮点数。

  • 0 代表使用最佳匹配
  • 1代表所有语句同等重要

而这个例子中的tie_breaker会重新让dis_max考虑到其他field的得分影响,比如下面的0.4,表示最终的doc得分会考虑其他match的影响,但是它的影响会被弱化成原来的0.4。

GET /your_index/your_type/_search
{   
    # 基于 tie_breaker 优化dis_max
    # tie_breaker可以使dis_max考虑其它field的得分影响
    "query": { 
     # 直接取下面多个query中得分最高的query当成最终得分
     # 这也是best field策略
     "dis_max": { 
        "queries":[
           {"match":{"name":"关注"}},
           {"match":{"content":"白日梦"}}
        ],
        "tie_breaker":0.4
     }
    }
}

多字段查询使用

GET /book/_search
{
  "query": {
    "multi_match": {
      "type": "best_fields",
      "tie_breaker": 0.2,
      "query": "elasticsearch",
      "minimum_should_match": "20%",
      "fields": ["description","name"]
    }
  }
}

简单例子

POST books/_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "term": {
            "name": {
              "value": "linux"
            }
          }
        },
        {
          "term": {
            "intro": {
              "value": "kernel"
            }
          }
        }
      ],
      "tie_breaker": 0.9
    }
  }
}

如上示例,我们查询书名中出现 “linux” 或者书本简介中出现 “kernel” 的文档,而最终返回的相关性评分将以匹配 “linux” 或者匹配 “kernel” 中最大的那个评分为准。
在介绍 mutil-match 的时候也有一个 tie_breaker 参数,其作用跟本实例中的一样,今天我们来复习一下。 当指定 “tie_breaker” 的时候,算分结果将会由以下算法来决定:

  1. 令算分最高的字段的得分为 s1
  2. 令其他匹配的字段的算分 * tie_breaker 的和为 s2
  3. 最终算分为:s1 + s2

“tie_breaker” 的取值范围为:[0.0, 1.0]。当其为 0.0 的时候,按照上述公式来计算,表示使用最佳匹配字段的得分作为相关性算分。当其为 1.0 的时候,表示所有字段的得分同等重要。当其在 0.0 到 1.0 之间的时候,代表其他字段的得分也需要参与到总得分的计算当中去。通俗来说就是其他字段可以使用 “tie_breaker” 来进行“维权”