Elasticsearch 復合查詢

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

一、bool query(布爾查詢)

1、概念

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

Bool查詢語法有以下特點

  1. 子查詢可以任意順序出現(xiàn)
  2. 可以嵌套多個查詢,包括bool查詢
  3. 如果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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容