創作人:銘毅天下
審稿人:李捷
本應用實踐,主要針對 Elasticsearch 如何實作類似百度廣告置頂顯示,給定商品資料的效果展開介紹,例如實作置頂顯示某特定資料,像搜尋某關鍵詞,出現關聯廣告置頂顯示的效果。
舉例:某搜尋引擎 “電動汽車”,結果如下:
上面實作的本質:
傳回結果的第一頁頭1條或多條資料是服務端(如電商網站、主流搜尋引擎)指定的資料,而非按照相關度評分計算得出的結果資料。
這時候,不禁要問 Elasticsearch 能實作類似功能不 ?
拆解實作
Elasticsearch from + size 分頁實作機制的原理(大緻意思):
page 1:from 0,size:10——傳回第 0 到 第 9 條資料。
page 2:from 10,size:10——傳回第 0 到 第 19 條資料,截取第 10 到 第 19 條資料;
page 3:from 20,size:10——傳回第 0 到 第 29 條資料,截取 第 20到 第 29 條資料。
......
本質是深度分頁,肯定越往後翻頁響應越慢。
要實作根據固定關鍵詞,添加特定資料置頂顯示的效果,探讨方案如下:
方案一:不重新分頁,犧牲首頁部分資料
不再做重新分頁,強制将 page 1 部分資料,換成:類【廣告位】置頂顯示資料。
顯然,會有資料丢失,導緻搜尋精準率下降,使用者一般不會接受。
方案二:重新記憶體分頁
将類【廣告位】置頂顯示資料 + 已有傳回的前 10 頁(舉例:100 條資料)重新組合後,再分頁。
需要記憶體維護一堆資料,有較大記憶體開銷。使用者期望翻頁越深(比如:100頁+),維護資料越大,處理越慢、延時會越明顯。
方案三:其他方案
類主流搜尋引擎實作的方法或者新的實作機制。
但此時要想,有沒有更簡潔的實作方式呢?
Elastic 官方沒有考慮這個使用者需求嗎?
有的,從 Elasticsearch 7.4.0 新增的 pinned query 就能實作這種功能。
Pinned query 介紹
Pinned query 是 Elasticsearch 7.4.0 版本之後,實作的增強檢索功能。
Pinned:中文翻譯為“固定”。
Pinned query 則可以解釋為——固定某些結果,首頁置頂顯示的檢索方式。
下圖能形象的說明:綠色的 Pinned results 就是要首頁置頂顯示的結果。
Pinned query 實戰實作
基礎資料 Demo 如下,直接拿文章開頭的截圖示例模拟一下,假設 id為 1、2、3 的3條資料是需要特意置頂顯示的資料
PUT index_001
{
"mappings": {
"properties": {
"title":{
"type": "text",
"analyzer": "ik_max_word",
"fields": {
"keyword":{
"type":"keyword"
}
}
}
}
}
}
PUT index_001/_bulk
{"index":{"_id":1}}
{"title":"大衆汽車首款純電動ID.4_預售進行時_先訂先享"}
{"index":{"_id":2}}
{"title":"保時捷首款純電動跑車Taycan - 現已到店 - 電馳神往"}
{"index":{"_id":3}}
{"title":"純電動電動汽車?英國國際貿易部_邀您來投資英國汽車工業"}
{"index":{"_id":4}}
{"title":"四輪電動車_ 電動汽車報價_阿裡巴巴采購批發_超多品類低價批發"}
{"index":{"_id":5}}
{"title":"電動汽車之家,為新能源汽車而生 - 第一電動網"}
{"index":{"_id":6}}
{"title":"中國電動汽車網_新能源汽車_電動汽車網"}
{"index":{"_id":7}}
{"title":"電車之家_領先的電動汽車及新能源汽車行業門戶網站"}
如果要召回既包含:“電動汽車” 完全比對,又包含“電動”或“汽車” 分詞比對的全量資料。大緻的檢索語句如下:
POST index_001/_search
{
"query": {
"bool": {
"should": [
{
"match_phrase": {
"title": {
"query": "電動汽車",
"boost": 5
}
}
},
{
"bool": {
"should": [
{
"match": {
"title": "電動"
}
},
{
"match_phrase": {
"title": "汽車"
}
}
],
"minimum_should_match": 2
}
}
]
}
}
}
如上檢索部分:完全比對加了 boost 提升權重。
傳回結果如下:
傳回結果按照評分由高到低順序排列,_id 序列為:5、7、3、6、4 ......
置頂顯示_id 為 1、2、3 的資料,pinned query 實作如下:
GET index_001/_search
{
"query": {
"pinned": {
"ids": [
"1",
"2",
"3"
],
"organic": {
"bool": {
"should": [
{
"match_phrase": {
"title": {
"query": "電動汽車",
"boost": 5
}
}
},
{
"bool": {
"should": [
{
"match": {
"title": "電動"
}
},
{
"match_phrase": {
"title": "汽車"
}
}
],
"minimum_should_match": 2
}
}
]
}
}
}
}
}
本質是在原來檢索語句的基礎上,添加了如下部分代碼:
"pinned": {
"ids": [
"1",
"2",
"3"
],
"organic": {
第一:置頂顯示的 id ,寫法如下:
第二:除了置頂資料之外的其餘正常檢索語句塊内容。隻是加了“organic” 包裹一層。其中的檢索語句還是原來的寫法 ,拷貝過來即可。
傳回結果已 pinned(類似做了“廣告位”定制),_id 序列為:1、2、3、5 ....... 實作了類百度置頂顯示廣告的效果。
Pinned query 源碼解讀
認知前提:源碼中最大評分計算方法
float MAX_ORGANIC_SCORE = Float.intBitsToFloat((0xfe << 23)) - 1;
本質下面代碼等價:
float max_rst = (float)Math.pow(2,127);//1.7014118E38
也就是說:MAX_ORGANIC_SCORE 大小為:2 的 127 次幂,是 Elasticsearch float 最大值。
最大評分作用
正常查詢的評分得分不會超過 MAX_ORGANIC_SCORE, 将固定查詢(pinned query)的評分設定為:MAX_ORGANIC_SCORE。
pinned query 保證置頂顯示解密
原理:将置頂顯示的資料通過 bool 組合查詢 + boost 提升權重的方式給設定了 float 最大值評分,這樣就能保證置頂顯示了。
核心源碼實作如下:
注意細節沒有深究,比如:置頂傳回的結果顯示的是原始評分。
小結
讀者可能會問:這并沒有實作基于特定關鍵詞傳回特定資料的需求?其實有了pinned query 再将特定關鍵詞與待置頂顯示文章 _id ,建立個一對多的映射關系就可以實作。映射關系可以自己記憶體維護或者借助 redis 實作都可以。
你我發現的新需求,很可能别人早就發現,且已經送出 Git了。更可怕的是:官方新版本已經實作了!
要注重基礎夯實的同時,多關注一下技術動态。兩手抓、兩手都要硬!
參考:
創作人簡介:
銘毅天下,Elastic 認證工程師、Elastic 官方合作教育訓練講師、阿裡雲 MVP、CSDN 部落格
專家、銘毅天下 Elasticsearch 公衆号作者、死磕 Elasticsearch 知識星球星主。近 10
年工作經驗,關注 Elastic Stack 技術棧、大資料技術領域。
部落格:
https://elastic.blog.csdn.net/