赞
踩
Elasticsearch的script脚本是一个非常灵活的一个功能,可能平时用到比较少,但是在一些特殊需求时,script脚本还是非常合适的。
script非常灵活,但也不能滥用,在数据量很大时,动态的script field也非常影响性能。
如果确实有需要,可以用空间换时间的方法,在数据存ES的时候,pipeline语法把值进行解析,存储附加信息,方便搜索。
注意:我当前用的ES7.8,如果是别的老版本,语法可能不同
PUT pigg_test_store { "mappings": { "properties": { "name": { "type": "keyword" }, "age": { "type": "integer" }, "address": { "type": "text", "fields": { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } }, "birthday": { "type": "date" }, "socres": { "type": "integer" }, "chinese": { "type": "integer" }, "math":{ "type": "integer" }, "english":{ "type": "integer" } } } }
PUT pigg_test_store/_doc/1 { "name": ["王磊","王石"], "age": 33, "address": "江苏盐城盐都区", "birthday": "1989-12-25", "socres": [90, 98, 88], "chinese":90, "match": 98, "english":88 } PUT pigg_test_store/_doc/2 { "name": "朱大珣", "age": 31, "address": "江苏徐州睢宁县", "birthday": "1991-06-05", "socres": [93, 91, 98], "chinese":93, "match": 91, "english":98 }
修改文档时,通过ctx._source.fieldname来指定某个字段
POST pigg_test_store/_update/1
{
"script": "ctx._source.age += 1"
}
POST pigg_test_store/_update/1
{
"script": "ctx._source.age = 34"
}
一种更好的方法如下:
POST pigg_test_store/_update/1
{
"script": {
"source": "ctx._source.age = params.value",
"params": {
"value": 34
}
}
}
第一种方法不带params,每次执行它的脚本,都需要重新编译。
编译好的script是可以缓存的,第二种方法用到params,当要修改值时(比如把34改成37),只需修改params里的value值,而不需要修改"ctx._source.age = params.value"这个脚本,所以第二种方法只需要编译一次。
POST pigg_test_store/_update/1
{
"script": {
"source": "ctx._source.name.add(params.value)",
"params": {
"value": "王冬"
}
}
}
POST pigg_test_store/_update/1
{
"script": {
"source": "ctx._source.name.remove(ctx._source.name.indexOf(params.value))",
"params": {
"value": "王冬"
}
}
}
但是如果name不存在指定的值,这么删除会报错,所以得先判断下是否存在
POST pigg_test_store/_update/1
{
"script": {
"source": "if(ctx._source.name.indexOf(params.value) >= 0) ctx._source.name.remove(ctx._source.name.indexOf(params.value))",
"params": {
"value": "王冬"
}
}
}
除了用indexOf判断,也可以用contains判断,这么一看感觉painless的语法和Java的还挺像的啊。
POST pigg_test_store/_update/1
{
"script":{
"source": "if(ctx._source.name.contains(params.newname)) {ctx._source.name.remove(ctx._source.name.indexOf(params.newname))}",
"lang": "painless",
"params": {
"newname": "王大老板"
}
}
}
POST pigg_test_store/_update/1
{
"script": {
"source": "ctx._source.new_name = ctx._source.name"
}
}
但是这里得注意,这里也仅仅是复制值,不复制字段的mapping配置,如果不预先设置new_name的mapping配置,new_name还是会是ES的默认的text。
POST pigg_test_store/_update/1
{
"script": "ctx._source.remove('new_name')"
}
script fields给我的感觉就像MySQL里,在SELECT后面加上函数处理,比如拼接2列。
script fields平时并不存在,不占存储空间,属于运行时属性。因为它是运行时的,所以当文档的数据特别大的时候,会比较降低性能。
和上面修改文档的ctx._source.fieldname不同,在查询时,需要用
doc[‘fieldname’].value
doc.fieldname.value
params._source.fieldname。
doc和_source不同:doc会把数据加载到内存中,提高效率,但只是基本的简单类型。
GET pigg_test_store/_search
{
"script_fields": {
"sum_score": {
"script": {
"lang": "painless",
"source": "def sum = 0; sum = doc['chinese'].value + doc['match'].value + doc['english'].value; return sum; "
}
}
}
}
如果text类型,doc[‘fieldname’].value会报错。
当text类型包含keyword子字段时,用doc[‘fieldname.keyword’].value就可以。
GET pigg_test_store/_search
{
"script_fields": {
"new_address": {
"script": {
"source": "'地址是:' + doc['address.keyword'].value"
}
}
}
}
GET pigg_test_store/_search
{
"script_fields": {
"new_address": {
"script": {
"source": "'地址是:' + params._source.address"
}
}
}
}
GET pigg_test_store/_search
{
"query": {
"script": {
"script": "doc['name'].size() == 3"
}
}
}
GET pigg_test_store/_search
{
"query": {
"script": {
"script": "doc['name'].length == 3"
}
}
}
针对日期类型,还可以获取日期的属性
获取年份 GET pigg_test_store/_search { "script_fields": { "year_of_birth": { "script": { "source": "doc.birthday.value.year" } } } } 获取月份 GET pigg_test_store/_search { "script_fields": { "month_of_birth": { "script": { "source": "doc.birthday.value.monthOfYear" } } } } 获取日期 GET pigg_test_store/_search { "script_fields": { "day_of_birth": { "script": { "source": "doc.birthday.value.dayOfMonth" } } } }
如下统计所有人的总名称个数
GET pigg_test_store/_search
{
"size": 10,
"aggs": {
"name_total_count": {
"sum": {
"script": "doc['name'].size()"
}
}
}
}
pipeline预处理,在数据入库时,计算好相应的值,这样空间换时间。
比如doc[‘name’].size()在数据巨大时,比较慢,那么可以在保存name时,也多存一个字段length,保存name数据的数据个数。
PUT _ingest/pipeline/calculate_length
{
"description": "Calculate the length of name array",
"processors": [
{
"script": {
"source": "ctx.length = ctx.name.length"
}
}
]
}
插入数据
注意下面的?pipeline=calculate_length
PUT pigg_test_store/_doc/1?pipeline=calculate_length
{
"name": ["王磊","王石"],
"age": 33,
"address": "江苏盐城盐都区",
"birthday": "1989-12-25",
"socres": [90, 98, 88],
"chinese":90,
"match": 98,
"english":88
}
GET pigg_test_store/_doc/1 发现返回结果多了一个length属性,它存储的是name数组的数据个数 "_source" : { "birthday" : "1989-12-25", "address" : "江苏盐城盐都区", "match" : 98, "length" : 2, "socres" : [ 90, 98, 88 ], "chinese" : 90, "name" : [ "王磊", "王石" ], "english" : 88, "age" : 33 }
这样只有查询在length上做term查询,就可以查询到数据了,比script脚本"doc[‘name’].size() == 2"效率高。
GET pigg_test_store/_search
{
"query": {
"term": {
"length": {
"value": "2"
}
}
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。