文章目录
- 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')
: 判断是否包含goals
key。之前我们说过的。文档会被映射为一个 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"
}