探究 | Elasticsearch Painless 脚本 ctx、doc、_source 的区别是什么?

1、实战问题

星主,请教一下,我在painless中使用doc的形式访问字段,如if(doc['xxx'].value ...)报错了,是painless中不允许使用doc吗?

我看官方示例和您之前的博客都是用ctx,请问 ctx 和 doc, params,params._source之间有什么区别吗? 我知道doc直接从内存获取,params从磁盘获取,但是对于上述4个的区别不是很了解,也没有查询到相关的资料......

——来自《死磕Elasticsearch 知识星球》

上述问题不止一次被问到,我自己在使用 painless 脚本的时候,也会遇到上述困惑。

今天,我们把这几种的区别梳理清楚。

2、关于 Elasticsearch painless 脚本

如果对 painless “无痛”脚本不了解的,推荐阅读:

  1. 干货 | Elasticsearch7.X Scripting脚本使用详解

  2. Elasticsearch 预处理没有奇技淫巧,请先用好这一招!

  3. Elasticsearch 脚本安全使用指南

  4. Elasticsearch 线上问题实战——如何借助 painless 更新时间?

3、 从应用层面解读:ctx、doc、_source 的区别?

3.1 场景1:ingest 管道预处理脚本使用 ctx

上例子:

PUT _ingest/pipeline/check_url
{
  "processors": [
    {
      "set": {
        "if": """ctx.href.url!=null && ctx.href.url.startsWith("http")""",
        "field": "href.insecure",
        "value": true
      }
    }
  ]
}
POST test/_doc/1?pipeline=check_url
{
  "href": {
    "url": "http://www.elastic.co/"
  }
}
POST test/_search

解读如下:

上面的脚本通过 ingest painless 脚本实现了判定:

ctx.href.url 如果非空且 ctx.href.url 以 http 开头,则:href.insecure 设置为:true。

159d4c24af3bdb926d2ccff1f9671e18.png

官方文档地址:

https://www.elastic.co/guide/en/elasticsearch/painless/7.15/painless-ingest-processor-context.html

3.2 场景 2:update/update_by_query 脚本使用 ctx._source

POST test/_doc/2
{
  "tags": "green"
}
GET test/_doc/2
POST test/_update/2
{
  "script": {
    "source": "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'none' }",
    "lang": "painless",
    "params": {
      "tag": "green"
    }
  }
}
GET test/_doc/2

上面的例子解读如下:

如果标签字段:tags 包含:“green”,则 执行删除操作;否则保持现状。

4363584fd6ee6899b057ec66cf4a5fab.png

官方文档地址:

https://www.elastic.co/guide/en/elasticsearch/painless/7.15/painless-update-context.html

3.3 场景3:reindex 脚本使用 ctx._source

上例子:

POST test-04/_doc/1
{
  "foo": "bar",
  "views": 1
}
POST _reindex
{
  "source": {
    "index": "test-04"
  },
  "dest": {
    "index": "test-new-04"
  },
  "script": {
    "source": "if (ctx._source.foo == 'bar') {ctx._source.views++; ctx._source.remove('foo')}",
    "lang": "painless"
  }
}
GET test-new-04/_search

如上 reindex 脚本解读如下:

如果源索引:test-04 字段 foo 内容=‘bar’,则 reindex 后删除 ‘foo’ 字段 且 views 取值加 1 。

f31b90a25e07701b61d6a4d2113954f8.png

3.4 场景4:search 脚本使用 doc['XXX']

DELETE test_03
PUT test_03
{
  "mappings": {
    "properties": {
      "views": {
        "type": "integer"
      }
    }
  }
}
POST test_03/_doc/1
{
  "views": 30
}
GET test_03/_search
{
  "script_fields": {
    "rnd_views": {
      "script": {
        "lang": "painless",
        "source": """
java.util.Random rnd = new Random();
doc['views'].value+rnd.nextInt(1000);
"""
      }
    }
  },
  "query": {
    "match_all": {}
  }
}

如上search 脚本解读如下:

对观看数 views 在检索的时候加了随机值。

106451a84d6f05c39d004c27064b2efe.png

官方文档:

https://www.elastic.co/guide/en/elasticsearch/painless/7.15/painless-field-context.html

3.5 应用层面小结

从上面的应用层面,我们能看出区别:

  • ingest 场景,使用:ctx.XXX;

  • update / update / update_by_query / reindex 场景,使用:ctx._source;

  • search和聚合场景,使用:doc['value']。

当然,Elasticsearch 远不止上面这些场景,更多推荐阅读:

b5076bb2958f78e73718231270d0d104.png

4、那遇到复杂的脚本处理咋办呢?

4.1 获取字符串中的子串

举例如下:求字符串中的某子串,java 语法中的 substring 还能用吗?

如果使用:ingest processor 预处理方式,怎么查官方是否支持,我相信是大家关注的问题。

因为:支不支持可以试,但试是穷举的方式,时间复杂度为 O(n);

能查看官方明确说支持,是最快的方式,时间复杂度为O(1)。

对于我们程序员来说,怎么快,我们就怎么来。

来吧,一步步走一遍,其他复杂例子原理同。

4.1.1 第一步,找 shard API。

细节 API 入口文档。

