Elasticsearch 有沒(méi)有數(shù)組類(lèi)型?有哪些坑?
1、Elasticsearch 數(shù)組常見(jiàn)問(wèn)題清單
近期 Elasticsearch 數(shù)組問(wèn)題被問(wèn)到的比較多,為了方便大家對(duì)數(shù)組建立全局認(rèn)知,我把數(shù)組相關(guān)實(shí)戰(zhàn)問(wèn)題梳理出來(lái),讓更多后來(lái)人遇到類(lèi)似問(wèn)題少走不必要的彎路。
精簡(jiǎn)幾個(gè)核心問(wèn)題列舉如下:
Elasticsearch 是否有數(shù)組類(lèi)型? Elasticsearch 數(shù)據(jù)選型有沒(méi)有坑? Elasticsearch 如何獲取數(shù)組中第i號(hào)位置的值? Elasticsearch 如何獲取數(shù)組最后一個(gè)元素的值?
2、Elasticsearch 是否有數(shù)組類(lèi)型?
了解 Elasticsearch 版本歷史的讀者會(huì)知道 1.X 版本中沒(méi)有獨(dú)立模塊強(qiáng)調(diào)數(shù)組 Array 類(lèi)型。
Elasticsearch 2.X 版本之后,數(shù)組類(lèi)型才單獨(dú)被拎出來(lái)。
在 Elasticsearch 中,沒(méi)有專(zhuān)門(mén)的數(shù)組數(shù)據(jù)類(lèi)型。默認(rèn)情況下,任何字段都可以包含零個(gè)或多個(gè)值,但是,數(shù)組中的所有值必須具有相同的數(shù)據(jù)類(lèi)型。什么意思呢?
long 類(lèi)型存儲(chǔ)一個(gè)值是long類(lèi)型,存儲(chǔ)多個(gè)自然就成為 long 數(shù)組類(lèi)型;
keyword 類(lèi)型存儲(chǔ)一個(gè)值是 keyword 類(lèi)型,存儲(chǔ)多個(gè)值就成為 keyword 數(shù)組類(lèi)型。
實(shí)戰(zhàn)一把:
PUT?my-index-1230
{
??"mappings":?{
????"properties":?{
??????"horry":?{
????????"type":?"keyword"
??????}
????}
??}
}
#?單值?keyword?類(lèi)型
POST?my-index-1230/_doc
{
??"horry":?"pingpang"
}
#?keyword?數(shù)組類(lèi)型
POST?my-index-1230/_doc
{
??"horry":?[
????"pingpang",
????"basketball",
????"football"
??]
}
GET?my-index-1230/_search
如上示例,可以更加清晰的看出,當(dāng)我們選型使用數(shù)組時(shí),和平時(shí) Mapping 設(shè)置類(lèi)型一樣,不需要額外的修改 Mapping 任何內(nèi)容,只需要導(dǎo)入相同數(shù)據(jù)類(lèi)型的數(shù)據(jù)即可。
這時(shí)候,讀者可能會(huì)問(wèn),我不小心寫(xiě)入了不同類(lèi)型的數(shù)據(jù)咋辦?
繼續(xù)實(shí)戰(zhàn)一把,探個(gè)究竟。
#?導(dǎo)入不合規(guī)數(shù)據(jù),數(shù)據(jù)依然可以寫(xiě)入
POST?my-index-1230/_doc
{"horry":["11111",22222,33333]}
#?數(shù)據(jù)依然可以被召回
GET?my-index-1230/_search
{
??"query":?{
????"term":?{
??????"horry":?{
????????"value":?"22222"
??????}
????}
??}
}
由于 Elasticsearch 做了弱類(lèi)型匹配校驗(yàn)檢查,導(dǎo)致數(shù)據(jù)依然可以寫(xiě)入,我們?nèi)庋劭吹降恼晤?lèi)型,實(shí)際本質(zhì)存儲(chǔ)為keyword 類(lèi)型,這也是為什么“22222”能被檢索召回的原因。
3、Elasticsearch 數(shù)據(jù)選型有沒(méi)有坑?
3.1 動(dòng)態(tài)導(dǎo)入數(shù)組類(lèi)型數(shù)據(jù),第一次寫(xiě)入數(shù)據(jù)的類(lèi)型決定了數(shù)組的類(lèi)型。
#?默認(rèn)檢測(cè)指定為?long?類(lèi)型
POST?my-index-1230-01/_doc
{
??"horry":?[
????11111,
????22222,
????33333
??]
}
如上,寫(xiě)入的是 long 類(lèi)型的數(shù)組。long 咋來(lái)的,動(dòng)態(tài)類(lèi)型匹配 date_detection 得到的!
https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-mapping.html
3.2 不能獨(dú)立的檢索數(shù)組中的單個(gè)字段
#?數(shù)組數(shù)據(jù)會(huì)作為一個(gè)整體被召回
GET?my-index-1230/_search
{
??"query":?{
????"match":?{
??????"horry":??"basketball"
????}
??}
}
如果非要實(shí)現(xiàn)單獨(dú)召回?cái)?shù)據(jù),需要借助:nested 嵌套類(lèi)型實(shí)現(xiàn)。
4、Elasticsearch 如何獲取數(shù)組中第i號(hào)位置的值?
舉例,僅檢索召回 “basketball”,需要借助 script_field 或者 runtime field 實(shí)現(xiàn)。
實(shí)現(xiàn)參考如下:
POST?my-index-1230/_doc
{
??"horry":?[
????"all",
????"pingpang",
????"basketball",
????"football"
??]
}
GET?my-index-1230/_search
注意:存儲(chǔ)數(shù)組數(shù)據(jù)順序是:
| 序號(hào) | 值 |
|---|---|
| 0 | all |
| 1 | pingpang |
| 2 | basketball |
| 3 | football |
GET?my-index-1203/_search
{
??"query":?{
????"match_all":?{}
??},
??"script_fields":?{
????"test1":?{
??????"script":?{
????????"lang":?"painless",
????????"source":?"doc['horry'][2]"
??????}
????}
??}
}
如上示例中的2,可以換成:0,1,2,3。
召回結(jié)果如下:
| 序號(hào) | 值 |
|---|---|
| 0 | all |
| 1 | basketball |
| 2 | football |
| 3 | pingpang |
后臺(tái)按照字母順序做了處理,返回結(jié)果數(shù)據(jù)。
也就是說(shuō):我們以腳本的方式無(wú)法精準(zhǔn)獲取對(duì)應(yīng)位次上的數(shù)據(jù)。
這塊目前看,沒(méi)有最優(yōu)的獲取方式。如果大家有,歡迎留言交流。
4、Elasticsearch 如何獲取數(shù)組最后一個(gè)元素的值?
通過(guò) ingest pipeline 預(yù)處理方式實(shí)現(xiàn)如下:
DELETE?my-index-123001
POST?my-index-123001/_doc
{
??"horry":?[
????"pingpang",
????"basketball",
????"football"
??]
}
PUT?_ingest/pipeline/my_pipeline
{
??"processors":?[
????{
??????"script":?{
????????"source":?"""
?????????????if(ctx.horry!=null)
?????????????{
???????????????List?list?=?ctx.horry;?
??????????????List?ListOfLast?=?list.subList(list.size()?-?1,?list.size());?
??????????????String?lastval=ListOfLast.toString();?
??????????????lastval=lastval.substring(1,?lastval.length()?-?1);
??????????????ctx.lastval=lastval;
?????????????}
??????????"""
??????}
????}
??]
}
POST?my-index-123001/_update_by_query?pipeline=my_pipeline
{
??"query":?{
????"match_all":?{}
??}
}
GET?my-index-123001/_search
中間核心腳本不是最優(yōu)實(shí)現(xiàn)方式,歡迎大家留言反饋精簡(jiǎn)處理方式。
第一步:數(shù)組轉(zhuǎn) list; 第二步:取 list 最后一個(gè)元素值,結(jié)果仍然為 list; 第三步:list 轉(zhuǎn) string; 第四步:string 取核心元素,去頭、去尾。 第五步:將中間結(jié)果賦值給新字段。
返回結(jié)果如下:
{
??????"_index"?:?"my-index-123001",
??????"_type"?:?"_doc",
??????"_id"?:?"RxLlC34BuPkjCUZU5-XW",
??????"_score"?:?1.0,
??????"_source"?:?{
????????"lastval"?:?"football",
????????"horry"?:?[
??????????"pingpang",
??????????"basketball",
??????????"football"
????????]
??????}
????}5、小結(jié)
Elasticsearch 數(shù)組選型需要結(jié)合業(yè)務(wù)場(chǎng)景需要。
數(shù)據(jù)相關(guān)的問(wèn)題還有很多很多,比如:
數(shù)組高亮問(wèn)題
數(shù)組聚合問(wèn)題
數(shù)組召回?cái)?shù)據(jù)問(wèn)題?
.....
大家在選型或者實(shí)踐的過(guò)程中,如果涉及大量腳本的時(shí)候,要多考慮能否通過(guò)寫(xiě)入前 ingest 預(yù)處理方式。本質(zhì)是:以空間換時(shí)間,最大化提升檢索效率。
歡迎大家就數(shù)組問(wèn)題留言討論。
推薦
更短時(shí)間更快習(xí)得更多干貨!
已帶領(lǐng)81位球友通過(guò) Elastic 官方認(rèn)證!

