掌握 ElasticSearch 组合查询:Bool Query 详解与实践_elasticsearch bool查询
掌握 ElasticSearch 组合查询:Bool Query 详解与实践
- 
- 一、引言 (Introduction)
 - 二、Bool 查询基础
 - 
- 2.1 什么是 Bool 查询?
 - 2.2 Bool 查询的四种子句
 - 2.3 语法结构
 
 - 三、Bool 查询的四种子句详解与示例
 - 
- 3.1 `must` 子句
 - 3.2 `filter` 子句
 - 3.3 `should` 子句
 - 3.4 `must_not` 子句
 
 - 四、`minimum_should_match` 参数
 - 
- 4.1 什么是 `minimum_should_match`?
 - 4.2 默认值规则
 - 4.3 使用示例
 - 4.4 注意事项
 
 - 五、嵌套 Bool 查询
 - 
- 5.1 什么是嵌套 Bool 查询?
 - 5.2 使用场景
 - 5.3 示例
 
 - 六、Bool 查询与相关性得分
 - 
- 6.1 Bool 查询如何影响得分?
 - 6.2 调整子句的权重
 
 - 八、总结
 
 
一、引言 (Introduction)
在信息检索和数据分析的场景中,我们经常需要面对复杂的查询需求。简单的关键词搜索可能无法满足我们的要求,我们需要更精细的控制,例如:
- 查找 同时 满足多个条件的文档(例如,既包含 “Elasticsearch” 又包含 “tutorial” 的文章)。
 - 过滤 出符合特定条件的文档(例如,只查看最近一周内发布的文章)。
 - 排除 满足特定条件的文档(例如,不显示已下架的商品)。
 - 对搜索结果进行 优先级排序(例如,优先显示标题中包含关键词的文章)。
 