f59049af57a92cb9f33447b0d8c0e8e0.png

https://www.elastic.co/guide/en/elasticsearch/painless/master/painless-api-reference-shared.html

4.1.2 第二步,找到 string

如上是 7.13 版本截图,早期版本如:7.2 版本还有 string类, 7.13 已没有。

8890011577cbcf81b17f9edd1471b498.png

4.1.3 第三步:找 substring

dddaf4382b56f86ee996b2caa8cc4796.png

4.1.4 第四步:找 java API

这就到了 oracle 官网了。

bc9b8efc30f3df37e1da36ee77ea019d.png实践一把:

POST test-05/_doc/1
{
  "title": "hello world"
}
PUT _ingest/pipeline/substring_pipeline
{
  "processors": [
    {
      "script": {
        "lang": "painless",
        "source": """
ctx.sub_title = ctx.title.substring(0,5);
"""
      }
    }
  ]
}
POST test-05/_update_by_query?pipeline=substring_pipeline
{
  "query": {
    "match_all": {}
  }
}
POST test-05/_search

上面脚本是借助 ingest pipelie 实现:取子串内容,源串为:“hello world”,取出后的子串为:“hello”。

核心步骤再结合上面的截图解释一下:

-步骤1:确认 string 类型支持取子串;

-步骤2:找到取子串的语法:substring;

-步骤3:通过 ingest 预处理方式实现取子串,借助 ctx 组合 substring 实现。

9f98f31b9d7cf8bba5c649614cf6d074.png

4.2 获取日期格式的年份

POST test_06/_bulk
{"index":{"_id":1}}
{"m_type":1,"create_time":"2015-01-01T12:10:30Z","update_time":"2021-07-01T12:10:30Z"}
GET test_06/_mapping
GET test_06/_search
{
  "script_fields": {
    "rnd_views": {
      "script": {
        "lang": "painless",
        "source": """
doc['create_time'].value.getYear()
"""
      }
    }
  },
  "query": {
    "match_all": {}
  }
}

如上实例,借助 painless 脚本实现了获取日期类型数据的年份,是借助 getYear( ) 的函数实现的。

cfe86e1a2265dfd3157146195de03880.png

5、小结

Painless 脚本在数据预处理、更新、reindex、获取字段方面应用广泛。

因业务场景的不同,脚本使用方式也会有不同。大家使用过程中要根据使用方式的不同,来决定ctx、doc、_source 的选型。

希望本文对你的脚本实战选型有所帮助,也欢迎留言交流你的脚本使用心得......

推荐

1、重磅 | 死磕 Elasticsearch 方法论认知清单(2021年国庆更新版)

2Elasticsearch 7.X 进阶实战私训课(口碑不错)

95e89afec30c8aed24dd2c4075c7b6fd.png

更短时间更快习得更多干货!

已带领72位球友通过 Elastic 官方认证!

可能是中国学习氛围最好的 Elastic 圈子!

文字教程、视频教程、打卡氛围一应俱全。

全球近 1500+ 球友等你 >>

a7618fd4848a746dc81d5dc91e3bdade.gif

比同事抢先一步学习进阶干货!

  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
当使用 Elasticsearch 的 `_bulk` API 发送批量操作请求后,它将返回一个响应,其中包含每个操作的结果。响应的格式为 JSON,你可以解析它来获取有关每个操作的详细信息。 以下是一个示例 `_bulk` API 的响应结果: ```json { "took": 15, "errors": false, "items": [ { "index": { "_index": "myindex", "_type": "_doc", "_id": "1", "status": 200, "error": null } }, { "update": { "_index": "myindex", "_type": "_doc", "_id": "2", "status": 409, "error": { "type": "version_conflict_engine_exception", "reason": "Version conflict, document already exists (current version [1])" } } }, { "delete": { "_index": "myindex", "_type": "_doc", "_id": "3", "status": 200, "result": "deleted", "_shards": { "total": 2, "successful": 1, "failed": 0 } } } ] } ``` 在这个示例中,响应包含了三个操作的结果:一个索引操作、一个更新操作和一个删除操作。 - `took` 字段表示执行这个批量请求所花费的时间(以毫秒为单位)。 - `errors` 字段指示是否在批量请求中发生了错误。如果所有操作都成功,则为 `false`;如果至少有一个操作失败,则为 `true`。 - `items` 字段是一个数组,包含每个操作的结果。每个操作结果都是一个对象,其中包含了操作的类型(如 `index`、`update`、`delete` 等)和相应的元数据信息(如索引名称、文档 ID、状态码等)。 - 对于成功的操作,`status` 字段表示 HTTP 状态码,通常为 200。`error` 字段为 `null`。 - 对于失败的操作,`status` 字段可能表示错误的 HTTP 状态码,例如 409 表示版本冲突。`error` 字段包含了错误的详细信息,如错误类型和原因。 - 对于某些操作(如删除操作),还可能包含其他字段,如 `result` 表示操作的结果(如 "deleted" 表示删除成功),以及 `_shards` 字段表示操作在分片上的执行情况。 你可以根据需要解析这个响应,并处理每个操作的结果,以了解每个操作的成功与否,以及出错操作的具体错误信息。 希望这个解释对你有帮助!如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铭毅天下

和你一起,死磕Elastic!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值