天天看點

Elaticsearch 6.8 算分介紹

  • 前段時間寫多項式算分插件,發現 ES 的算分概念還是挺多的,主要有 Query, Weight, Scorer。本文簡單介紹一下,不過還有一些細節看得也不清晰,如果有錯,歡迎斧正

查詢流程概覽

  • 首先看下 ES 的查詢流程
    • HTTP 收到請求,按 Shard 分發到 Data
    • Data 按 shard 查詢結束後,發往 http merge, 然後再發往 Data fetch
    • 算分便是發生在 lucene:search (org.apache.lucene.search.IndexSearcher#search) 中
      Elaticsearch 6.8 算分介紹

算分過程

  • 算分整體分為四步
    1. 從 Query 依次按層建構 Weight
    2. 從 Weight 建構 BulkScorer
    3. 從 BulkScorer 建構 Scorer
    4. Collector 收集時用 Scorer 按文檔打分,得到分數
      Elaticsearch 6.8 算分介紹
Elaticsearch 6.8 算分介紹
Elaticsearch 6.8 算分介紹
Elaticsearch 6.8 算分介紹
Elaticsearch 6.8 算分介紹

從 Query Builder 到 Query

  • 熟悉 ES 的同學可能都知道我們寫的查詢在 ES 内部都是 QueryBuilder,那麼 Query 是什麼呢?又怎麼從 QueryBuilder 轉換到 Query 的呢?
  • QueryBuilder 為 ES 内部對象,用于和使用者(XContent)以及叢集間傳遞(Stream)。QueryBuilder 均有兩個方法 toQuery , ToFilter 。 分别轉為普通的 lucene Query 和不用算分的 lucene Query。不過 toFilter 目前基本無使用。大多 QueryBuilder 采用

    AbstractQueryBuilder

    實作,使用者僅需實作自定義序列化反序列化及

    doToQuery

    即可
    Elaticsearch 6.8 算分介紹
  • Query 為 lucene 内部對象, 用于 Lucene 中的查詢,主要有兩個核心方法, rewrite 和 createWeight
    Elaticsearch 6.8 算分介紹
    • rewrite 将高階 query 改寫為基礎 query,比如

      fuzzy,prefix,query_string,regexp,wildcard

      ,改寫成

      bool query

      或者

      bitset

      , 在 es 中也可使用

      _validate/query?rewrite=true

      直接看到改寫結果
      Elaticsearch 6.8 算分介紹
Elaticsearch 6.8 算分介紹
  • createWeight 則是構造用于查詢的 Weight,在其中可以指定要不要算分,當不要算分時,有些 query 會進行改寫,比如 bool query 會将 must 移入 filter。另外也隻有不要算分時,weight 才會進緩存。

Weight 是什麼

  • ES 文檔中是這樣描述的
The purpose of Weight is to ensure searching does not modify a Query, so that a Query instance can be reused.
  • 可見,Weight 最大的作用就是儲存和 IndexSearcher 相關的狀态,類似 Query 級的上下文,來保證 Query 的複用。 (不過沒看出有對 Query 的複用?)
  • Weight 主要有4個核心方法,1個輔助方法
    Elaticsearch 6.8 算分介紹
    • scorer 打分器
      • 傳入一個 LeafReaderContext ,傳回一個"打分器", 為什麼加引号,詳見下文介紹
    • bulkScorer
      • 批量打分器,search 調用的入口方法,友善在批量場景下做優化,比如二階段查詢加速,倒排鍊合并,大多數場景采用

        DefaultBulkScorer

    • scorerSupplier
      • 可以在不夠造 scorer 之前先判斷一下 cost。不過預設的實作是先建立了個 scorer 然後取了其疊代器的 cost
    • extractTerms
      • 獲得 query 中的 term,term query a:b, 則會獲得b . dfs 和高亮時會用到,如未實作,則無法高亮。如 terms 當 term 數大于16時,則會走 TermInSetQuery 的 weight, 其未實作此方法,是以無法高亮
    • explain
      • 解釋某篇 doc 命中/沒命中及分數原因
    • matches
      • 判斷某個 doc 有沒有命中,如果有二階段,則先用二階段粗略判斷一次。傳回一個

        MatchesIterator

        僅測試中用到

不單純的 Scorer

  • Scorer 雖名為打分器,但實際由兩部分組成,score 和 iterator,score 為真實打分邏輯,iterator 為命中 doc 的疊代器,也就是在生成 scorer 時才去查詢了哪些 doc 命中,也是以即使無需算分時,也需要有 scorer 對象,僅是 score 方法傳回固定值。
    Elaticsearch 6.8 算分介紹
  • 那麼 Scorer 和 相似性算分 Similarity 又是什麼關系呢?
    • Similarity 的具體實作為 SimScorer, 其為 TermScorer 的一部分,在 termScorer 算分時會調用相似性算分
      Elaticsearch 6.8 算分介紹
Elaticsearch 6.8 算分介紹

總結

  • 是以,總結一下,要實作一個算分需要怎麼樣呢?首先 QueryBuilder 轉換時要用 toQuery, 然後 needsScores 為 true, 然後 bulkScorer 中給 collector 設定了 scorer, 最後在 Collector 中調用 score 方可完成算分。

參考資料

繼續閱讀