天天看點

一個線上問題引發的思考——Elasticsearch 8.X 如何實作更精準的檢索?

1、線上問題

一個線上問題引發的思考——Elasticsearch 8.X 如何實作更精準的檢索?

——問題來自:死磕Elasticsearch 知識星球微信群

這個問題涉及到業務細節,至今沒有定論。不過,該問題引發了我的思考。

2、我的一點思考

我們使用 Elasticsearch 到底用來做什麼?

除了 Elasticsearch 早已不是10年前因“菜單”而火出技術圈的搜尋引擎元件,它早已不是“單兵作戰”,而是 ELKB 形成的 Elastic Stack “行走江湖”。

一個線上問題引發的思考——Elasticsearch 8.X 如何實作更精準的檢索?

但,至少技術選型涉及到大資料的檢索幾乎無一例外 Elasticsearch 都是“首發陣容”。

2.1 關于全文檢索,使用者更關注什麼?

關于全文搜尋,《這就是搜尋引擎》張俊林博士從搜尋引擎的角度闡述了使用者的關注點,核心就是兩個。

  • 其一:精準率;
  • 其二:召回率。

通俗點講:

  • 精準率是站在使用者角度,召回的資料貼合使用者的預期,越準确越好。

當然,大資料時代的今天,單純的字、詞比對早已跟不上時代的步伐,基于使用者行為的推薦(如:抖音、網易雲音樂)往往更吊起使用者的胃口。

一個線上問題引發的思考——Elasticsearch 8.X 如何實作更精準的檢索?
  • 而召回率是滿足檢索條件的語句都盡可能的召回,到底要什麼,讓使用者在結果中二次再做選擇。

這兩種都有應用場景,無所謂誰對誰錯。

提到 Elasticsearch 精準召回資料,先不談“精準”,先說一下召回。

如下圖所示,可以分兩部分看:資料的寫入過程、資料檢索過程。

一個線上問題引發的思考——Elasticsearch 8.X 如何實作更精準的檢索?

資料寫入過程要比圖中複雜,我們着重關注建立反向索引的過程,因為後面我們要基于反向索引做全文檢索。

2.2 資料寫入過程

寫入的文本如下:

一個線上問題引發的思考——Elasticsearch 8.X 如何實作更精準的檢索?

基于 ik_smart 分詞後,反向索引的中的分詞詞典如下所示:

一個線上問題引發的思考——Elasticsearch 8.X 如何實作更精準的檢索?

2.3 資料檢索過程

Elasticsearch 提到檢索,這其實是一個大的概念,不信你看下面的腦圖。

一個線上問題引發的思考——Elasticsearch 8.X 如何實作更精準的檢索?

Elasticsearch 檢索從大的角度可以看成黑盒,類似:Google、baidu 搜尋框。使用者搜尋框輸入内容,檢索召回資料。

但是,技術人員的眼裡看搜尋,更關注用哪種類型搜尋。這涉及到檢索分類。

Elasticsearch 檢索大緻可以分為:全文檢索、精準比對檢索、多表關聯檢索、組合檢索、不常用檢索。

精準比對檢索回答的是“是和否、存在或不存在”問題?比如:小明的手機号為:“13566668888”,多一位、少一位、錯一位都不能被召回!完全滿足檢索條件就召回,不滿足檢索條件就不要召回。不存在中間情況。

而全文檢索回答的是“相關度”的問題?如文章開頭提到的“手表”、“手表帶”、“表帶”就有相關度,哪些資料該召回?誰優先被召回(也就是誰排在前面)。

match_phrase 和 match 等實作檢索的機制是不一樣的,“profile:true" API 能幫我們更精細的看到底層的實作。

簡單點說:match_phrase 走的是短語檢索比對,而 match 走的是多字段拆解後的 term query 的 bool 語句組合體。

一個線上問題引發的思考——Elasticsearch 8.X 如何實作更精準的檢索?
一個線上問題引發的思考——Elasticsearch 8.X 如何實作更精準的檢索?

2.4 如何了解精準?

其實這個沒有普适的标準,不同的業務系統是不一樣的。

建議,結合業務需求、産品經理和技術經理、項目經理、核心技術人員共同敲定。

