Painless script在ElasticSearch 中的使用的一些問題
最近在準(zhǔn)備ElasticSearch認證工程師的考試,其中Script是比較重要西潘,也是比較難的部分卷玉。在這里做一下總結(jié)的,主要是自己在做真題時不確定的一些東西喷市。
使用場景
Script使用場景比較多相种,在做真題的過程中,我遇到過下面一些場景:
- Ingress Pipeline中使用品姓。
- reindex索引時使用寝并。
- query時使用。
- 使用script產(chǎn)生一個新的field腹备,加入到搜索出的文檔中衬潦。
- 作為filter的內(nèi)嵌函數(shù)。對結(jié)果進行過濾植酥。
- 在function_score中使用镀岛,取代算分。
- search template中使用友驮。
- update/update_by_query中使用漂羊。(7.x版本似乎這個功能有bug,做父子關(guān)聯(lián)更新時卸留,提示父子文檔需要在同一個分片上走越,但是一共只有一個分片)
- 排序中使用。產(chǎn)生一個metric作為排序指標(biāo)耻瑟。
- 在filter中使用旨指。
官網(wǎng)上還有一些其他的使用場景赏酥,比如在聚合中使用script,似乎一直沒有在真題中見過谆构。
Painless腳本語言
本文的主角painless腳本裸扶,是Java語言的一個子集,很多地方的用法和Java是類似的低淡,但是也有一些不一樣的地方姓言。之前使用的時候,我一直當(dāng)做Java在寫蔗蹋,其實踩了不少坑。painless的主要API其實是在[painless API reference] > [Shard API]里面囱淋,官方文檔的位置在https://www.elastic.co/guide/en/elasticsearch/painless/7.2/painless-api-reference-shared.html猪杭。這個位置我也是寫本文的時候才發(fā)現(xiàn)的⊥滓拢看了這部分文檔皂吮,揭開了之前的一些疑惑。
比如說税手,把一個字符串按照空格分割成一個字符串?dāng)?shù)組蜂筹,我之前使用java的split API來進行分解,ES報錯找不到相關(guān)接口芦倒。后來在文檔中發(fā)現(xiàn)painless的相關(guān)API是String[] splitOnToken(String)
艺挪。當(dāng)然,正是因為這個兵扬,我有機會接觸到了ingest pipeline中的grok以及splite processor麻裳。
其他比較多的一些用法有:
- 訪問某個field
在painless里面,文檔的field數(shù)據(jù)要么直接存儲在ctx
中(ingest器钟,reindex)津坑,要么存儲在ctx
的成員ctx._source
中,這倆個對象傲霸,都是map類型的疆瑰,在painless中可以有兩種方法進行訪問。以ctx._source
為例昙啄,假設(shè)有一個field叫做apple穆役,我們可以使用ctx._source['apple']
或者ctx._source.apple
進行訪問。
- 判斷field是否存在
比如說跟衅,判斷apple字段是否存在孵睬,我可以用if (ctx._source.apple == null)
進行判斷。又因為ctx._source
是一個hashmap伶跷,我們也可以用map的相關(guān)API進行判斷:if (ctx._source.containKey('apple'))
掰读。
- 設(shè)置新field
上文曾經(jīng)描述秘狞,可以使用ctx['key_name']
和ctx.key_name
這種方法來對hashmap的字段進行訪問。這種方法統(tǒng)一可以用來設(shè)置新的值蹈集。
ctx['key_name'] = value;
ctx.key_name = value;
- String轉(zhuǎn)換成int
可以使用Integer的函數(shù)parseInt來進行轉(zhuǎn)換烁试。比如說:
String num = '123';
int num_int = Integer.parseInt(num);
其實這個用法和Java是一樣的,轉(zhuǎn)換成其他數(shù)字類型也是一樣的拢肆。
- 單值或者數(shù)組
這個場景其實我并沒有在實際的題目中看到减响,但是這個場景我覺得是非常可能出現(xiàn)的郭怪,因為ES有個特性支示,當(dāng)我們?yōu)槟硞€字段做映射時,我們可以指定其類型鄙才,比如說颂鸿,指定為text。在實際寫入文檔時攒庵,我們可以寫入一個值嘴纺,或者是多個值組成的數(shù)組。那么問題就來了浓冒,我怎樣在painless腳本里處理這樣的情況呢栽渴?我并沒有機會知道,我獲得的一個值是一個String還是一個ArrayList稳懒。那這種情況怎么處理呢闲擦?查看painless的關(guān)鍵字https://www.elastic.co/guide/en/elasticsearch/painless/7.2/painless-keywords.html,可以發(fā)現(xiàn)僚祷,和Java一樣佛致,painless有關(guān)鍵字instanceof
。我們可以使用instanceof來對類型進行判斷辙谜。
進行一個小實驗俺榆,假設(shè)有一個index,其包含一個字段tags装哆,tags可以是一個罐脊,也可以是多個。
PUT test1
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"tags": {
"type": "keyword"
}
}
}
}
POST test1/_bulk
{"index": {"_id": 1}}
{"tags": "cat"}
{"index": {"_id": 2}}
{"tags": ["cat", "dog"]}
{"index": {"_id": 3}}
{"tags": ["cat", "dog", "tiger"]}
我們要將索引test1重新導(dǎo)入到索引test2中蜕琴,并且增加一個字段將所有的tag拼接起來萍桌。tags字段現(xiàn)在可能是String,也可能是一個ArrayList凌简。因此我們再寫painless腳本時要注意識別tags的類型上炎。
PUT _ingest/pipeline/join_tags
{
"description": "join_tags",
"processors": [
{
"script": {
"lang": "painless",
"source": """
ctx.tags_str = "";
if (ctx.tags instanceof ArrayList)
{
for (String tag: ctx.tags)
{
if (ctx.tags_str.length() > 0)
{
ctx.tags_str += " " + tag;
}
else
{
ctx.tags_str = tag;
}
}
}
else
{
ctx.tags_str = ctx.tags
}
"""
}
}
]
}
好了,現(xiàn)在可以用_reindex來將test1導(dǎo)入到test2中了。
POST _reindex
{
"source": {
"index": "test1"
},
"dest": {
"index": "test2",
"pipeline": "join_tags"
}
}
順便提一句藕施,_reindex里面也可以直接寫script寇损,但是就像上面提到的,處理父子關(guān)系時會出問題裳食。
小結(jié)
上面就是我這幾天做題遇到的一些場景矛市,當(dāng)時做的時候,其實還遇到不少其他情況诲祸,不過現(xiàn)在記得不是很清楚了浊吏。等我后面想起來再做補充吧。