Elasticsearch(7) ---複合查詢

複合查詢有bool query(布爾查詢)、boosting query(提高查詢)、constant_score(固定分數查詢)、dis_max(最佳匹配查詢)、function_score(函數查詢)。

一、bool query(布爾查詢)

1、概念

定義 可以理解成通過布爾邏輯將較小的查詢組合成較大的查詢。

Bool查詢語法有以下特點

  1. 子查詢可以任意順序出現
  2. 可以嵌套多個查詢,包括bool查詢
  3. 如果bool查詢中沒有must條件,should中必須至少滿足一條纔會返回結果。

bool查詢包含四種操作符,分別是must,should,must_not,query。他們均是一種數組,數組裏面是對應的判斷條件。

must:    必須匹配。貢獻算分
must_not:過濾子句,必須不能匹配,但不貢獻算分 
should:  選擇性匹配,至少滿足一條。貢獻算分
filter:  過濾子句,必須匹配,但不貢獻算分

2、官方例子

看下官方舉例

<span style="color:#444444"><code>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
    }
  }
}</code></span>

在filter元素下指定的查詢對評分沒有影響 , 評分返回爲0。分數僅受已指定查詢的影響。

官方例子

<span style="color:#444444"><code>GET _search
{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "status": "active"
        }
      }
    }
  }
}</code></span>

這個例子查詢查詢爲所有文檔分配0分,因爲沒有指定評分查詢。

官方例子

<span style="color:#444444"><code>GET _search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "term": {
          "status": "active"
        }
      }
    }
  }
}</code></span>

此bool查詢具有match_all查詢,該查詢爲所有文檔指定1.0分。

3、Bool嵌套查詢

<span style="color:#444444"><code># 嵌套,實現了 should not 邏輯
POST /products/_search
{
  "query": {
    "bool": {
      "must": {
        "term": {
          "price": "30"
        }
      },
      "should": [
        {
          "bool": {
            "must_not": {
              "term": {
                "avaliable": "false"
              }
            }
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}</code></span>

 

 

二、boosting query

1、概念

在上面的複合查詢我們可以通過must_not+must 先剔除不想匹配的文檔,再獲取匹配的文檔,但是有一種場景就是我並不需要完全剔除,而是把需要剔除的那部分文檔的

分數降低。這個時候就可以使用boosting query。下面會舉例說明。

2、舉例

1)、創建索引並添加數據

<span style="color:#444444"><code># 創建索引並添加數據
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" }</code></span>

2)、 bool must複合查詢

<span style="color:#444444"><code>#查詢結果3->1->2
POST news/_search
{
  "query": {
    "bool": {
      "must": {
        "match":{"content":"apple"}
      }
    }
  }
}</code></span>

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

上面第二種比較粗暴,可能我實際開發過程中,如果出現 pie,我並不想把這條記錄完全過濾掉,而是希望降低他的分數,讓它也出現在列表中,只是查詢結果可能比較靠後。

 
# 通過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參數進行

相乘來得到新的_score。因此,negative_boost參數一般小於1.0。在上面的例子中,任何包含了指定負面詞條的文檔的_score都會是其原本_score的一半。

3、思考boosting query應用場景

場景舉例 我們通過去索引中搜索 "蘋果公司" 相關的信息,然後我們在查詢中的信息爲 "蘋果"

1)那麼我們查詢的條件是:must = "蘋果"。也就是文檔中必須包含"蘋果"

但是我們需要的結果是蘋果公司相關信息,如果你的文檔是 "蘋果樹","蘋果水果",那麼其實此蘋果非彼蘋果如果匹配到其實沒有任何意義。

2)那麼我們修改查詢條件爲: must = "蘋果" AND must_not = "樹 or 水果"

就是說就算文檔包含了蘋果,但因爲包含了樹或者水果那麼我們也會過濾這條文檔信息,因爲我們要查的蘋果公司相關信息,如果你是蘋果樹那對我來講確實是不匹配,

所以直接過濾掉,看是沒啥問題。

但是你想,這樣做是不是太粗暴了,因爲一個文檔中包含"蘋果"和"樹"那不代表一定是蘋果樹,而可能是 "蘋果公司組織員工一起去種樹" 那麼這條文檔理應出現

而不是直接過濾掉,所以我們就可以用boosting query。就像上面這個例子一樣。

 

 

三、constant_score(固定分數查詢)

定義 常量分值查詢,目的就是返回指定的score,一般都結合filter使用,因爲filter context忽略score。

舉例

<span style="color:#444444"><code>(結果 1->2->3 同時分數都爲2.5)
POST news/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "match": {
         "content":"apple"
        }
      },
      "boost": 2.5
    }
  }
}</code></span>