滿足使用者要求的“精準”才算是精準。

比如文章開頭提到的“手表”,其實有多種了解?

  • 其一:隻有“手表”兩個字,沒有任何其他,這種叫精準。
  • 其二:分詞詞典裡有“手表”,就要召回,這種也叫精準。
  • 其三:隻要文本裡有就要召回,這也是某種意義的精準。比如:“南京市長江大橋”,搜尋“江大橋”也要求召回。

等等......不一而足。

有了上面的思考,我們嘗試解讀一下開篇的問題。

3、Elasticsearch 8.X 更精準檢索實作

如下示例,拿資料說話!

3.1 字詞混合索引檢索方案

  • 詞索引——使用 ik_smart 分詞。
  • 字索引——使用  standard 标準分詞。

保留了 keyword 類型,便于方案二的精準字元比對。

DELETE test-20220928
PUT test-20220928
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "analyzer": "ik_smart",
        "fields": {
          "standard": {
            "type": "text",
            "analyzer": "standard"
          },
          "keyword": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

POST test-20220928/_bulk
{"index":{"_id":1}}
{"title":"手表帶真好看"}
{"index":{"_id":2}}
{"title":"手表最近賣的不好,咋整"}
{"index":{"_id":3}}
{"title":"卡西歐手表不錯哦"}
{"index":{"_id":4}}
{"title":"手表"}      

先看“手表帶真好看”這個文檔 ik_smart 的分詞結果。

其他幾個文檔{“2”,“3”,“4”} 都包含手表的分詞,大家可以自己驗證,篇幅原因,沒有截圖。

一個線上問題引發的思考——Elasticsearch 8.X 如何實作更精準的檢索?

如下檢索是 bool 組合混合體。

對于:must 條件要求單字相連的多字(可以了解為短語,但不見得是有意義的短語,如:江大橋)必須滿足,用 短語 match_phrase 進行檢索。

對于:should 條件滿足 ik_smart 分詞存在結果,則召回資料,且極大的提升評分權重。

POST test-20220928/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match_phrase": {
            "title": {
              "query": "手表",
              "boost": 50
            }
          }
        }
      ],
      "must": [
        {
          "match": {
            "title.standard": "手表"
          }
        }
      ]
    }
  }
}      

明顯看出來:包含手表要排在前面。

一個線上問題引發的思考——Elasticsearch 8.X 如何實作更精準的檢索?

3.2 自定義評分實作精準檢索

使用前提:針對是 keyword 類型。

大家記住:sort 排序、aggregation 聚合、script 腳本都隻能針對 keyword 類型,text 類型都是不支援的,除非開啟“fielddata”(必要性非常小,使用場景也小,不建議開啟)。

如下腳本的含義,如果字段精準比對,沒有多餘字元,則評分極高,設定為1000;如果字段以給定關鍵詞開頭,則評分高,設定為500;如果屬于包含關系,則評分也較高,設定為100;如果沒有包含,那評分為10。

POST test-20220928/_search
{
  "query": {
    "script_score": {
      "query": {
        "match_all": {}
      },
      "script": {
        "source": "if(doc['title.keyword'].value == params.keyword) { return 1000; } else if(doc['title.keyword'].value.startsWith(params.keyword)){ return 500; } else if(doc['title.keyword'].value.contains(params.keyword)) { return 100; }  else { return 10;}",
        "params": {
          "keyword": "手表"
        }
      }
    }
  }
}      

相當于我們人工幹預了評分,基于字段精準比對的情況實作了評分的區分。這樣,最先召回的結果資料就是我們最期望的精準比對結果了。

一個線上問題引發的思考——Elasticsearch 8.X 如何實作更精準的檢索?

4、小結

針對企業級實戰問題,引發了思考,并根據思考嘗試做了解答。

當然,這道業務題目會有具體的細節業務場景,還需要進一步溝通交流。

  • 分詞(中文分詞器、預設分詞器)
  • 組合分詞(fields)
  • 組合檢索
  • 排序(評分)+ 全文檢索+召回
  • 自定義評分(自己定義的規則來進行資料的評分,進而将評分高的優先傳回,排在前面進行傳回!)