为了应对这些复杂的查询需求,Elasticsearch 提供了 bool 查询。bool 查询就像一个强大的工具箱,它允许你将多个查询条件组合在一起,形成更复杂的查询逻辑。你可以将 bool 查询看作是乐高积木,通过组合不同的积木块(查询子句),你可以搭建出各种各样的结构(查询逻辑)。
本文将深入探讨 Elasticsearch 7.10 版本中的 bool 查询。你将学习到:
bool查询的基本概念和工作原理。bool查询的四种核心子句:must、filter、should、must_not。- 如何使用 
minimum_should_match参数控制should子句的行为。 - 如何嵌套 
bool查询,构建更复杂的查询逻辑。 bool查询如何影响文档的相关性得分。- 通过实战案例学习如何在实际应用中运用 
bool查询。 
二、Bool 查询基础
2.1 什么是 Bool 查询?
bool 查询是 Elasticsearch 中一种复合查询(compound query),它允许你将多个查询子句(query clause)组合在一起。这些子句可以是任何类型的查询,例如 match、term、range 等,甚至是另一个 bool 查询(嵌套)。
bool 查询的核心思想是 “more_matches_is_better”,也就是说,文档匹配的子句越多,它的相关性得分(_score)就越高。这使得 bool 查询非常适合用于构建复杂的查询逻辑,同时兼顾查询结果的相关性排序。
为了帮助你更好地理解 bool 查询,我们可以将其类比为编程语言中的逻辑运算符:
must类似于逻辑与 (AND):要求所有条件都必须满足。filter也类似于逻辑与(AND):要求所有条件都必须满足。should类似于逻辑或 (OR):至少有一个条件满足即可。must_not类似于逻辑非 (NOT):要求所有条件都不满足。
2.2 Bool 查询的四种子句
bool 查询包含四种核心子句,每种子句都有不同的作用和对得分的影响:
mustmust 子句中的所有条件。must 子句越多,文档得分越高。filterfilter 子句中的所有条件。shouldshould 子句中的一个或多个条件。should 子句越多,文档得分越高。must_notmust_not 子句中的所有条件。导出到 Google 表格
要点:
must和filter子句都要求文档必须匹配,但filter子句不参与得分计算,因此通常比must子句更高效。should子句是可选的,但匹配should子句会提高文档的得分。must_not子句用于排除文档,它也不参与得分计算。filter和must_not子句处于_过滤器上下文_中,这意味着它们不计算得分,并且 Elasticsearch 会自动缓存这些子句的结果,以提高后续查询的性能。
2.3 语法结构
bool 查询的基本语法结构如下:
GET /_search{ \"query\": { \"bool\" : { \"must\" : [ { /* 查询 1 */ }, { /* 查询 2 */ } ], \"filter\": [ { /* 过滤条件 1 */ }, { /* 过滤条件 2 */ } ], \"should\" : [ { /* 查询 3 */ }, { /* 查询 4 */ } ], \"must_not\" : [ { /* 排除条件 1 */ }, { /* 排除条件 2 */ } ] } }}
解释:
query: 这是 ElasticSearch 查询 DSL 的根元素。bool: 表示这是一个bool查询。must、filter、should、must_not: 这是bool查询的四种子句,每个子句都可以包含一个或多个查询条件(可以是任何类型的查询,例如match、term、range等)。
三、Bool 查询的四种子句详解与示例
接下来,我们将深入探讨 must、filter、should 和 must_not 四种子句的用法,并通过示例演示如何在实际场景中应用它们。
数据准备:
首先,我们创建一个名为 articles 的索引,并添加一些示例数据。我们将使用这些数据来演示 bool 查询的各种用法。
PUT articles{ \"mappings\": { \"properties\": { \"title\": { \"type\": \"text\" }, \"content\": { \"type\": \"text\" }, \"category\": { \"type\": \"keyword\" }, \"status\": { \"type\": \"keyword\" }, \"author_id\": { \"type\": \"keyword\" }, \"is_featured\": { \"type\": \"boolean\" }, \"discount\": { \"type\": \"double\" }, \"created_at\": { \"type\": \"date\" }, \"out_of_stock\":{ \"type\":\"boolean\"} } }}POST articles/_bulk{\"index\":{\"_index\": \"articles\"}}{\"title\": \"Elasticsearch Tutorial for Beginners\", \"content\": \"This tutorial covers the basics of Elasticsearch.\", \"category\": \"technology\", \"status\": \"published\", \"author_id\": \"123\", \"is_featured\": true, \"discount\": 0.0, \"created_at\": \"2023-10-26\", \"out_of_stock\": false}{\"index\":{\"_index\": \"articles\"}}{\"title\": \"Advanced Logstash Techniques\", \"content\": \"Learn advanced techniques for processing logs with Logstash.\", \"category\": \"technology\", \"status\": \"published\", \"author_id\": \"456\", \"is_featured\": false, \"discount\": 10.0, \"created_at\": \"2023-10-27\", \"out_of_stock\": false}{\"index\":{\"_index\": \"articles\"}}{\"title\": \"Introduction to Kibana\", \"content\": \"Visualize your Elasticsearch data with Kibana.\", \"category\": \"technology\", \"status\": \"draft\", \"author_id\": \"123\", \"is_featured\": false, \"discount\": 0.0, \"created_at\": \"2023-10-28\", \"out_of_stock\": false}{\"index\":{\"_index\": \"articles\"}}{\"title\": \"Elasticsearch and Logstash Integration\", \"content\": \"Integrate Elasticsearch and Logstash for log management.\", \"category\": \"devops\", \"status\": \"published\", \"author_id\": \"789\", \"is_featured\": true, \"discount\": 20.0, \"created_at\": \"2023-10-29\", \"out_of_stock\": false}{\"index\":{\"_index\": \"articles\"}}{\"title\": \"Elasticsearch for Java Developers\", \"content\": \"A comprehensive guide to using Elasticsearch with Java.\", \"category\": \"technology\", \"status\": \"published\", \"author_id\": \"123\", \"is_featured\": false, \"discount\": 0.0, \"created_at\": \"2023-10-25\", \"out_of_stock\": true}{\"index\":{\"_index\": \"articles\"}}{\"title\": \"Elasticsearch for Python Developers\", \"content\": \"Learn how to use Elasticsearch with Python.\", \"category\": \"technology\", \"status\": \"published\", \"author_id\": \"456\", \"is_featured\": true, \"discount\": 5.0, \"created_at\": \"2023-10-24\", \"out_of_stock\": false}
3.1 must 子句
- 作用: 
must子句要求文档 必须 匹配其中包含的所有查询条件。可以将其理解为逻辑“与” (AND) 操作。 - 对得分的影响: 匹配 
must子句中的查询条件会 增加 文档的相关性得分(_score)。匹配的must子句越多,得分越高。 
示例:
- 
查找同时包含 “Elasticsearch” 和 “tutorial” 关键词的文章(使用
match查询):GET articles/_search{ \"query\": { \"bool\": { \"must\": [ { \"match\": { \"title\": \"Elasticsearch\" } }, { \"match\": { \"content\": \"tutorial\" } } ] } }}这个查询要求文档的
title字段必须包含 “Elasticsearch”,并且content字段必须包含 “tutorial”。只有同时满足这两个条件的文章才会被返回。根据我们创建的数据,只有第一条数据 符合这两个条件: - 
查找
category为 “technology” 且status为 “published” 的文章(结合term查询):GET articles/_search{ \"query\": { \"bool\": { \"must\": [ { \"term\": { \"category\": \"technology\" } }, { \"term\": { \"status\": \"published\" } } ] } }}结果:根据我们创建的数据,第一,二,五,六条会被搜索出来
 
3.2 filter 子句
- 作用: 
filter子句要求文档 必须 匹配其中包含的所有查询条件,但与must子句不同的是,filter子句 不参与 相关性得分计算。它只起到过滤的作用。 - 对得分的影响: 无影响(过滤器上下文)。所有匹配 
filter子句的文档得分都相同(默认为 1.0,可以通过constant_score查询修改)。 - 优势:
- 性能更高: 由于不计算得分,
filter子句的执行速度通常比must子句更快。 - 结果可缓存: Elasticsearch 会自动缓存 
filter子句的结果,以提高后续相同过滤条件的查询性能。 
 - 性能更高: 由于不计算得分,
 
示例:
- 
在搜索结果中过滤出
created_at在过去三年内并且标题包含「Elasticsearch」的文章(使用range查询):GET articles/_search{ \"query\": { \"bool\": { \"must\": [ { \"match\": { \"title\": \"Elasticsearch\" } } ], \"filter\": [ { \"range\": { \"created_at\": { \"gte\": \"now-3y/y\" } } } ] } }}结果:根据我们创建的数据,第一,二,三,四条会被搜索出来
 - 
在搜索结果中过滤出
author_id为 “123” 的文章(使用term查询):GET articles/_search{ \"query\": { \"bool\": { \"must\": [ { \"match\": { \"content\": \"Elasticsearch\" } } ], \"filter\": [ { \"term\": { \"author_id\": \"123\" } } ] } }}结果:根据我们创建的数据,有 3 条会被搜索出来
 
3.3 should 子句
- 作用: 
should子句表示文档 应该 匹配其中包含的查询条件,但 不是必须 的。可以将其理解为逻辑“或” (OR) 操作。 - 对得分的影响: 匹配 
should子句中的查询条件会 增加 文档的相关性得分。匹配的should子句越多,得分越高。 - 特殊情况:
- 如果 
bool查询只包含should子句,而没有must或filter子句,则至少需要匹配一个should子句(minimum_should_match默认为 1)。 - 如果 
bool查询包含must或filter子句,则should子句变为可选的加分项,即使一个should子句都不匹配,文档也会被返回(只要满足must或filter条件)。 
 - 如果 
 
示例:
- 
搜索文章,优先显示标题中包含 “Elasticsearch” 或 “Logstash” 的文章(使用
match查询):GET articles/_search{ \"query\": { \"bool\": { \"should\": [ { \"match\": { \"title\": \"Elasticsearch\" } }, { \"match\": { \"title\": \"Logstash\" } } ] } }}这个查询会返回
title中包含 “Elasticsearch” 或 “Logstash” 或两者都包含的文章。包含的词项越多,得分越高。由于没有must或filter子句,至少需要匹配一个should子句(minimum_should_match默认为 1)。根据我们创建的数据,有 5 条会被搜索出来。 - 
搜索文章,优先显示
is_featured为true或discount大于 0 的文章:GET articles/_search{ \"query\": { \"bool\": { \"must\": [ { \"match\": { \"title\": \"Elasticsearch\" } } ], \"should\": [ { \"term\": { \"is_featured\": true } }, { \"range\": { \"discount\": { \"gt\": 0 } } } ] } }}这个查询首先使用
match查询查找title中包含 “Elasticsearch” 的文章, 然后,它使用should子句来提升is_featured为true或discount大于 0 的文章的得分。即使文章不满足should子句中的任何条件,只要满足must子句,仍然会被返回。根据我们创建的数据,第一,四,五,六条会被搜索出来,并且第一,四,六条分数会更高。 
3.4 must_not 子句
- 作用: 
must_not子句要求文档 必须不 匹配其中包含的所有查询条件。可以将其理解为逻辑“非” (NOT) 操作。 - 对得分的影响: 无影响(过滤器上下文)。与 
filter子句类似,must_not子句不参与相关性得分计算。 - 注意: 由于 
must_not子句处于过滤器上下文中,因此 Elasticsearch 会自动缓存其结果以提高性能。 
示例:
- 
搜索文章,排除
status为 “draft” 的文章(使用term查询):GET articles/_search{ \"query\": { \"bool\": { \"must\": [ { \"match\": { \"title\": \"Elasticsearch\" } } ], \"must_not\": [ { \"term\": { \"status\": \"draft\" } } ] } }}这个查询首先使用
match查询查找title包含 “Elasticsearch” 的文章(must子句),然后使用must_not子句排除status为 “draft” 的文章。只有满足must条件且不满足must_not条件的文章才会被返回。根据我们创建的数据,第一,四,五,六条会被搜索出来。 - 
搜索文章,排除
out_of_stock为true且不是is_featured的文章:GET articles/_search{ \"query\": { \"bool\": { \"must\": [ {\"match\":{\"title\": \"Elasticsearch\"}} ], \"must_not\": [ { \"term\": { \"out_of_stock\": true } }, {\"term\": {\"is_featured\": false}} ] } }}这个查询首先使用
match查询查找title包含 “Elasticsearch” 的文章, 然后使用must_not子句排除out_of_stock为true且不是is_featured的文章。根据我们创建的数据,第一,四,六条会被搜索出来。 
四、minimum_should_match 参数
4.1 什么是 minimum_should_match?
minimum_should_match 参数用于控制 should 子句的行为。它指定了在 bool 查询中,至少需要匹配多少个 should 子句,文档才会被认为是匹配的。
你可以将 minimum_should_match 设置为:
- 整数: 表示至少需要匹配的 
should子句的数量。 - 百分比: 表示至少需要匹配的 
should子句占总should子句数量的百分比(向下取整)。 
4.2 默认值规则
minimum_should_match 的默认值取决于 bool 查询的结构:
- 只有 
should子句: 如果bool查询只包含should子句,而没有must或filter子句,则minimum_should_match默认为 1。这意味着至少需要匹配一个should子句。 - 包含 
must或filter子句: 如果bool查询包含must或filter子句,则minimum_should_match默认为 0。这意味着should子句变为可选的加分项,即使一个should子句都不匹配,文档也会被返回(只要满足must或filter条件)。 
4.3 使用示例
- 
设置
minimum_should_match为具体数值:假设我们要搜索文章,要求标题中包含 “Elasticsearch” 或 “Logstash” 或 “Kibana”,并且至少需要匹配其中两个关键词:
GET articles/_search{ \"query\": { \"bool\": { \"should\": [ { \"match\": { \"title\": \"Elasticsearch\" } }, { \"match\": { \"title\": \"Logstash\" } }, { \"match\": { \"title\": \"Kibana\" } } ], \"minimum_should_match\": 2 } }}这个查询要求至少匹配两个
should子句。例如,如果一篇文章的标题只包含 “Elasticsearch”,则不会被返回;如果标题包含 “Elasticsearch” 和 “Logstash”,则会被返回。 - 
设置
minimum_should_match为百分比:假设我们要搜索文章,要求标题或内容中包含 “Elasticsearch”、“Logstash”、“Kibana” 或 “Beats”,并且至少需要匹配其中 50% 的关键词:
GET articles/_search{ \"query\": { \"bool\": { \"should\": [ { \"match\": { \"title\": \"Elasticsearch\" } }, { \"match\": { \"title\": \"Logstash\" } }, { \"match\": { \"content\": \"Kibana\" } }, { \"match\": { \"content\": \"Beats\" } } ], \"minimum_should_match\": \"50%\" } }}这个查询有 4 个
should子句,minimum_should_match设置为 “50%”,这意味着至少需要匹配 4 * 50% = 2 个子句。 
4.4 注意事项
当 should 和 must 或者 filter 一起出现的时候,should 会退化为一个加分项,如果一个文档不满足任何 should 中的条件,但是满足 must 中的条件,也是会被搜索出来的。
五、嵌套 Bool 查询
5.1 什么是嵌套 Bool 查询?
嵌套 Bool 查询是指在一个 bool 查询的子句(must、filter、should、must_not)中,再嵌套另一个 bool 查询。通过这种嵌套,你可以构建出非常复杂的查询逻辑,以满足各种细粒度的搜索需求。
5.2 使用场景
嵌套 Bool 查询通常用于以下场景:
- 构建复杂的逻辑组合: 当你需要组合多个条件,并且这些条件之间存在复杂的 AND、OR、NOT 关系时,可以使用嵌套 Bool 查询。例如,“(A AND B) OR (C AND (D OR E))”。
 - 更精细地控制得分: 通过嵌套 Bool 查询,你可以更精细地控制不同查询子句对最终得分的贡献。例如,你可以将某些条件放在外层 
bool查询的should子句中,而将另一些条件放在内层bool查询的must子句中。 
5.3 示例
假设我们需要实现以下搜索需求:
搜索标题中包含 “Elasticsearch” 且 (作者 ID 为 “123” 或为精选文章) 且发布状态不是 “draft” 的文章。
这个需求的逻辑关系比较复杂,可以用如下的逻辑表达式来表示:
(title 包含 \"Elasticsearch\") AND ((author_id = \"123\") OR (is_featured = true)) AND (status != \"draft\")
我们可以使用嵌套 bool 查询来实现这个需求:
GET articles/_search{ \"query\": { \"bool\": { \"must\": [ { \"match\": { \"title\": \"Elasticsearch\" } }, { \"bool\": { \"should\": [  { \"term\": { \"author_id\": \"123\" } },  { \"term\": { \"is_featured\": true } } ], \"minimum_should_match\": 1 } }, { \"bool\": { \"must_not\": [  { \"term\": { \"status\": \"draft\" } } ] } } ] } }}
解释:
- 外层 
bool查询:- 使用 
must子句组合三个条件:match查询:标题包含 “Elasticsearch”。- 嵌套的 
bool查询:作者 ID 为 “123” 或为精选文章。 - 嵌套的 
bool查询 (使用must_not): 排除 status 为 draft 的文章。 
 
 - 使用 
 - 内层 
bool查询(作者/精选):- 使用 
should子句组合两个条件:term查询:作者 ID 为 “123”。term查询:is_featured为true。
 minimum_should_match: 1,表示至少需要匹配一个should子句。
 - 使用 
 - 内层 
bool查询 (排除状态):- 使用 
must_not子句,其内部是一个term查询,用于排除status为draft。 
 - 使用 
 
六、Bool 查询与相关性得分
6.1 Bool 查询如何影响得分?
bool 查询的子句对文档相关性得分(_score)的影响,我们已经在前面的章节中详细讨论过:
must子句: 匹配的must子句越多,文档得分越高。filter子句: 不影响得分。should子句: 匹配的should子句越多,文档得分越高。must_not子句: 不影响得分。
总结: must 和 should 子句会影响得分,而 filter 和 must_not 子句不影响得分。
6.2 调整子句的权重
在某些情况下,你可能希望调整不同查询子句对得分的贡献。例如,你可能希望标题中包含关键词的文档比内容中包含关键词的文档得分更高。
你可以使用 boost 参数来调整查询子句的权重。boost 参数是一个正数,用于增加或减少查询子句的相对重要性。boost 的默认值为 1.0。
示例:
假设我们搜索文章,希望标题中包含 “Elasticsearch” 的文档比内容中包含 “Elasticsearch” 的文档得分更高:
GET articles/_search{ \"query\": { \"bool\": { \"should\": [ { \"match\": { \"title\": { \"query\": \"Elasticsearch\", \"boost\": 2.0 } } }, { \"match\": { \"content\": \"Elasticsearch\" } } ] } }}
在这个查询中,我们为标题的 match 查询设置了 boost 值为 2.0。这意味着标题中包含 “Elasticsearch” 的文档的得分将比内容中包含 “Elasticsearch” 的文档的得分更高(大约是两倍)。
注意: boost 参数只是一个提示,Elasticsearch 不保证得分的精确比例。实际得分还受到其他因素的影响,例如词频、逆文档频率等。
八、总结
bool 查询是 Elasticsearch 中一种非常强大且灵活的查询工具,它可以帮助你构建复杂的查询逻辑,以满足各种搜索需求。通过组合 must、filter、should 和 must_not 四种子句,以及使用 minimum_should_match 参数和嵌套 bool 查询,你可以实现对搜索结果的精细控制。
希望本文能够帮助你深入理解 Elasticsearch 7.10 版本中的 bool 查询,并在实际应用中灵活运用。


