Elasticsearch(7) --- 複合查詢

複合查詢

說明:該博客對於的Elasticsearch 的版本爲7.3。html

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

1、bool query(布爾查詢)

一、概念

定義 能夠理解成經過布爾邏輯將較小的查詢組合成較大的查詢。json

Bool查詢語法有如下特色數組

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

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

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

二、官方例子

看下官方舉例dom

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。分數僅受已指定查詢的影響。elasticsearch

官方例子ide

GET _search
{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "status": "active"
        }
      }
    }
  }
}

這個例子查詢查詢爲全部文檔分配0分,由於沒有指定評分查詢。函數

官方例子工具

GET _search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "term": {
          "status": "active"
        }
      }
    }
  }
}

此bool查詢具備match_all查詢,該查詢爲全部文檔指定1.0分。

三、Bool嵌套查詢

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

2、boosting query

一、概念

在上面的複合查詢咱們能夠經過must_not+must 先剔除不想匹配的文檔,再獲取匹配的文檔,可是有一種場景就是我並不須要徹底剔除,而是把須要剔除的那部分文檔的

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

二、舉例

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

# 建立索引並添加數據
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

上面第二種比較粗暴,可能我實際開發過程當中,若是出現 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的一半。

三、思考boosting query應用場景

場景舉例 咱們經過去索引中搜索 '蘋果公司' 相關的信息,而後咱們在查詢中的信息爲 '蘋果'

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

可是咱們須要的結果是蘋果公司相關信息,若是你的文檔是 '蘋果樹','蘋果水果',那麼其實此蘋果非彼蘋果若是匹配到其實沒有任何意義。

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

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

因此直接過濾掉,看是沒啥問題。

可是你想,這樣作是否是太粗暴了,由於一個文檔中包含'蘋果'和'樹'那不表明必定是蘋果樹,而多是 '蘋果公司組織員工一塊兒去種樹' 那麼這條文檔理應出現

而不是直接過濾掉,因此咱們就能夠用boosting query。就像上面這個例子同樣。


3、constant_score(固定分數查詢)

定義 常量分值查詢,目的就是返回指定的score,通常都結合filter使用,由於filter context忽略score。

舉例

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

運行結果

能夠看出分數都是2.5


4、dis_max(最佳匹配查詢)

一、概念

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

二、舉例

1)建立數據

#一、建立索引
PUT /dismax
{
    "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 1
    },
    "mappings": {
            "properties": {
                "title": {
                    "type":"text"
                },
                "content": {
                    "type":"text"
                }
        }
    }
}

#二、建立數據
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" }}
            ]
        }
    }
}

運行結果

咱們能夠很明顯看出: 只是取分數最高的那個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)

#運行結果 3-2-1
GET /dismax/_search
{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "java beginner" }},
                { "match": { "content":  "java beginner" }}
            ],
             "tie_breaker" : 0.5
        }
    }
}

這裏能夠看出看出: 取分數最高的那個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


5、function_score(函數查詢)

一、概念

定義 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)當用戶搜索酒店,它的要求是 一、離他目前位置1Km內 二、價格在500元內。若是咱們只是使用一個 filter 排除全部市中心方圓 1KM之外的酒店,
再用一個filter排除每晚價格超過500元的酒店,這種做法太過強硬,可能有一間房在2K米,可是超級便宜一晚只要100元,用戶可能會所以願意妥協住這間房。

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


### 參考

一、Elasticsearch核心技術與實戰---阮一鳴(eBay Pronto平臺技術負責人

二、ES7.3版官方複合查詢API

三、Elasticsearch學習筆記—Boosting query

四、ElasticSearch - function_score



我相信,不管從此的道路多麼坎坷,只要抓住今天,早晚會在奮鬥中嚐到人生的甘甜。抓住人生中的一分一秒,賽過虛度中的一月一年!(11)
相關文章
相關標籤/搜索