Elasticsearch 之 Filter 與 Query 有啥不同?

武培軒發表於2021-02-05

今天來了解下 Elasticsearch(以下簡稱 ES) 中的 Query 和 Filter。

在 ES 中,提供了 Query 和 Filter 兩種搜尋:

  • Query Context:會對搜尋進行相關性算分
  • Filter Context:不需要相關性算分,能夠利用快取來獲得更好的效能

舉一個栗子,比如需要搜尋一場電影,包含以下資訊:

評論中包含了燒腦,評分高於 8 分,同時上映時間在 2010 到 2020 之間。

所以這個搜尋包括了三個判斷邏輯,針對三個不同的欄位進行查詢,如果需要滿足這樣的查詢需求,在 ES 當中提供了 bool 查詢,一個 bool 查詢可以包含一個或多個查詢字句,支援以下四種查詢:

  • must:必須匹配,貢獻算分
  • should:選擇性匹配,貢獻算分
  • must_not:查詢字句,必須不能匹配
  • filter:必須匹配,不貢獻算分

上圖是一個 bool 查詢,是對使用者(user)進行搜尋,城市必須是北京(beijing) ,性別必須是男(man),這個採用的是 filter,說明這個對算分是不會產生影響的,must_not 是一個 range 的查詢:年齡大於等於 35 歲;should 裡是一個陣列,說明這個 should 中可以寫多個條件,只要使用者的名字是這兩個中的一個就是滿足條件的。

其實,bool 查詢的子查詢可以任意順序出現,並且可以巢狀多個查詢。

另外,should 的使用分兩種情況:

  • bool 查詢中只包含 should,不包含 must 查詢
  • bool 查詢中同時包含 should 和 must 查詢

下面讓我們來看看這兩種情況有何不同?

如果在 bool 查詢中沒有 must 子句,should 中必須至少滿足一條查詢(可以通過 minimum_should_match 來設定滿足條件的個數或者百分比)。

同時包含 should 和 must 時,文件不必滿足 should 中的條件,但是如果滿足條件,會增加相關性算分。

Filter Context

上面說到了 filtermust_not 是不會影響算分的,通過查詢結果中可以看到 _score 都是 0。

Query Context

採用 should 查詢,會進行算分處理,結果如下圖所示:

同時,查詢語句的結構,也會對相關度算分產生影響:

  • 同一層級的查詢欄位,權重是相同的
  • 通過巢狀 bool 查詢,可以改變對算分的影響

Boost & Boosting Query

相關度還可以通過對某個欄位設定 boost 的值來進行控制:

  • 當 boost > 1 時,打分的相關度相對性提升
  • 當 0 < boost < 1 時,打分的權重相對性降低
  • 當 boost < 0 時,貢獻負分

或者使用 ES 提供的 Boosting Query 進行查詢:

首先插入幾條資料用於測試:

POST /product/_bulk
{ "index": { "_id": 1 }}
{ "content":"Apple Mac" }
{ "index": { "_id": 2 }}
{ "content":"Apple iPad" }
{ "index": { "_id": 3 }}
{ "content":"Apple Juice" }

如下圖所示,左邊就是一個 Boosting Query,positive 查詢意思是如果 content 中包含 Apple 會按照原始的相關性分數進行打分,negative 查詢則是滿足 positive 查詢同時滿足 negative 查詢(content 中包含 Juice)的會按照原始的相關性分數乘以 negative_boost 進行打分,negative_boost 是用於降低與 negative 匹配文件的相關性算分的。

如右圖所示,這個的查詢結果為三條資料,可以發現 Apple MacApple iPad 的相關性算分相同,都排在前面,而 Apple Juice 的相關性算分是其他兩個的 0.1 倍,排在最後。

用一個表格來總結下 Query Context 和 Filter Context 的區別:

Context Type 含義 使用方式
Query 查詢與查詢語句最匹配的文件,對所有文件進行相關性算分並排序 query;bool 中的 must 和 should
Filter 查詢與查詢語句相匹配的文件 bool 中的 filter 和 must_not;constant_score 中的 filter

filter 不需要計算相關性算分,不需要按照相關分數進行排序,同時還有內建的自動 cache 最常使用的 filter 的資料,而 query 相反,需要計算相關性算分,按照分數進行排序,而且無法 cache 結果,因此在某些不需要相關性算分的查詢場景,儘量使用 Filter Context 來讓查詢更加高效。

下圖為 eBay 對於 Filter Context 和 Query Context 的效能比較:

Filter Context VS Query Context

那麼 filter 的 cache 是怎麼做的呢?

ES 會構建一個文件匹配過濾器的位集 bitset(用來標識一個文件對一個 filter 條件是否匹配,如果匹配就是 1,不匹配就是 0),下次再有這個 filter 條件過來的時候就不用重新掃描倒排索引,反覆生成 bitset,可以大幅度提升效能,另外當新增或更新文件時,這個 filter 的位集 bitset 也會更新。

總結

當使用者輸入多個條件進行查詢的時候,可以使用 bool 查詢,在 bool 查詢中,filtermust_not 屬於 Filter Context,不會對算分結果產生影響;mustshould 屬於 Query Context,會對結果算分產生影響。

在 bool 查詢中,查詢結構是對相關性算分有影響的,可以通過巢狀的方式修改不同欄位在查詢中的權重以及直接通過指定欄位的 boost 值來控制在搜尋中的權重,另外使用 Boosting Query 可以提升搜尋的精準性,同時也可以將更多的搜尋顯示在結果中。

最好的關係就是互相成就,大家的點贊、在看、分享、留言就是我創作的最大動力。

參考

Elastic Stack從入門到實踐

Elasticsearch核心技術與實戰

Elasticsearch頂尖高手系列-快速入門篇

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-boosting-query.html

相關文章