重生之我们在ES顶端相遇第 18 章 - Script 使用(进阶)
文章目录
- 0. 前言
 - 1. 基本使用
 - 2. 读请求中访问文档字段
 - 2.1 遍历 List
 - 2.2 判断对象存不存在
 - 2.3 判断值是否为空
 - 2.4 总结
 
- 3. 写请求中访问文档字段
 - 3.1 数字相加
 - 3.2 字符串相加
 - 3.3 将字符串转为数组
 
0. 前言
在前面部分,我们介绍了 ES 的基本使用和要掌握的基础性读写原理。
 从本章开始,会开始介绍 ES 进阶使用。
 本章要介绍的内容是 Script 的基本使用。
 在日常开发中,读请求很少会用,写请求会使用比较多。
 在读请求、写请求中,Script 访问文档值的方式有所不同,下面我将对 Script 在读写请求中使用进行介绍。
1. 基本使用
我们来看一个修改算分的例子。
写入文档
PUT test_18/_doc/1
{"counter": 1,"tags": ["red"],"scores": [1,2,3,4,5]
}
 
查询时,修改算分
GET test_18/_search
{"query": {"script_score": {"query": {"term": {"tags": "red"}},"script": {"lang": "painless","source": "Math.log(_score * 2) + params['my_modifier'] * doc['counter'].value","params": {"my_modifier": 2}}}}
}
 
script_score 是 ES 用于修改算分的查询,当然这不是我们的重点。
script 中包含 3 个属性
- lang: 默认为 
painless,非必填 - source: 脚本,必填
 - params: 传递给脚本的参数,没有定义参数时,不用填
 
可以看到,在 Script 中我们可以使用 JAVA Api, 可以访问自定义的参数, 可以访问 ES 文档字段值。
2. 读请求中访问文档字段
painless script language 其实跟 JAVA 语法大相径庭。你可以简单认为就是在写 JAVA 代码。
在读请求中,ES 将文档映射为名为 doc 的 map。可以通过 doc['字段名'] 访问对应字段的对象。
2.1 遍历 List
GET test_18/_search
{"query": {"script_score": {"query": {"match_all": {}},"script": {"lang": "painless","source": """int total = 0;for (int i = 0; i < doc['scores'].length; ++i) {total += doc['scores'][i];}return total;"""}}}
}
 
代码解释
doc['scores']获取到文档对象,即scores 字段.length是JAVA数组的方法doc['socere'][i]访问数组中具体的值
script 中不允许访问 text 字段
GET test_18/_search
{"query": {"function_score": {"script_score": {"script": {"lang": "painless","source": """int total = 0;for (int i = 0; i < doc['tags.keyword'].length; ++i) {total += doc['counter'].value;}return total;"""}}}}
}
 
代码解释
doc['tags.keyword'],拿到tags.keyword对象。需要特别注意,这里不能使用doc['tags']。因为 ES 在 script 中不允许访问 text 字段doc['counter'],获取 counter 对象。.value获取 counter 对象的值
2.2 判断对象存不存在
doc.containsKey['field']
GET test_18/_search
{"query": {"script_score": {"query": {"match_all": {}},"script": {"lang": "painless","source": """int total = 0;if (doc.containsKey('goals')) {return doc['goals'].value;} else {return (int)_score;}"""}}}
}
 
代码解释
doc.containsKey('goals'): 判断是否包含goalskey。之前我们说过的。文档会被映射为一个 map。因此 doc 具备 map 的函数。return (int)_score:_score在script_score中是一个特殊字段。(int) 是类型强转,和JAVA语法一致
2.3 判断值是否为空
doc['field'].size()
GET test_18/_search
{"query": {"script_score": {"query": {"match_all": {}},"script": {"lang": "painless","source": """int total = 0;if (doc['counter'].size() == 0) {return 3;} else {return 2;}"""}}}
}
 
代码解释
doc['counter'].size()获取counter对象长度
2.4 总结
- 读请求中,可以使用 
doc['字段名']访问对应字段的对象 - script 读请求中,无法访问 
text字段 painless语法与JAVA语法类似- 关于 
painless更多可阅读 ES painless 官网 painless中可以使用哪些 API,可以参考 painless 支持的 API 文档
3. 写请求中访问文档字段
在写请求中使用脚本,我们大部分情况下是结合 Ingest Pipeline 一起使用。
 在 Ingest pipeline 中,通过 ctx.xxx 就可以访问到文档字段。
注:Ingest pipeline:可以在文档写入前对文档进行预处理。
下面,我们来看几个例子
3.1 数字相加
将 math_score + verbal_score 赋值给 total_score
创建索引
PUT test_18_write_01
{"mappings": {"properties": {"math_score": {"type": "integer"},"verbal_score": {"type": "integer"},"total_score": {"type": "integer"}}}
}
 
创建 ingest pipeline
PUT _ingest/pipeline/test_18_write_01_pipeline
{"description": "Calculates the total test score","processors": [{"script": {"source": "ctx.total_score = (ctx.math_score + ctx.verbal_score)"}}]
}
 
写入时,指定 pipeline
PUT test_18_write_01/_doc/1?pipeline=test_18_write_01_pipeline
{"math_score": 99,"verbal_score": 89
}
 
查看结果
GET test_18_write_01/_search
 

已经为我们自动写入了 total_score
3.2 字符串相加
将 lastName、firstName 相加,赋值给 fullName
创建 ingest pipeline
PUT _ingest/pipeline/test_18_write_02_pipeline
{"description": "String concatenation test","processors": [{"script": {"source": """if (ctx.containsKey('lastName') && ctx.containsKey('firstName')) {ctx.fullName = ctx.lastName + ' ' + ctx.firstName;}"""}}]
}
 
写入文档,并指定 pipeline
PUT test_18_write_02/_doc/1?pipeline=test_18_write_02_pipeline
{"firstName": "hello","lastName": "elasticsearch"
}
 
3.3 将字符串转为数组
将 name 转换为数组,并赋值给 names
创建 pipeline
PUT _ingest/pipeline/test_18_write_03_pipeline
{"description": "string to array","processors": [{"script": {"source": """ctx.names = ctx.name.splitOnToken(',')"""}}]
}
 
写入数据,并指定 pipeline
PUT test_18_write_03/_doc/1?pipeline=test_18_write_03_pipeline
{"name": "hello,elasticsearch"
}
