松哥原创的 Spring Boot 视频教程已经杀青,感兴趣的小伙伴戳这里-->松哥要升级 SpringBoot 视频了,看看新增了哪些内容!
ElasticSearch 系列已经整到第 21 篇啦,今天我们来学习 Es 中的复合查询,复合查询这块我打算通过三个视频来和小伙伴们分享,今天我们先来看前两个。
constant_score query & bool query:
dis_max query:
如果大家觉得视频风格还能接受,也可以看看松哥的付费视频:Spring Boot+Vue+微人事视频教程
以下是视频笔记:
注意,笔记只是视频内容的一个简要记录,因此笔记内容比较简单,完整的内容可以查看视频。
17.ElasticSearch 复合查询
17.1 constant_score query
当我们不关心检索词项的频率(TF)对搜索结果排序的影响时,可以使用 constant_score 将查询语句或者过滤语句包裹起来。
GET books/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "name": "java"
        }
      },
      "boost": 1.5
    }
  }
}
17.2 bool query
bool query 可以将任意多个简单查询组装在一起,有四个关键字可供选择,四个关键字所描述的条件可以有一个或者多个。
- 
must:文档必须匹配 must 选项下的查询条件。 
 - 
should:文档可以匹配 should 下的查询条件,也可以不匹配。 
 - 
must_not:文档必须不满足 must_not 选项下的查询条件。 
 - 
filter:类似于 must,但是 filter 不评分,只是过滤数据。 
 
例如查询 name 属性中必须包含 java,同时书价不在 [0,35] 区间内,info 属性可以包含 程序设计 也可以不包含程序设计:
GET books/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "name": {
              "value": "java"
            }
          }
        }
      ],
      "must_not": [
        {
          "range": {
            "price": {
              "gte": 0,
              "lte": 35
            }
          }
        }
      ],
      "should": [
        {
          "match": {
            "info": "程序设计"
          }
        }
      ]
    }
  }
}
这里还涉及到一个关键字,minmum_should_match 参数。
minmum_should_match 参数在 es 官网上称作最小匹配度。在之前学习的 multi_match 或者这里的 should 查询中,都可以设置 minmum_should_match 参数。
假设我们要做一次查询,查询 name 中包含 语言程序设计 关键字的文档:
GET books/_search
{
  "query": {
    "match": {
      "name": "语言程序设计"
    }
  }
}
在这个查询过程中,首先会进行分词,分词结果如下:

分词后的 term 会构造成一个 should 的 bool query,每一个 term 都会变成一个 term query 的子句。换句话说,上面的查询和下面的查询等价:
GET books/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "name": {
              "value": "语言"
            }
          }
        },
        {
          "term": {
            "name": {
              "value": "程序设计"
            }
          }
        },
        {
          "term": {
            "name": {
              "value": "程序"
            }
          }
        },
        {
          "term": {
            "name": {
              "value": "设计"
            }
          }
        }
      ]
    }
  }
}
在这两个查询语句中,都是文档只需要包含词项中的任意一项即可,文档就回被返回,在 match 查询中,可以通过 operator 参数设置文档必须匹配所有词项。
如果想匹配一部分词项,就涉及到一个参数,就是 minmum_should_match,即最小匹配度。即至少匹配多少个词。
GET books/_search
{
  "query": {
    "match": {
      "name": {
        "query": "语言程序设计",
        "operator": "and"
      }
    }
  }
}
GET books/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "term": {
            "name": {
              "value": "语言"
            }
          }
        },
        {
          "term": {
            "name": {
              "value": "程序设计"
            }
          }
        },
        {
          "term": {
            "name": {
              "value": "程序"
            }
          }
        },
        {
          "term": {
            "name": {
              "value": "设计"
            }
          }
        }
      ],
      "minimum_should_match": "50%"
    }
  },
  "from": 0,
  "size": 70
}
50% 表示词项个数的 50%。
如下两个查询等价(参数 4 是因为查询关键字分词后有 4 项):
GET books/_search
{
  "query": {
    "match": {
      "name": {
        "query": "语言程序设计",
        "minimum_should_match": 4
      }
    }
  }
}
GET books/_search
{
  "query": {
    "match": {
      "name": {
        "query": "语言程序设计",
        "operator": "and"
      }
    }
  }
}
17.3 dis_max query
假设现在有两本书:
PUT blog
{
  "mappings": {
    "properties": {
      "title":{
        "type": "text",
        "analyzer": "ik_max_word"
      },
      "content":{
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}
POST blog/_doc
{
  "title":"如何通过Java代码调用ElasticSearch",
  "content":"松哥力荐,这是一篇很好的解决方案"
}
POST blog/_doc
{
  "title":"初识 MongoDB",
  "content":"简单介绍一下 MongoDB,以及如何通过 Java 调用 MongoDB,MongoDB 是一个不错 NoSQL 解决方案"
}
现在假设搜索 Java解决方案 关键字,但是不确定关键字是在 title 还是在 content,所以两者都搜索:
GET blog/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title": "java解决方案"
          }
        },
        {
          "match": {
            "content": "java解决方案"
          }
        }
      ]
    }
  }
}
搜索结果如下:

