掌握 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
查詢包含四種核心子句,每種子句都有不同的作用和對得分的影響:
子句 | 作用 | 對得分的影響 |
---|---|---|
must | 必須匹配。文檔必須滿足 must 子句中的所有條件。 | 匹配的 must 子句越多,文檔得分越高。 |
filter | 必須匹配(過濾器上下文)。文檔必須滿足 filter 子句中的所有條件。 | 不影響得分。 |
should | 可以匹配。文檔應該滿足 should 子句中的一個或多個條件。 | 匹配的 should 子句越多,文檔得分越高。 |
must_not | 必須不匹配(過濾器上下文)。文檔必須不滿足 must_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
查詢,并在實際應用中靈活運用。