Elasticsearch8.1中的Script使用實(shí)例深入解讀

一、什么是 Elasticsearch Script?
Elasticsearch 中的 Script 是一種靈活的方式,允許用戶在查詢、聚合和更新文檔時(shí)執(zhí)行自定義的腳本。這些腳本可以用來動(dòng)態(tài)計(jì)算字段值、修改查詢行為、執(zhí)行復(fù)雜的條件邏輯等等。
二、支持的腳本語言有哪些
支持多種腳本語言,包括 Painless、Expression、Mustache、Java等,其中默認(rèn)的是Painless。

三、Painless 腳本的使用
Painless 是一種專為 Elasticsearch 設(shè)計(jì)的腳本語言,具有安全、快速、簡(jiǎn)單的特點(diǎn),使其在 Elasticsearch 中非常方便入門。
- 安全性: Painless 被設(shè)計(jì)為一種安全的腳本語言。它采取了一系列的安全措施,如禁止無限循環(huán)、禁止訪問 Java 類庫中的危險(xiǎn)類等,以減輕潛在的安全風(fēng)險(xiǎn)。
- 高性能: Painless 是為高性能而設(shè)計(jì)的,特別是在 Elasticsearch 中。它經(jīng)過了優(yōu)化,可以在大規(guī)模數(shù)據(jù)集上快速執(zhí)行。
- 易學(xué)易用: Painless 實(shí)現(xiàn)了任何具有基本編碼經(jīng)驗(yàn)的人都自然熟悉的語法。Painless 使用 Java 語法的子集,并進(jìn)行了一些額外的改進(jìn),以增強(qiáng)可讀性并刪除樣板文件。
- 無需編譯: Painless 腳本不需要預(yù)先編譯。它可以在運(yùn)行時(shí)解釋,所以我們可以動(dòng)態(tài)調(diào)整腳本而無需重新編譯整個(gè)應(yīng)用程序。
- 支持參數(shù)化: Painless 允許在腳本中使用參數(shù),這可以使腳本更通用,適用于多種情況。參數(shù)化腳本可以接受外部傳遞的值,從而在不修改腳本的情況下改變其行為。
- 支持多種數(shù)據(jù)類型: Painless 支持多種數(shù)據(jù)類型,包括數(shù)字、字符串、日期、布爾值等。
- 集成性: Painless 被緊密集成到 Java 中,可以用于查詢、聚合、腳本字段、腳本排序等各種用例。
3.1、編寫我們的第一個(gè)腳本
使用的Elasticsearch 版本為 8.1,歷史文章除非特別說明,最近更文的 ES版本都為 Elasticsearch 的 8.1 版本
腳本的組成有三個(gè)參數(shù),只要是在 Elasticsearch API 支持腳本的地方,都可以使用如下三個(gè)參數(shù)來使用腳本。
"script": {
"lang": "...",
"source" | "id": "...",
"params": { ... }
}lang:執(zhí)行腳本語言類型,默認(rèn)painlesssource,id:腳本的源碼本身,或者提前存儲(chǔ)的腳本IDparams:作為變量傳遞給腳本的參數(shù)
下面我們將通過實(shí)際的例子來進(jìn)行說明
3.2、在檢索中使用腳本
首先我們先往索引中插入一篇文檔
PUT zfc-doc-000007/_doc/1
{
"sum": 5,
"message":"test painless"
}使用腳本實(shí)現(xiàn) sum的值 乘2,此處使用變量 multiplier,在腳本的參數(shù)中指定參數(shù)值為2,其中doc['sum'].value * params['multiplier']的意思就是獲取文檔中sum的值并乘以腳本中 multiplier 的值
GET zfc-doc-000007/_search
{
"script_fields": {
"my_doubled_field": {
"script": {
"source": "doc['sum'].value * params['multiplier']",
"params": {
"multiplier": 2
}
}
}
}
}在獲取腳本的參數(shù)中的變量值除了使用params['參數(shù)名']這種方式之外,還可以使用params.get('multiplier')方法獲取
GET zfc-doc-000007/_search
{
"script_fields": {
"my_doubled_field": {
"script": {
"lang": "painless",
"source": "doc['sum'].value * params.get('multiplier');",
"params": {
"multiplier": 2
}
}
}
}
}上面我們是在檢索請(qǐng)求中使用的腳本字段來使用的腳本,下面我們先內(nèi)置一個(gè)腳本,通過使用腳本ID來使用內(nèi)置的腳本
3.3、使用內(nèi)置的腳本
創(chuàng)建一個(gè)腳本calculate-score,它可以使用Math.log(_score * 2) + params['my_modifier']修改分?jǐn)?shù)值
POST _scripts/calculate-score
{
"script": {
"lang": "painless",
"source": "Math.log(_score * 2) + params['my_modifier']"
}
}創(chuàng)建完成的腳本我們可以使用_scriptAPI查看腳本的內(nèi)容
GET _scripts/calculate-score
在檢索中只需要如下指定腳本的ID即可進(jìn)行檢索時(shí)使用
GET zfc-doc-000007/_search
{
"query": {
"script_score": {
"query": {
"match": {
"message": "painless"
}
},
"script": {
"id": "calculate-score",
"params": {
"my_modifier": 2
}
}
}
}
}如果想刪除腳本只需要調(diào)用DELETE 即可
DELETE _scripts/calculate-score
下面我們?cè)賮硌菔疽幌氯绾问褂媚_本更新文檔中的內(nèi)容
3.4、使用腳本操作文檔
先添加一個(gè)文檔來進(jìn)行測(cè)試
PUT zfc-doc-000007/_doc/1
{
"counter" : 1,
"tags" : ["red"]
}使用腳本對(duì)文檔中的 counter 的值與腳本中的 count 值進(jìn)行相加
POST zfc-doc-000007/_update/1
{
"script" : {
"source": "ctx._source.counter += params.count",
"lang": "painless",
"params" : {
"count" : 4
}
}
}我們還可以對(duì)文檔中的數(shù)組類型的tags字段進(jìn)行增加子對(duì)象,比如增加一個(gè)blue
POST zfc-doc-000007/_update/1
{
"script": {
"source": "ctx._source.tags.add(params['tag'])",
"lang": "painless",
"params": {
"tag": "blue"
}
}
}使用腳本對(duì)文檔中的 tags 的值進(jìn)行刪除,條件就是當(dāng) tag 的值與腳本中的值相等時(shí)刪除。如下為當(dāng) tags 的值為blue時(shí),刪除blue
POST zfc-doc-000007/_update/1
{
"script": {
"source": "if (ctx._source.tags.contains(params['tag'])) { ctx._source.tags.remove(ctx._source.tags.indexOf(params['tag'])) }",
"lang": "painless",
"params": {
"tag": "blue"
}
}
}上面只是對(duì)已有字段的增加刪除修改,下面還可以使用腳本進(jìn)行新字段的增加,比如增加一個(gè)字段new_field,值是value_of_new_field
POST zfc-doc-000007/_update/1
{
"script" : "ctx._source.new_field = 'value_of_new_field'"
}上面是字段的增加,下面就是字段的移除
POST zfc-doc-000007/_update/1
{
"script" : "ctx._source.remove('new_field')"
}除了對(duì)字段的刪除,數(shù)組對(duì)象內(nèi)部值的刪除,還可以對(duì)文檔進(jìn)行刪除。如下,當(dāng) tags 里面包含 blue 時(shí),刪除當(dāng)前文檔
POST zfc-doc-000007/_update/1
{
"script": {
"source": "if (ctx._source.tags.contains(params['tag'])) { ctx.op = 'delete' } else { ctx.op = 'none' }",
"lang": "painless",
"params": {
"tag": "blue"
}
}
}3.5、使用腳本解析日志信息
所謂的解析字符串,只是一組固定格式的字符串,提前使用變量的形式編譯,在插入文檔時(shí),通過腳本進(jìn)行解析保存,方便后面的檢索等請(qǐng)求
假如我們有如下數(shù)據(jù)
"message" : "247.37.0.0 - - [30/Apr/2020:14:31:22 -0500] \"GET /images/hm_nbg.jpg HTTP/1.0\" 304 0"
那么我們可以使用如下變量的形式解析該字符串
%{clientip} %{ident} %{auth} [%{@timestamp}] \"%{verb} %{request} HTTP/%{httpversion}\" %{status} %{size}下面我們使用例子來說明腳本解析字符串之后是何種形式的存在
創(chuàng)建一個(gè)索引保存解析的數(shù)據(jù)
PUT zfc-doc-000008
{
"mappings": {
"properties": {
"message": {
"type": "wildcard"
}
}
}
}內(nèi)置一個(gè)腳本,實(shí)現(xiàn)解析字符串信息,并提取需要的信息,如下為提取當(dāng)前日志中的 http 響應(yīng)信息response,對(duì)于如下腳本的測(cè)試API使用詳情可以參考官網(wǎng)
https://www.elastic.co/guide/en/elasticsearch/painless/8.1/pa...
POST /_scripts/painless/_execute
{
"script": {
"source": """
String response=dissect('%{clientip} %{ident} %{auth} [%{@timestamp}] "%{verb} %{request} HTTP/%{httpversion}" %{response} %{size}').extract(doc["message"].value)?.response;
if (response != null) emit(Integer.parseInt(response));
"""
},
"context": "long_field",
"context_setup": {
"index": "zfc-doc-000008",
"document": {
"message": """247.37.0.0 - - [30/Apr/2020:14:31:22 -0500] "GET /images/hm_nbg.jpg HTTP/1.0" 304 0"""
}
}
}如果我們還想操作當(dāng)前解析的數(shù)據(jù)我們可以使用運(yùn)行時(shí)字段,因?yàn)檫\(yùn)行時(shí)字段不需要進(jìn)行索引會(huì)更加的靈活,可以很方便的修改腳本及運(yùn)行方式。
那么我們現(xiàn)在刪除一下剛剛創(chuàng)建的索引,重新添加一下,創(chuàng)建語句如下
DELETE zfc-doc-000008
PUT /zfc-doc-000008
{
"mappings": {
"properties": {
"@timestamp": {
"format": "strict_date_optional_time||epoch_second",
"type": "date"
},
"message": {
"type": "wildcard"
}
}
}
}添加一個(gè)運(yùn)行時(shí)字段來保存解析的結(jié)果
PUT zfc-doc-000008/_mappings
{
"runtime": {
"http.response": {
"type": "long",
"script": """
String response=dissect('%{clientip} %{ident} %{auth} [%{@timestamp}] "%{verb} %{request} HTTP/%{httpversion}" %{response} %{size}').extract(doc["message"].value)?.response;
if (response != null) emit(Integer.parseInt(response));
"""
}
}
}添加幾條測(cè)試數(shù)據(jù)用于測(cè)試
POST /zfc-doc-000008/_bulk?refresh=true
{"index":{}}
{"timestamp":"2020-04-30T14:30:17-05:00","message":"40.135.0.0 - - [30/Apr/2020:14:30:17 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
{"index":{}}
{"timestamp":"2020-04-30T14:30:53-05:00","message":"232.0.0.0 - - [30/Apr/2020:14:30:53 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:12-05:00","message":"26.1.0.0 - - [30/Apr/2020:14:31:12 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:19-05:00","message":"247.37.0.0 - - [30/Apr/2020:14:31:19 -0500] \"GET /french/splash_inet.html HTTP/1.0\" 200 3781"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:22-05:00","message":"247.37.0.0 - - [30/Apr/2020:14:31:22 -0500] \"GET /images/hm_nbg.jpg HTTP/1.0\" 304 0"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:27-05:00","message":"252.0.0.0 - - [30/Apr/2020:14:31:27 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:28-05:00","message":"not a valid apache log"}下面我們進(jìn)行運(yùn)行時(shí)字段檢索響應(yīng)為304的數(shù)據(jù)
GET zfc-doc-000008/_search
{
"query": {
"match": {
"http.response": "304"
}
},
"fields" : ["http.response"]
}剛才是屬于提前內(nèi)置好運(yùn)行時(shí)字段,我們也可以直接在檢索時(shí)指定運(yùn)行時(shí)字段來使用,但下面所示的僅在運(yùn)行時(shí)有效。如下所示
GET zfc-doc-000008/_search
{
"runtime_mappings": {
"http.response": {
"type": "long",
"script": """
String response=dissect('%{clientip} %{ident} %{auth} [%{@timestamp}] "%{verb} %{request} HTTP/%{httpversion}" %{response} %{size}').extract(doc["message"].value)?.response;
if (response != null) emit(Integer.parseInt(response));
"""
}
},
"query": {
"match": {
"http.response": "304"
}
},
"fields" : ["http.response"]
}我們也可以根據(jù)特定的值進(jìn)行拆分,獲取所需要的信息
3.6、使用腳本解析 GC 信息
例如如下 Elasticsearch 的 GC 信息
[2021-04-27T16:16:34.699+0000][82460][gc,heap,exit] class space used 266K, capacity 384K, committed 384K, reserved 1048576K
下面我們根據(jù) GC 信息編寫一個(gè)解析模式
[%{@timestamp}][%[code]][%{desc}] %{ident} used %{usize}, capacity %{csize}, committed %{comsize}, reserved %{rsize}然后在檢索時(shí)就可以使用如下語句來提交信息到運(yùn)行時(shí)字段,首先添加測(cè)試數(shù)據(jù),注意索引名稱已經(jīng)更換,解析模式不匹配會(huì)報(bào)錯(cuò)
POST /zfc-doc-000010/_bulk?refresh
{"index":{}}
{"gc": "[2021-04-27T16:16:34.699+0000][82460][gc,heap,exit] class space used 266K, capacity 384K, committed 384K, reserved 1048576K"}
{"index":{}}
{"gc": "[2021-03-24T20:27:24.184+0000][90239][gc,heap,exit] class space used 15255K, capacity 16726K, committed 16844K, reserved 1048576K"}
{"index":{}}
{"gc": "[2021-03-24T20:27:24.184+0000][90239][gc,heap,exit] Metaspace used 115409K, capacity 119541K, committed 120248K, reserved 1153024K"}
{"index":{}}
{"gc": "[2021-04-19T15:03:21.735+0000][84408][gc,heap,exit] class space used 14503K, capacity 15894K, committed 15948K, reserved 1048576K"}
{"index":{}}
{"gc": "[2021-04-19T15:03:21.735+0000][84408][gc,heap,exit] Metaspace used 107719K, capacity 111775K, committed 112724K, reserved 1146880K"}
{"index":{}}
{"gc": "[2021-04-27T16:16:34.699+0000][82460][gc,heap,exit] class space used 266K, capacity 367K, committed 384K, reserved 1048576K"}使用檢索語句展示解析數(shù)據(jù)到運(yùn)行時(shí)字段中
GET zfc-doc-000010/_search
{
"runtime_mappings": {
"gc_size": {
"type": "keyword",
"script": """
Map gc=dissect('[%{@timestamp}][%[code]][%{desc}] %{ident} used %{usize}, capacity %{csize}, committed %{comsize}, reserved %{rsize}').extract(doc["gc.keyword"].value);
if (gc != null) emit("used" + ' ' + gc.usize + ', ' + "capacity" + ' ' + gc.csize + ', ' + "committed" + ' ' + gc.comsize);
"""
}
},
"size": 1,
"aggs": {
"sizes": {
"terms": {
"field": "gc_size",
"size": 10
}
}
},
"fields" : ["gc_size"]
}通過上面的查詢測(cè)試可以知道,Elasticsearch 中的 script 默認(rèn)的時(shí) painless 語言,功能已經(jīng)非常強(qiáng)大可以滿足我們的日常需求,如果還想更高級(jí)的腳本,可以使用 Java 語言來編寫自己的腳本。關(guān)于 Expressions 的表達(dá)式的使用就參與官網(wǎng)吧,本文的所有例子均來自官網(wǎng),并自測(cè)完成。如有錯(cuò)誤歡迎指出,共同進(jìn)步。
以上就是Elasticsearch8.1中的Script使用實(shí)例深入解讀的詳細(xì)內(nèi)容,更多關(guān)于Elasticsearch8.1 Script的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Post請(qǐng)求參數(shù)是數(shù)組或者List時(shí)的請(qǐng)求處理方式
這篇文章主要介紹了Post請(qǐng)求參數(shù)是數(shù)組或者List時(shí)的請(qǐng)求處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05
SpringBoot項(xiàng)目導(dǎo)出jar包及瘦身部署方式
今天項(xiàng)目要求Nginx+jar包運(yùn)行多個(gè)項(xiàng)目,在此記錄一下部署的過程,其中借鑒了好多網(wǎng)上前輩的經(jīng)驗(yàn),感謝各位的無私分享2024-07-07
詳解Springboot2.3集成Spring security 框架(原生集成)
這篇文章主要介紹了詳解Springboot2.3集成Spring security 框架(原生集成),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
idea創(chuàng)建javaweb原生項(xiàng)目的實(shí)現(xiàn)示例
這篇文章主要介紹了idea創(chuàng)建javaweb原生項(xiàng)目的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
劍指Offer之Java算法習(xí)題精講數(shù)組與字符串題
跟著思路走,之后從簡(jiǎn)單題入手,反復(fù)去看,做過之后可能會(huì)忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會(huì)發(fā)現(xiàn)質(zhì)的變化2022-03-03
HashMap紅黑樹入門(實(shí)現(xiàn)一個(gè)簡(jiǎn)單的紅黑樹)
紅黑樹(Red Black Tree) 是一種自平衡二叉查找樹,是在計(jì)算機(jī)科學(xué)中用到的一種數(shù)據(jù)結(jié)構(gòu),典型的用途是實(shí)現(xiàn)關(guān)聯(lián)數(shù)組。 紅黑樹發(fā)明時(shí)被稱為平衡二叉B樹,后來修改為如今的“紅黑樹”2021-06-06
mybatis注解開發(fā) 一對(duì)多嵌套查詢方式
這篇文章主要介紹了mybatis注解開發(fā) 一對(duì)多嵌套查詢方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2023-03-03