肉眼观察,感觉第二个和查询关键字相似度更高,但是实际查询结果并非这样。
要理解这个原因,我们需要来看下 should query 中的评分策略:
- 
首先会执行 should 中的两个查询 
 - 
对两个查询结果的评分求和 
 - 
对求和结果乘以匹配语句总数 
 - 
在对第三步的结果除以所有语句总数 
 
反映到具体的查询中:
前者
- 
title 中 包含 java,假设评分是 1.1 
 - 
content 中包含解决方案,假设评分是 1.2 
 - 
有得分的 query 数量,这里是 2 
 - 
总的 query 数量也是 2 
 
最终结果:(1.1+1.2)*2/2=2.3
后者
- 
title 中 不包含查询关键字,没有得分 
 - 
content 中包含解决方案和 java,假设评分是 2 
 - 
有得分的 query 数量,这里是 1 
 - 
总的 query 数量也是 2 
 
最终结果:2*1/2=1
在这种查询中,title 和 content 相当于是相互竞争的关系,所以我们需要找到一个最佳匹配字段。
为了解决这一问题,就需要用到 dis_max query(disjunction max query,分离最大化查询):匹配的文档依然返回,但是只将最佳匹配的评分作为查询的评分。
GET blog/_search
{
  "query": {
    "dis_max": {
      "queries": [
        {
          "match": {
            "title": "java解决方案"
          }
        },
        {
          "match": {
            "content": "java解决方案"
          }
        }
        ]
    }
  }
}
查询结果如下:

在 dis_max query 中,还有一个参数 tie_breaker(取值在0~1),在 dis_max query 中,是完全不考虑其他 query 的分数,只是将最佳匹配的字段的评分返回。但是,有的时候,我们又不得不考虑一下其他 query 的分数,此时,可以通过 tie_breaker 来优化 dis_max query。tie_breaker 会将其他 query 的分数,乘以 tie_breaker,然后和分数最高的 query 进行一个综合计算。
ElasticSearch 系列其他文章:
- 
打算出一个 ElasticSearch 教程,谁赞成,谁反对? 
 - 
ElasticSearch 从安装开始 
 - 
ElasticSearch 第三弹,核心概念介绍 
 - 
ElasticSearch 中的中文分词器该怎么玩? 
 - 
ElasticSearch 索引基本操作 
 - 
ElasticSearch 文档的添加、获取以及更新 
 - 
ElasticSearch 文档的删除和批量操作 
 - 
ElasticSearch 文档路由,你的数据到底存在哪一个分片上? 
 - 
ElasticSearch 并发的处理方式:锁和版本控制 
 - 
ElasticSearch 中的倒排索引到底是什么? 
 - 
ElasticSearch 动态映射与静态映射 
 - 
ElasticSearch 四种字段类型详解 
 - 
ElasticSearch 中的地理类型和特殊类型 
 - 
ElasticSearch 23 种映射参数详解 
 - 
ElasticSearch 如何配置某个字段的权重? 
 - 
ElasticSearch 23 种映射参数详解【3】 
 - 
ElasticSearch 映射模版 
 - 
ElasticSearch 搜索入门 
 - 
ElasticSearch 全文搜索怎么玩? 
 - 
ElasticSearch 打错字还能搜索到?试试 fuzzy query! 
 
1
50+ 需求文档免费下载!
2
Spring Security 教程合集
3
接了两个私活,都是血汗钱

本文分享自微信公众号 - 江南一点雨(a_javaboy)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
