侧边栏壁纸
博主头像
孔子说JAVA博主等级

成功只是一只沦落在鸡窝里的鹰,成功永远属于自信且有毅力的人!

  • 累计撰写 352 篇文章
  • 累计创建 135 个标签
  • 累计收到 10 条评论

目 录CONTENT

文章目录

ES教程9-bool查询语法及示例

孔子说JAVA
2022-10-14 / 0 评论 / 0 点赞 / 273 阅读 / 9,130 字 / 正在检测是否收录...
广告 广告

es bool 查询是把任意多个简单查询组合在一起,使用 must、should、must_not、filter 选项来表示简单查询之间的逻辑,每个选项都可以出现 0 次到多次。它是为了满足比较复杂的查询需求,如需要在多个字段上查询多种多样的文本,并且根据一系列的标准来过滤。

1、bool查询介绍

Boolean query 可以基于多个条件的组合对文档进行查询。使用的是Lucene BooleanQuery的映射。它是基于一个或多个布尔子句构建的,每个子句对应一个类型。bool query 主要通过下列 4 个类型来构建用户想要的布尔查询,每个选项的含义如下:

  • must:文档必须匹配该选项下的查询条件,相当于逻辑运算的 AND,且参与文档相关度的评分(计算score)。
  • should:文档可以匹配 should 选项下查询条件的0个或者多个(没有 must 语句的时候,至少有一个 should 语句必须匹配),相当于逻辑运算的 OR,minimum_should_match参数定义了至少满足几个子句,且参与文档相关度的评分(计算score)。
  • must_not:与 must 相反,匹配该选项下的查询条件的文档不会被返回;需要注意的是,must_not 语句不会影响评分,它的作用只是将不相关的文档排除。
  • filter:和 must 一样,匹配 filter 选项下的查询条件的文档才会被返回,但是 filter 不评分(不计算score),只起到过滤功能。

注意,每一个子查询都独自地计算文档的相关性得分。一旦他们的得分被计算出来, bool 查询就将这些得分进行合并并且返回一个代表整个布尔操作的得分。

计算规则

  • bool查询采用的策略是“匹配越多越好(more_matches_is_better)”,所以每个命中的must和should语句都会计算score,最终会将满足 must 和 should 子句的文档合并起来计算一个总分。
  • 在 filter 子句查询中,分值将会都返回 0。
  • must_not 子句并不影响得分,它们存在的意义是排除已经被包含的文档。

如上所述,bool 查询的计算得分主要是 must 和 should 子句,它们的计算规则是,把所有符合 must 和 should 的子句得分加起来,然后乘以匹配子句的数量,再除以子句的总数。

GET books/_search
{
  "query": {
    "bool": {
      "filter": {
        "term": { "status": 1 }
      },
      "must_not": {
        "range": { "price": { "gte": 70 } }
      },
      "must": {
        "match": { "title": "java" }
      },
      "should": [
        {
          "match": { "description": "虚拟机" }
        },
        {
          "match": { "content": "计算公式" }
        }
      ]
    }
  }
}

2、bool查询示例

must 语句必须匹配,但有多少 should 语句应该匹配呢?默认情况下,没有 should 语句是必须匹配的,但只有一个例外,那就是当没有 must 语句的时候,至少有一个 should 语句必须匹配。此外我们可以通过 minimum_should_match 参数控制需要匹配的 should 语句的数量。

2.1 must

当查询中包含must查询时,查询条件必须满足,相当于逻辑查询中的“与”查询。命中的文档必须匹配该子查询的结果,并且ES会将该子查询与文档的匹配程度值加入总得分里。must搜索包含一个数组,可以把其他的term级别的查询及布尔查询放入其中。

示例1:

{
  "query": {
    "bool": {
      "must": {
        "term": {
          "age": 20
        }
      }
    }
  }
}

其等同于:

SELECT * FROM xxx WHERE age = 20;

示例2:

{ 
  "query": { 
    "bool": { 
      "must": [                //must查询,数组内可封装各类子查询,AND运算
        {                      //第一个子查询:城市为北京 
          "term": { 
            "city": { 
              "value": "北京" 
            } 
          } 
        }, 
        {                    //第二个子查询:价格>=350且价格<=400  
          "range": { 
            "price": { 
              "gte": 350, 
              "lte": 400 
            } 
          } 
        } 
      ] 
    } 
  } 
}

