使用场景
用于解决复杂业务问题,如:自定义字段、自定义评分、自定义更新、自定义聚合分析等
缺点
性能问题。官方文档性能优化中明确指出使用脚本会导致性能低;
如非必要,不要使用脚本,尽量用其他方式替换,如下:
使用脚本进行前缀查询:
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前缀匹配,性能提升5倍
script模板
"script": {
"lang": "...",
"source" | "id": "...",
"params": { ... }
}
lang:代表language脚本语言,默认指定为:painless。
source:脚本的核心部分,id应用于:stored script。
params:传递给脚本使用的变量参数。
Painless Scripting 简介
Painless是一种简单,安全的脚本语言,专为与Elasticsearch一起使用而设计。从ES5.0开始,它是Elasticsearch的默认脚本语言,可以安全地用于内联和存储脚本。
Painless特点:
性能牛逼:Painless脚本运行速度比备选方案(包括Groovy)快几倍。
安全性强:使用白名单来限制函数与字段的访问,避免了可能的安全隐患。
可选输入:变量和参数可以使用显式类型或动态def类型。
上手容易:扩展了java的基本语法,并兼容groove风格的脚本语言特性。
特定优化:是ES官方专为Elasticsearch脚本编写而设计。
存储脚本
脚本可以使用_scripts端点存储在集群状态中并从集群状态检索。
下面是使用位于/_scripts/{id}的存储脚本的示例。
首先,在集群状态下创建名为calculate-score的脚本:
POST _scripts/calculate-score
{
"script": {
"lang": "painless",
"source": "Math.log(_score * 2) + params.my_modifier"
}
}
同样的获取脚本可以用:
GET _scripts/calculate-score
存储的脚本可以通过如下方式指定id参数来使用:
GET _search
{
"query": {
"script": {
"script": {
"id": "calculate-score",
"params": {
"my_modifier": 2
}
}
}
}
}
删除脚本
DELETE _scripts/calculate-score
案例
自定义字段
返回原有Mapping未定义的字段值。
1.1、以idplus返回id字段的翻倍后的结果。
GET index6/_search
{
"script_fields": {
"idplus": {
"script": {
"lang": "expression",
"source": "doc['id'] * multiplier",
"params": {
"multiplier": 2
}
}
}
}
}
结果
{
省略...
"hits" : [
{
"_index" : "index6",
"_type" : "_doc",
"_id" : "2001",
"_score" : 1.0,
"fields" : {
"idplus" : [
4002.0
]
}
},
省略...
]
}
1.2、返回日期字段中的“年”或“月”或“日”等。
1GET hockey/_search
2{
3 "script_fields": {
4 "birth_year": {
5 "script": {
6 "source": "doc.born.value.year"
7 }
8 }
9 }
10}
自定义评分
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}
自定义更新
1.1、_update:将已有字段值赋值给其他字段。
POST index6/_update/1
{
"script": {
"lang": "painless",
"source": """
ctx._source.firstName = params.firstName;
ctx._source.lastName = params.lastName
""",
"params": {
"firstName": "aa",
"lastName": "bb"
}
}
}
1.2、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}
自定义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}
自定义聚合
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}