Elasticsearch教程(30) pipeline处理 painless script脚本详细总结 查询更新案例

一、前言

Elasticsearch的script脚本是一个非常灵活的一个功能,可能平时用到比较少,但是在一些特殊需求时,script脚本还是非常合适的。

script非常灵活,但也不能滥用,在数据量很大时,动态的script field也非常影响性能。

如果确实有需要,可以用空间换时间的方法,在数据存ES的时候,pipeline语法把值进行解析,存储附加信息,方便搜索。

注意:我当前用的ES7.8,如果是别的老版本,语法可能不同

二、创建测试数据

1. 创建index

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"
      }
    }
  }
}

2. 插入数据

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
}

三、利用script脚本修改文档

1. 将integer类型的age进行数学计算

修改文档时,通过ctx._source.fieldname来指定某个字段

POST pigg_test_store/_update/1
{
    
    
  "script": "ctx._source.age += 1"
}

2. 指定integer类型的age为一个新值

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"这个脚本,所以第二种方法只需要编译一次。

3. 指定数组添加一个值

POST pigg_test_store/_update/1
{
    
    
  "script": {
    
    
    "source": "ctx._source.name.add(params.value)",
    "params": {
    
    
      "value": "王冬"
    }
  }
}

4. 指定数组删除某个值

POST pigg_test_store/_update/1
{
    
    
  "script": {
    
    
    "source": "ctx._source.name.remove(ctx._source.name.indexOf(params.value))",
    "params": {
    
    
      "value": "王冬"
    }
  }
}

但是如果name不存在指定的值,这么删除会报错,所以得先判断下是否存在

5. 先判断是否存在,然后指定数组删除某个值

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(params.newname)}",
    "lang": "painless",
    "params": {
    
    
      "newname": "王大老板"
    }
  } 
}

6. 字段直接复制值

POST pigg_test_store/_update/1
{
    
    
  "script": {
    
    
    "source": "ctx._source.new_name = ctx._source.name"
  }
}

但是这里得注意,这里也仅仅是复制值,不复制字段的mapping配置,如果不预先设置new_name的mapping配置,new_name还是会是ES的默认的text。

7. 删除一个字段,不修改mapping

POST pigg_test_store/_update/1
{
    
    
  "script": "ctx._source.remove('new_name')"
}

四、利用script fields查询文档

script fields给我的感觉就像MySQL里,在SELECT后面加上函数处理,比如拼接2列。

script fields平时并不存在,不占存储空间,属于运行时属性。因为它是运行时的,所以当文档的数据特别大的时候,会比较降低性能。

1. 获取字段值的方式

和上面修改文档的ctx._source.fieldname不同,在查询时,需要用

doc[‘fieldname’].value

doc.fieldname.value

params._source.fieldname

doc和_source不同:doc会把数据加载到内存中,提高效率,但只是基本的简单类型。

2. 求语数外三门的总分

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; "
      }
    }
  }
}

3. 如果text类型,用doc[‘fieldname’].value会报错

如果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"
      }
    }
  }
}

4. 如果text类型,用params._source.fieldname

GET pigg_test_store/_search
{
    
    
  "script_fields": {
    
    
    "new_address": {
    
    
      "script": {
    
    
        "source": "'地址是:' + params._source.address"
      }
    }
  }
}

5. 用size()或length获取数组的长度

GET pigg_test_store/_search
{
    
    
  "query": {
    
    
    "script": {
    
    
      "script": "doc['name'].size() == 3"
    }
  }
}
GET pigg_test_store/_search
{
    
    
  "query": {
    
    
    "script": {
    
    
      "script": "doc['name'].length == 3"
    }
  }
}

6. 获取日期的属性

针对日期类型,还可以获取日期的属性

获取年份
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"
      }
    }
  }
}

7. 在聚合中使用script

如下统计所有人的总名称个数

GET pigg_test_store/_search
{
    
    
  "size": 10,
  "aggs": {
    
    
    "name_total_count": {
    
    
      "sum": {
    
    
        "script": "doc['name'].size()"
      }
    }
  }
}

五、pipeline处理,提高查询效率

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"
      }
    }
  }
}

猜你喜欢

转载自blog.csdn.net/winterking3/article/details/114033906