其等同于:

SELECT * FROM xxx WHERE city = '北京' and price>=250 and price<=400;

在Java客户端上构建must搜索时,可以使用QueryBuilders.boolQuery().must()进行构建,上面的range查询例子改写成Java客户端请求的形式为:

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//构建城市term查询
TermQueryBuilder termQueryIsReady = QueryBuilders.termQuery("city", "北京");
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price").gte(350).lte(600); //构建价格range查询
//进行关系“与”查询
boolQueryBuilder.must(termQueryIsReady).must(rangeQueryBuilder);
searchSourceBuilder.query(boolQueryBuilder);
searchRequest.source(searchSourceBuilder);  //设置查询SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

2.2 must_not

当查询中包含must not查询时,表示当前查询为“非”查询,查询条件不需要满足,相当于not。命中的文档不能匹配该查询中的一个或多个子查询的结果,ES会将该查询与文档的匹配程度加入总得分里。must not查询包含一个数组,可以把其他term级别的查询及布尔查询放入其中。

{
  "query": {
    "bool": {
      "must_not": [
        { "term": { "age": 20 } },
        { "term": { "gender": "male" } }
      ]
    }
  }
}

其等同于:

SELECT * FROM xxx WHERE age != 20 AND gender != "male";

在Java客户端上构建must_not搜索时,可以使用QueryBuilders.boolQuery().mustNot()方法进行构建,上面的例子改写成Java客户端请求的形式为:

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//构建age为 20 的term查询
TermQueryBuilder termQueryIsReady = QueryBuilders.termQuery("age", 20);
//构建gender为“male”的term查询
TermQueryBuilder termQueryWritter = QueryBuilders.termQuery("gender", "male");
//进行关系“必须不”查询
boolQueryBuilder.mustNot(termQueryIsReady).mustNot(termQueryWritter);
searchSourceBuilder.query(boolQueryBuilder);
searchRequest.source(searchSourceBuilder);     //设置查询
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

另外,must_not与filter相同,采用过滤器执行而不需要计算文档的得分,所以返回的结果对应的分值为0。

同一个bool查询语句可以有多个不同的条件,如:

{
  "query": {
    "bool": {
      "must": {
        "term": {
          "age": 20
        }
      },
      "must_not": {
        "term": {
          "gender": "male"
        }
      }
    }
  }
}

该查询返回 age 值为 20,且 gender 值不为 male 的文档结果。其等同于:

SELECT * FROM xxx WHERE age = 20 AND gender != "male";

2.3 should

当查询中包含should查询时,表示当前查询为“或”查询,查询条件可以满足也可以不满足。命中的文档可以匹配该查询中的一个或多个子查询的结果,并且ES会将该查询与文档的匹配程度加入总得分里。should查询包含一个数组,可以把其他的term级别的查询及布尔查询放入其中。

  • 如果bool查询下没有must子句,那至少应该有一个should子句。但是如果有must子句,那么没有should子句匹配也可以进行查询。
{
  "query": {
    "bool": {
      "should": [
        { "term": { "age": 20 } },
        { "term": { "gender": "male" } },
        { "range": { "height": { "gte": 170 } } },
      ]
    }
  }
}

其等同于:

SELECT * FROM xxx WHERE age = 20 OR gender = "male" or height >= 170;

在Java客户端上构建should搜索时,可以使用QueryBuilders.boolQuery().should()进行构建,上面的例子改写成Java客户端请求的形式为:

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//构建age的term查询
TermQueryBuilder termQueryIsReady = QueryBuilders.termQuery("age", 20);
TermQueryBuilder termQueryWritter = QueryBuilders.termQuery("gender", "male");
//构建价格range查询
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("height").gte(170); 
//进行关系“或”查询
boolQueryBuilder.should(termQueryIsReady).should(termQueryWritter).should(rangeQueryBuilder);
searchSourceBuilder.query(boolQueryBuilder);
searchRequest.source(searchSourceBuilder);   //设置查询
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

should 查询与SQL中的OR运算较为不同的一点是,should 查询可以使用 minimum_should_match 参数指定至少需要满足几个条件. 例如,查询的结果需要满足两个或两个以上的查询条件:

{
  "query": {
    "bool": {
      "should": [
        { "term": { "age": 20 } },
        { "term": { "gender": "male" } },
        { "term": { "height": 170 } },
      ],
      "minimum_should_match": 2
    }
  }
}