運行結果

3db04cac-b466-4dcf-81e0-b6bd39790836.jpguploading.4e448015.gif轉存失敗重新上傳取消

可以看出分數都是2.5

 

 

四、dis_max(最佳匹配查詢)

1、概念

dis_max : 只是取分數最高的那個query的分數而已。

看下官方例子

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)創建數據

<span style="color:#444444"><code>#1、創建索引
PUT /dismax
{
    "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 1
    },
    "mappings": {
            "properties": {
                "title": {
                    "type":"text"
                },
                "content": {
                    "type":"text"
                }
        }
    }
}

#2、創建數據
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"
}</code></span>

2)、should查詢

<span style="color:#444444"><code>#should查詢查詢 (結果 3->2->1
GET /dismax/_search
{
    "query": {
        "bool": {
            "should": [
                { "match": { "title": "java beginner" }},
                { "match": { "content":  "java beginner" }}
            ]
        }
    }
}</code></span>

運行結果

 

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)

<span style="color:#444444"><code>#運行結果(2-1-3)
GET /dismax/_search
{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "java beginner" }},
                { "match": { "content":  "java beginner" }}
            ]
        }
    }
}</code></span>

運行結果

e59faa55-83f1-4c2d-87f0-e10fe4e3105a.jpguploading.4e448015.gif轉存失敗重新上傳取消

我們可以很明顯看出: 只是取分數最高的那個query的分數排序。

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)

<span style="color:#444444"><code>#運行結果 3-2-1
GET /dismax/_search
{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "java beginner" }},
                { "match": { "content":  "java beginner" }}
            ],
             "tie_breaker" : 0.5
        }
    }
}</code></span>

19f16afc-36b1-4b3d-92ee-52401d18db99.jpguploading.4e448015.gif轉存失敗重新上傳取消

這裏可以看出看出: 取分數最高的那個query的分數,同時其它子查詢查詢的分數乘以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(函數查詢)

1、概念

定義 function_score是處理分值計算過程的終極工具。它讓你能夠對所有匹配了主查詢的每份文檔調用一個函數來調整甚至是完全替換原來的_score。

注意 要使用function_score,用戶必須定義一個查詢和一個或多個函數,這些函數計算查詢返回的每個文檔的新分數。

它擁有幾種預先定義好了的函數:

weight 對每份文檔適用一個簡單的提升,且該提升不會被歸約:當weight爲2時,結果爲2 * _score。

field_value_factor 使用文檔中某個字段的值來改變_score,比如將受歡迎程度或者投票數量考慮在內。

random_score 使用一致性隨機分值計算來對每個用戶採用不同的結果排序方式,對相同用戶仍然使用相同的排序方式。

衰減函數(Decay Function) - linear,exp,gauss

將像publish_date,geo_location或者price這類浮動值考慮到_score中,偏好最近發佈的文檔,鄰近於某個地理位置(譯註:其中的某個字段)的文檔或者價格

(譯註:其中的某個字段)靠近某一點的文檔。

script_score

使用自定義的腳本來完全控制分值計算邏輯。如果你需要以上預定義函數之外的功能,可以根據需要通過腳本進行實現。

2)使用場景

有關function_score如果要深入講,估計一篇博客都不夠,所以這裏說下在現實中可能會用的場景,如果你有這些場景,那麼就可以考慮用function_score。

1)假設我們又一個資訊類APP我們希望讓人閱讀量高的文章出現在結果列表的頭部,但是主要的排序依據仍然是全文搜索分值。

2)當用戶搜索酒店,它的要求是 1、離他目前位置1Km內 2、價格在500元內。如果我們只是使用一個 filter 排除所有市中心方圓 1KM以外的酒店,
再用一個filter排除每晚價格超過500元的酒店,這種作法太過強硬,可能有一間房在2K米,但是超級便宜一晚只要100元,用戶可能會因此願意妥協住這間房。

有關function_score例子這裏就不寫了,具體的可以參考官方文檔:Function score query

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章