Elasticsearch 有沒有比 reindex 更輕量級的更換字段類型的方式?
1、線上實戰(zhàn)環(huán)節(jié)遇到問題
現(xiàn)在有一組數(shù)據(jù),其中 resultChar 是 keyword 類型,但其中有數(shù)字也有字符串,請問怎么能在大于小于查詢的時候?qū)⑵渲械臄?shù)字按照數(shù)字的類型進行大于小于的查詢,結(jié)構(gòu)如下:
{
"lisCheckItemList": [
{
"resultChar": "1",
"itemName": "項目1"
},
{
"resultChar": "2",
"itemName": "項目2"
},
{
"resultChar": "11",
"itemName": "項目3"
},
{
"resultChar": "22",
"itemName": "項目4"
},
{
"resultChar": "25",
"itemName": "項目"
},
{
"resultChar": "3",
"itemName": "項目5"
},
{
"resultChar": "陰性",
"itemName": "項目6"
}
]
}
結(jié)果:如果查詢 resultChar 大于 2 的數(shù)據(jù)的話目前不會查詢出 resultChar 為 11 的數(shù)據(jù),因為現(xiàn)在的 resultChar 類型為 keyword .但是因為 resultChar 里面有數(shù)字也有字符所以不可以使用數(shù)字的類型.請問有什么方法可以將 keyword 類型中的數(shù)字按照數(shù)字類型進行大于小于查詢.
期待結(jié)果: 查詢 resultChar 大于 2的數(shù)據(jù)時會過濾掉小于 2 的并查詢出大于 2 的數(shù)據(jù)(查詢出 resultChar 為陰性的那個數(shù)據(jù)也可以,只要數(shù)字的查詢是對的就可以)
問題來源:Elasticsearch 中文社區(qū)
https://elasticsearch.cn/question/12809
2、問題拆解
2.1 發(fā)現(xiàn)問題:數(shù)據(jù)建模不合理
對于 resultchar 字段來說,這個字段前面幾個值都是數(shù)值加了引號的字符串類型,后面是“陰性”。MySQL 有數(shù)據(jù)完整性這一說,要求相同字段的語義是合理的,沒有歧義的,是相容的。
Elasticsearch 雖沒有類似的完整性說明。這種寫入數(shù)據(jù)方式,從 Elasticsearch 角度來看,沒有問題,都是字符串。但是,從業(yè)務(wù)層面來看,這帶來后來處理的不確定性和麻煩。
一句話,這種建模方式有“百害”而無一利。建議從業(yè)務(wù)角度出發(fā),及時止損。嚴格遵守數(shù)據(jù)建模規(guī)范理論,重新建模,數(shù)據(jù)寫入更加符合業(yè)務(wù)邏輯,從根源上解決這個問題。
2.2 問題本質(zhì)
將:resultChar 類型轉(zhuǎn)換成數(shù)值類型,以便執(zhí)行 range query 范圍查詢操作。
因為:keyword 類型本質(zhì)是字符串類型的一種,以 keyword 類型做字符串處理比的是首字符的 ASCII 碼值,達不到預(yù)期的效果。
2.3 方案探討
接下來是怎么轉(zhuǎn)換字段類型的問題?
傳統(tǒng)的處理方案如下:
方案一:重新建模、重新導(dǎo)入數(shù)據(jù)。
特點:從根源上解決問題。
方案二:reindex + alias 別名零停機方案。
特點:重新建模,重新遷移數(shù)據(jù),用別名方式方案讓用戶無感知。
這時候,我們會思考:有沒有更為簡潔的方式呢?
本文會提供如下方案三的一種方式, 讓大家評說是否簡潔。
方案三:convert ingest 預(yù)處理 + reindex 結(jié)合方案。
特點:無需重新建模,哪個字段不滿足要求就改哪個字段。
下面我們著重講解一下方案三。
3、方案三的實戰(zhàn)實現(xiàn)
3.1 數(shù)據(jù)建模
我們只有數(shù)據(jù),得從頭模擬,所以建模是第一步。
PUT test-20220529-04
{
"mappings": {
"properties": {
"lisCheckItemList": {
"type": "object",
"properties": {
"resultChar": {
"type": "keyword"
},
"itemName": {
"type": "keyword"
}
}
}
}
}
}
3.2 數(shù)據(jù)寫入
POST test-20220529-04/_doc/1
{
"lisCheckItemList": [
{
"resultChar": "1",
"itemName": "項目1"
},
{
"resultChar": "2",
"itemName": "項目2"
},
{
"resultChar": "11",
"itemName": "項目3"
},
{
"resultChar": "22",
"itemName": "項目4"
},
{
"resultChar": "25",
"itemName": "項目"
},
{
"resultChar": "3",
"itemName": "項目5"
}
]
}
POST test-20220529-04/_doc/2
{
"lisCheckItemList": [
{
"resultChar": "1",
"itemName": "項目1"
},
{
"resultChar": "2",
"itemName": "項目2"
}
]
}
POST test-20220529-04/_doc/3
{
"lisCheckItemList": [
{
"resultChar": "30",
"itemName": "項目1"
},
{
"resultChar": "100",
"itemName": "項目2"
}
]
}
3.3 數(shù)據(jù)預(yù)處理 convert 實現(xiàn)
# 物理的mapping 不會變
PUT _ingest/pipeline/mytx_pipeline_20220530
{
"processors": [
{
"foreach": {
"field": "lisCheckItemList",
"processor": {
"convert": {
"field": "_ingest._value.resultChar",
"type": "integer",
"ignore_failure": true
}
}
}
}
]
}
解釋一下,lisCheckItemList 是 object 對象,所以需要foreach 遍歷其下面的值,并通過:ingest.value.resultChar 的方式實現(xiàn)字段類型的 convert 轉(zhuǎn)換。
做了什么轉(zhuǎn)換呢?由:“keyword”類型變成“integer”類型。
3.4 數(shù)據(jù) reindex 遷移
POST _reindex
{
"source": {"index": "test-20220529-04"},
"dest": {"index": "test-20220529-05", "pipeline": "mytx_pipeline_20220530"}
}
強調(diào)一個語法知識點,也是大家認證考試容易出錯的點。
這里的 pipeline 要寫到“dest”目標(biāo)索引部分實現(xiàn),而不是“source”源索引部分。
3.5 數(shù)據(jù)檢索,驗證是否達到預(yù)期
POST test-20220529-05/_search
{
"query": {
"range": {
"lisCheckItemList.resultChar": {
"gte": 30,
"lt": 100
}
}
}
}
由于是在數(shù)據(jù)同步的時候,同時切換了數(shù)據(jù)類型。
所以,本質(zhì)上是沒有問題,rangquery 的檢索自然會達到預(yù)期效果。
4、小結(jié)
同步一個小細節(jié),如果我們上面的預(yù)處理 ingest 部分不是做遷移,而是做 update_by_query 會怎么樣?
這是一個不大不小的“腦洞”。我們結(jié)論說一下, 留給大家去思考和實踐。
如果僅是:update_by_query 和 ingest 結(jié)合,數(shù)據(jù)的類型也就是 Mapping 依然會是:keyword,但是數(shù)據(jù)的顯示會去掉了“ ”,這實際是“治標(biāo)不治本”的方式,不推薦大家使用。
小問題蘊含大道理。
比 reindex 更輕量級的更換字段類型的方式技能,你 Get 到了嗎?
歡迎大家留言反饋??!
推薦閱讀
更短時間更快習(xí)得更多干貨!
和全球 1600+ Elastic 愛好者一起精進!