在同一个 bool 语句中若不存在 must 或 filter 时,minimum_should_match 默认的值为1,即至少要满足其中一个条件;但若有其它 must 或 filter 存在时,minimum_should_match 默认值为0。

例如,所有返回的文档age值必定为20,但其中可能包括有status值不为"active"的文档. 若需要二者同时生效,可在bool查询中增加一个参数"minimum_should_match": 1.

{
  "query": {
    "bool": {
      "must": {
        "term": {
          "age": 20
        },
      },
      "should": {
        "term": {
          "status": "active"
        }
      }
    }
  }
}

2.4 filter

filter查询即过滤查询,该查询是布尔查询里非常独特的一种查询。其他布尔查询关注的是查询条件和文档的匹配程度,并按照匹配程度进行打分;而filter查询关注的是查询条件和文档是否匹配,不进行相关的打分计算,但是会对部分匹配结果进行缓存。

GET /hotel/_search 
{ 
  "query": { 
    "bool": { 
      "filter": [            // filter查询,数组内可封装各类子查询 
        {                    //第一个子查询:城市为北京 
          "term": { 
            "city": "北京" 
          } 
        }, 
        {                    //第一个子查询:满房状态为否 
          "term": { 
            "full_room": false 
          } 
        } 
      ] 
    } 
  } 
}

使用filter查询的子句是不计算分数的,这可以减少不小的时间开销。

在Java客户端上构建filter搜索时,可以使用QueryBuilders.boolQuery().filter()进行构建,上面的例子改写成Java客户端请求的形式为:

SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.filter(QueryBuilders.termQuery("city", "北京"));
boolQueryBuilder.filter(QueryBuilders.termQuery("full_room", false));
searchSourceBuilder.query(boolQueryBuilder);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

为提升查询效率,对于简单的term级别匹配查询,应该根据自己的实际业务场景选择合适的查询语句,需要确定这些查询项是否都需要进行打分操作,如果某些匹配条件不需要打分操作的话,那么应该把这些查询全部改成filter形式,让查询更高效。

3、多条件组合查询

bool多条件组合查询must should同时使用时,should失效的问题。

假设有如下形式的数据:

image-1665462496092

需求:查询分数是70或80的男生

3.1 错误写法

用人的正常逻辑思维,会觉得应该这么写 sex男&&(score70||socre==80),最终理想值是得到李四和赵六、

{
   "query": {
      "bool": {
         "must": [
            {"term": {"sex": {"value": "男"}}}
         ],
         "should": [
            {"term": { "score": {"value": "70"}}},
            {"term": {"score": {"value": "80"}}}
         ]
      }
   }
}

执行后发现should失效了,张三、李四、赵六都被查询出来了。

当使用should查询时,如果同时包含了must或者filter查询,那么should的查询语句就不是或者的意思了,而是有或者没有都行的含义。如果没有filter和must查询的话,那么必须满足一个should中的条件。

本例写法的涵义:性别必须为男,但是分数可以是70,也可以是80,也可以两者都不是。

3.2 正确写法

有三种写法可以解决should失效的问题

第一种:(sex男&&score70)||(sex男&&socre80)

{
   "query": {
      "bool": {
         "should": [
            {
               "bool": {
                  "must": [
                     {"term": {"sex": "男"}},
                     {"term": {"score": "70"}}
                  ]
               }
            },
            {
               "bool": {
                  "must": [
                     {"term": {"sex": "男"}},
                     {"term": {"score": "80"}}
                  ]
               }
            }
         ]
      }
   }
}

第二种:在must中再嵌套一层bool来做should过滤

{
   "query": {
      "bool": {
         "must": [
            {"term": {"sex": {"value": "男"}}},
            {
                "bool": {
                  "should": [
                     {"term": {"score": {"value": "70"}}},
                     {"term": {"score": {"value": "80"}}}
                  ]
               }
            }
         ]
      }
   }
}

第三种:使用minimum_should_match,设置至少匹配一项should子句

{
   "query": {
      "bool": {
         "must": [
            {"term": {"sex": {"value": "男"}}}
         ],
         "should": [
            {"term": { "score": {"value": "70"}}},
            {"term": {"score": {"value": "80"}}}
         ],
         "minimum_should_match": 1
      }
   }
}

minimum_should_match的值为1,表示至少匹配一项should子句。

0

评论区