干货 | Elasticsearch7.X Scripting脚本使用详解
0、題記
除了官方文檔,其他能找到的介紹Elasticsearch腳本(Scripting)的資料少之又少。
一方面:性能問題。
官方文檔性能優化中明確指出使用腳本會導致性能低;
另一方面:使用場景相對少。
非復雜業務場景下,基礎的增、刪、改、查基本上就能搞定。
但,不能否認,在解決復雜業務問題(如:自定義評分、自定義文本相關度、自定義過濾、自定義聚合分析)時,腳本依然是Elasticsearch強悍的利器之一。
本文在官方文檔基礎上,結合實際業務場景,在Elasticsearch7.3環境下進行腳本使用解讀。
1、官方scripting使用建議
Avoid scripts——In general, scripts should be avoided.
If they are absolutely needed, you should prefer the painless and expressions engines.
ebay在性能優化實踐中也強調(本文做了擴展延伸):
避免使用腳本查詢(script query)計算動態字段。
例如:我們有一個包含大量劇院信息的索引,我們需要查詢以"Down"開頭的所有劇院。你可能運行一個如下腳本查詢:
1POST?seats/_search
2{
3?"query":?{
4??"bool":{
5????"filter":?{?
6????"script":{
7??????"script":{
8????????"lang":"painless",
9????????"source":?"doc['theatre'].value.startsWith('Down')"
10??????}
11????}
12????}
13??}
14?}
15}
這個查詢非常耗費資源,并且減慢整個系統。
解決方案:
方案一:prefix前綴匹配;實測性能:prefix較scripting性能提升5倍。
方案二:索引時考慮添加一個名為“theatre_prefix”的keyword類型字段。然后我們可以查詢"theatre_prefix":"Down"。
2、ES Scripting歷史
| 版本 | 使用腳本 |
< Elasticsearch 1.4 | MVEL 腳本 |
< Elasticsearch 5.0 | Groovy 腳本 |
‘>= Elasticsearch 5.0 | painless 腳本 |
Groovy 的出現是解決MVEL的安全隱患問題;
但Groovy仍存在內存泄露+安全漏洞問題,
painless腳本的官宣時間:2016年9月21日。看似很新,截止目前,已經三年左右時間了。
正如其名字:無痛。painless的出現是為了用戶更方便、高效的使用腳本。
https://www.elastic.co/cn/blog/painless-a-new-scripting-language
3、Painless Scripting 簡介
Painless是一種簡單,安全的腳本語言,專為與Elasticsearch一起使用而設計。它是Elasticsearch的默認腳本語言,可以安全地用于內聯和存儲腳本。
Painless特點:
性能牛逼:Painless腳本運行速度比備選方案(包括Groovy)快幾倍。
安全性強:使用白名單來限制函數與字段的訪問,避免了可能的安全隱患。
可選輸入:變量和參數可以使用顯式類型或動態def類型。
上手容易:擴展了java的基本語法,并兼容groove風格的腳本語言特性。
特定優化:是ES官方專為Elasticsearch腳本編寫而設計。
4、Scripting 應用場景
認知前提:
增刪改查能解決業務場景80%的問題,Painless腳本操作一般應用于相對復雜的業務場景中。
常見場景舉例如下:
自定義字段
自定義評分
自定義更新
自定義reindex
聚合
其他自定義操作
5、Scripting 使用模板
心中有模板,腳本認知就有了“套路”。
1"script":?{
2????"lang":???"...",??
3????"source"?|?"id":?"...",?
4????"params":?{?...?}?
5??}
lang:代表language腳本語言,默認指定為:painless。
source:腳本的核心部分,id應用于:stored script。
params:傳遞給腳本使用的變量參數。
6、Scripting 實戰
6.1 自定義字段
舉例:返回原有Mapping未定義的字段值。
如:以my_doubled_field返回my_field字段的翻倍后的結果。
1GET?my_index/_search
2{
3??"script_fields":?{
4????"my_doubled_field":?{
5??????"script":?{
6????????"lang":???"expression",
7????????"source":?"doc['my_field']?*?multiplier",
8????????"params":?{
9??????????"multiplier":?2
10????????}
11??????}
12????}
13??}
14}
注意:這里腳本語言選擇的expression,下一節講解。
如:返回日期字段中的“年”或“月”或“日”等。
1GET?hockey/_search
2{
3??"script_fields":?{
4????"birth_year":?{
5??????"script":?{
6????????"source":?"doc.born.value.year"
7??????}
8????}
9??}
10}
6.2 自定義評分
1GET?my_index/_search
2{
3??"query":?{
4????"function_score":?{
5??????"query":?{
6????????"match":?{
7??????????"text":?"quick?brown?fox"
8????????}
9??????},
10??????"script_score":?{
11????????"script":?{
12??????????"lang":?"expression",
13??????????"source":?"_score?*?doc['popularity']"
14????????}
15??????}
16????}
17??}
18}
6.3 自定義更新
Update:將已有字段值賦值給其他字段。
1POST?hockey/_update/1
2{
3??"script":?{
4????"lang":?"painless",
5????"source":?"""
6??????ctx._source.last?=?params.last;
7??????ctx._source.nick?=?params.nick
8????""",
9????"params":?{
10??????"last":?"gaudreau",
11??????"nick":?"hockey"
12????}
13??}
14}
Update_by_query:滿足b開頭(注意正則)的字段,末尾添加matched。
1POST?hockey/_update_by_query
2{
3??"script":?{
4????"lang":?"painless",
5????"source":?"""
6??????if?(ctx._source.last?=~?/b/)?{
7????????ctx._source.last?+=?"matched";
8??????}?else?{
9????????ctx.op?=?"noop";
10??????}
11????"""
12??}
13}
6.4 自定義reindex
Elasticsearch認證考試題:
有index_a包含一些文檔, 要求創建索引index_b,通過reindex api將index_a的文檔索引到index_b。
要求:
1)增加一個整形字段,value是index_a的field_x的字符長度;
2)再增加一個數組類型的字段,value是field_y的詞集合。
(field_y是空格分割的一組詞,比方"foo bar",索引到index_b后,要求變成["foo", "bar"])
1POST?_reindex
2{
3??"conflicts":?"proceed",
4??"source":?{
5????"index":?"index_a"
6??},
7??"dest":?{
8????"index":?"index_b"
9??},
10??"script":?{
11????"source":?"ctx._source.parts?=?/?/.split(ctx._source.address);?ctx._source.tag?=?ctx._source.city.length();"
12??}
13}
語法參考:
https://www.elastic.co/guide/en/elasticsearch/painless/7.3/painless-regexes.html
6.5 聚合
1GET?/_search
2{
3????"aggs"?:?{
4????????"genres"?:?{
5????????????"terms"?:?{
6????????????????"script"?:?{
7????????????????????"source":?"doc['genre'].value",
8????????????????????"lang":?"painless"
9????????????????}
10????????????}
11????????}
12????}
13
14}
6.6 其他自定義操作
需要結合業務去實踐。
7、常見坑及問題
7.1 腳本只有Painless嗎?
顯然不是,第6節用到的expression 是Lucene’s expressions 腳本語言。
還可以基于腳本引擎自己開發插件實現,
https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting-engine.html
7.2 怎么界定是expressions 還是Painless?
"lang": "painless",
"lang": "expressions ",
是唯一區分。
7.3 使用painless就百分之百“無痛”,無漏洞后顧之憂了嗎?
凡事不能絕對。
核心注意點:
第一:不要root賬戶下運行Elasticsearch。
第二:不要公開ES路徑給其他用戶。
第三:不要公開ES路徑到互聯網。
實戰推薦:
1、用戶在搜索框中鍵入文本,文本將直接發送到后臺的match、match_phrase、Simple query string或 Suggesters.
2、作為應用程序開發過程的一部分(而非全部)開放上述查詢的腳本。
3、使用用戶提供的參數運行腳本。
4、文檔固定的Mapping結構。
不推薦:
1、用戶可以編寫任意scripts, queries(檢索), _search requests(search請求)。
2、文檔結構可以用戶自定義。
8、小結
本文講解了腳本的發展歷史、使用場景、應用實戰,但相比于實際業務的復雜需求仍然是九牛一毛。
實戰中,肯定還會遇到這樣、那樣的問題。
一方面:歡迎留言交流。
另一方面:多研讀官方文檔,很多細節值得深究。
參考:
https://www.elastic.co/guide/en/elasticsearch/reference/current/tune-for-search-speed.html
https://www.infoq.cn/article/elasticsearch-performance-tuning-practice-at-ebay
https://github.com/laoyang360/deep_elasticsearch/blob/master/es_dsl_study/6.scripting.md
https://github.com/elastic/elasticsearch/issues/19396
https://www.youtube.com/watch?v=3FLEJJ8PsM4
https://blog.csdn.net/u013613428/article/details/78134170
推薦閱讀:
重磅 | Elasticsearch7.X學習路線圖
Elasticsearch性能優化實戰指南
Elasticsearch解決問題之道——請亮出你的DSL!
更短時間更快習得更多干貨!
總結
以上是生活随笔為你收集整理的干货 | Elasticsearch7.X Scripting脚本使用详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 佳能400d功能介绍
- 下一篇: Memcached----2-3