復合查詢有:bool query
(布爾查詢)、boosting query
(提高查詢)、constant_score
(固定分數(shù)查詢)、dis_max
(最佳匹配查詢)、function_score
(函數(shù)查詢)。
一、bool query(布爾查詢)
1、概念
定義
可以理解成通過布爾邏輯
將較小的查詢組合成較大的查詢。
Bool查詢語法有以下特點
- 子查詢可以任意順序出現(xiàn)
- 可以嵌套多個查詢,包括bool查詢
- 如果bool查詢中沒有must條件,should中必須至少滿足一條才會返回結果。
bool查詢包含四種操作符,分別是must,should,must_not,filter。他們均是一種數(shù)組,數(shù)組里面是對應的判斷條件。
must: 必須匹配。貢獻算分
must_not:過濾子句,必須不能匹配,但不貢獻算分
should: 選擇性匹配,至少滿足一條。貢獻算分
filter: 過濾子句,必須匹配,但不貢獻算分
2、官方例子
看下官方舉例
POST _search
{
"query": {
"bool" : {
"must" : {
"term" : { "user" : "kimchy" }
},
"filter": {
"term" : { "tag" : "tech" }
},
"must_not" : {
"range" : {
"age" : { "gte" : 10, "lte" : 20 }
}
},
"should" : [
{ "term" : { "tag" : "wow" } },
{ "term" : { "tag" : "elasticsearch" } }
],
"minimum_should_match" : 1,
"boost" : 1.0
}
}
}
在filter元素下指定的查詢對評分沒有影響 , 評分返回為0。分數(shù)僅受已指定查詢的影響。
官方例子
GET _search
{
"query": {
"bool": {
"filter": {
"term": {
"status": "active"
}
}
}
}
}
這個例子查詢查詢?yōu)樗形臋n分配0分,因為沒有指定評分查詢。
官方例子
GET _search
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"term": {
"status": "active"
}
}
}
}
}
此bool查詢具有match_all查詢,該查詢?yōu)樗形臋n指定1.0分。
3、Bool嵌套查詢
# 嵌套,實現(xiàn)了 should not 邏輯
POST /products/_search
{
"query": {
"bool": {
"must": {
"term": {
"price": "30"
}
},
"should": [
{
"bool": {
"must_not": {
"term": {
"avaliable": "false"
}
}
}
}
],
"minimum_should_match": 1
}
}
}
二、boosting query
1、概念
在上面的復合查詢我們可以通過must_not+must
先剔除不想匹配的文檔,再獲取匹配的文檔,但是有一種場景就是我并不需要完全剔除,而是把需要剔除的那部分文檔的分數(shù)降低。這個時候就可以使用boosting query。下面會舉例說明。
2、舉例
1)、創(chuàng)建索引并添加數(shù)據(jù)
# 創(chuàng)建索引并添加數(shù)據(jù)
POST /news/_bulk
{ "index": { "_id": 1 }}
{ "content":"Apple Mac" }
{ "index": { "_id": 2 }}
{ "content":"Apple iPad" }
{ "index": { "_id": 3 }}
{ "content":"Apple employee like Apple Pie and Apple Juice" }
2)、 bool must復合查詢
#查詢結果3->1->2
POST news/_search
{
"query": {
"bool": {
"must": {
"match":{"content":"apple"}
}
}
}
}
3)、bool must_not復合查詢
我們需要的是文檔中需要包含 apple,但是文檔中不包含 pie,那么我們可以這么做
#must_not的方式,將3的記錄強制排除掉 (結果 1->2)
POST news/_search
{
"query": {
"bool": {
"must": {
"match":{"content":"apple"}
},
"must_not": {
"match":{"content":"pie"}
}
}
}
}
3)、 boosting query
上面第二種比較粗暴,可能我實際開發(fā)過程中,如果出現(xiàn) pie,我并不想把這條記錄完全過濾掉,而是希望降低他的分數(shù),讓它也出現(xiàn)在列表中,只是查詢結果可能比較靠后。
# 通過Boosting的方式,將3的記錄也納入結果集,只是排名會靠后。(結果 1->2->3)
POST news/_search
{
"query": {
"boosting": {
"positive": {
"match": {
"content": "apple"
}
},
"negative": {
"match": {
"content": "pie"
}
},
"negative_boost": 0.5
}
}
}
說明`boosting需要搭配三個關鍵字 `positive` , `negative` , `negative_boost
只有匹配了 positive查詢 的文檔才會被包含到結果集中,但是同時匹配了negative查詢 的文檔會被降低其相關度,通過將文檔原本的_score和negative_boost
參數(shù)進行相乘來得到新的_score。因此,negative_boost參數(shù)一般小于1.0
。在上面的例子中,任何包含了指定負面詞條的文檔的_score
都會是其原本_score的一半。
3、思考boosting query應用場景
場景舉例
我們通過去索引中搜索 '蘋果公司' 相關的信息,然后我們在查詢中的信息為 '蘋果'。
1. 那么我們查詢的條件是:must = '蘋果'。也就是文檔中必須包含'蘋果'
但是我們需要的結果是蘋果公司相關信息,如果你的文檔是 '蘋果樹','蘋果水果',那么其實此蘋果非彼蘋果如果匹配到其實沒有任何意義。
2. 那么我們修改查詢條件為: must = '蘋果' AND must_not = '樹 or 水果'
就是說就算文檔包含了蘋果,但因為包含了樹或者水果那么我們也會過濾這條文檔信息,因為我們要查的蘋果公司相關信息,如果你是蘋果樹那對我來講確實是不匹配,所以直接過濾掉,看是沒啥問題。
但是你想,這樣做是不是太粗暴了,因為一個文檔中包含'蘋果'和'樹'那不代表一定是蘋果樹,而可能是 '蘋果公司組織員工一起去種樹' 那么這條文檔理應出現(xiàn),而不是直接過濾掉,所以我們就可以用boosting query
。就像上面這個例子一樣。
三、constant_score(固定分數(shù)查詢)
定義
常量分值查詢,目的就是返回指定的score
,一般都結合filter
使用,因為filter context忽略score。
舉例
(結果 1->2->3 同時分數(shù)都為2.5)
POST news/_search
{
"query": {
"constant_score": {
"filter": {
"match": {
"content":"apple"
}
},
"boost": 2.5
}
}
}
運行結果
可以看出分數(shù)都是2.5
四、dis_max(最佳匹配查詢)
1、概念
dis_max
: 只是取分數(shù)最高的那個query的分數(shù)而已。
看下官方例子
GET /_search
{
"query": {
"dis_max" : {
"queries" : [
{ "term" : { "title" : "Quick pets" }},
{ "term" : { "body" : "Quick pets" }}
],
"tie_breaker" : 0.7
}
}
}
解釋
假設一條文檔的'title'查詢得分是 1,'body'查詢得分是1.6。那么總得分為:1.6+1*0.7 = 2.3。
如果我們去掉"tie_breaker" : 0.7
,那么tie_breaker默認為0,那么這條文檔的得分就是 1.6 + 1*0 = 1.6
2、舉例
1)創(chuàng)建數(shù)據(jù)
#1、創(chuàng)建索引
PUT /dismax
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"title": {
"type":"text"
},
"content": {
"type":"text"
}
}
}
}
#2、創(chuàng)建數(shù)據(jù)
PUT /dismax/_doc/1
{
"title" : "i like java",
"content" : "the weather is nice today"
}
PUT /dismax/_doc/2
{
"title" : "It will rain tomorrow",
"content" : "Development beginner"
}
PUT /dismax/_doc/3
{
"title" :"i like java is very much",
"content" :"I am a development beginner"
}
2)、should查詢
#should查詢查詢 (結果 3->2->1
GET /dismax/_search
{
"query": {
"bool": {
"should": [
{ "match": { "title": "java beginner" }},
{ "match": { "content": "java beginner" }}
]
}
}
}
運行結果
should計算分值:
1. 運行should
子句中的兩個查詢 2)、相加查詢返回的分值
doc1:title: 0.53 + content: 0 = 0.53
doc2:title:0 + content:0.59 = 0,59
doc3:title:0.41 + content:0.42 = 0.83
所有最終運行結果: 3 – 2 – 1
2. dis_max查詢(不帶tie_breaker)
#運行結果(2-1-3)
GET /dismax/_search
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "java beginner" }},
{ "match": { "content": "java beginner" }}
]
}
}
}
運行結果
我們可以很明顯看出: 只是取分數(shù)最高的那個query的分數(shù)排序。
doc1:title: 0.53 ; content: 0 = 0.53
doc2:title:0 ; content:0.59 = 0.59
doc3:title:0.41 ; content:0.42 = 0.42
所以這里的排序為 2 – 1 – 3
3. dis_max查詢(帶tie_breaker)
#運行結果 3-2-1
GET /dismax/_search
{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "java beginner" }},
{ "match": { "content": "java beginner" }}
],
"tie_breaker" : 0.5
}
}
}
這里可以看出看出: 取分數(shù)最高的那個query的分數(shù),同時其它子查詢查詢的分數(shù)乘以tie_breaker
doc1:title: 0.53 + content: 0 = 0.53
doc2:title:0 + content:0.59 = 0.59
doc3:title:0.41 + content:0.42*0.5 = 0.62
所以這里的排序為 3 – 2 – 1
五、function_score(函數(shù)查詢)
1、概念
定義` function_score是處理分值計算過程的終極工具。它讓你能夠對所有匹配了主查詢的每份文檔`調用一個函數(shù)來調整甚至是完全替換原來的_score。
注意
要使用function_score,用戶必須定義一個查詢和一個或多個函數(shù),這些函數(shù)計算查詢返回的每個文檔的新分數(shù)。
它擁有幾種預先定義好了的函數(shù):
weight
對每份文檔適用一個簡單的提升,且該提升不會被歸約:當weight為2時,結果為2 * _score。
field_value_factor
使用文檔中某個字段的值來改變_score,比如將受歡迎程度或者投票數(shù)量考慮在內。
random_score
使用一致性隨機分值計算來對每個用戶采用不同的結果排序方式,對相同用戶仍然使用相同的排序方式。
衰減函數(shù)(Decay Function) - linear,exp,gauss
將像publish_date,geo_location或者price這類浮動值考慮到_score中,偏好最近發(fā)布的文檔,鄰近于某個地理位置(譯注:其中的某個字段)的文檔或者價格
(譯注:其中的某個字段)靠近某一點的文檔。
script_score
使用自定義的腳本來完全控制分值計算邏輯。如果你需要以上預定義函數(shù)之外的功能,可以根據(jù)需要通過腳本進行實現(xiàn)。
2)使用場景
有關function_score如果要深入講,估計一篇博客都不夠,所以這里說下在現(xiàn)實中可能會用的場景,如果你有這些場景,那么就可以考慮用function_score。
1)假設我們又一個資訊類APP我們希望讓人閱讀量高的文章出現(xiàn)在結果列表的頭部,但是主要的排序依據(jù)仍然是全文搜索分值。
2)當用戶搜索酒店,它的要求是 1、離他目前位置1Km內 2、價格在500元內。如果我們只是使用一個 filter 排除所有市中心方圓 1KM以外的酒店,
再用一個filter排除每晚價格超過500元的酒店,這種作法太過強硬,可能有一間房在2K米,但是超級便宜一晚只要100元,用戶可能會因此愿意妥協(xié)住這間房。
有關function_score例子這里就不寫了,具體的可以參考官方文檔:Function score query