前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Elasticsearch 企业级实战 01:Painless 脚本如何调试?

Elasticsearch 企业级实战 01:Painless 脚本如何调试?

作者头像
铭毅天下
发布2024-07-16 13:52:00
1070
发布2024-07-16 13:52:00
举报
文章被收录于专栏:铭毅天下

在企业级应用中,Elasticsearch 常常被用来处理复杂的数据查询和操作。

Painless 是 Elasticsearch 的内置脚本语言,虽然强大,但调试起来并不容易。

本文将详细介绍如何在实战中有效调试 Painless 脚本,以提高开发和运维效率。

本文所有实现均在 Elasticsearch 8.11 dev-tool 环境充分验证,建议放大图片查看结果。

1、 抛出问题

在使用 Elasticsearch 的过程中,咱们开发者经常需要编写和调试 Painless 脚本,例如在查询、更新文档或定义复杂的预处理条件时。

由于 Painless 没有 REPL 环境(实时代码测试工具),调试嵌入在 Elasticsearch 中的脚本变得更加困难。

开发者无法直接在交互式环境中输入和测试 Painless 脚本,必须依赖诸如 Kibana 的 Painless Lab 或其他工具来间接调试和验证脚本。

这增加了调试的复杂性和开发周期。

2、脚本调试方式分类

通过大量的调研工作,其实核心就分两类。

2.1 调试方案 1:Elasticsearch Debug.Explain 调试

Painless 提供的调试工具,可以在脚本中插入 Debug.explain 方法,通过抛出异常的方式输出变量信息。

参见官网:https://www.elastic.co/guide/en/elasticsearch/painless/current/painless-debugging.html

2.2 调试方案 2:Kibana Painless Lab 工具调试

Elasticsearch 7.13 引入的实验性功能 Painless Lab,是一个交互式代码编辑器,可以实时测试和调试 Painless 脚本。

参见如下图,相信你和我一样,看过这幅图,但没有真正用过。下一篇我们多花笔墨解读这一部分的用法。

3、Debug.Explain 调试实战案例

依然以官方示例作为范例解读,参见:

https://www.elastic.co/guide/en/elasticsearch/painless/current/painless-debugging.html#_debug_explain

3.1 官方样例解读

假设我们有一个包含球员数据的索引,文档如下:

代码语言:javascript
复制
DELETE hockey
PUT /hockey/_doc/1?refresh
{
"first": "johnny",
"last": "gaudreau",
"goals": [9, 27, 1],
"assists": [17, 46, 0],
"gp": [26, 82, 1]
}

我们可以使用以下脚本来查看 goals 字段的类型:

代码语言:javascript
复制
POST /hockey/_explain/1
{
  "query": {
    "script": {
      "script": "Debug.explain(doc.goals)"
    }
  }
}

执行结果如下:

当看到上面一堆输出的时候,相信你和我的表情一致:“这是啥?”、“错了吧?”、“就这”......

结合上文定义:“通过抛出异常的方式输出变量信息”,本质上是抛出异常了。

3.2 延伸详细解读

我们一点点剖析一下,如下内容官网没有提供。

我们使用脚本的本质,我延展一下:

3.2.1 脚本过滤检索
代码语言:javascript
复制
POST /hockey/_search
{
  "query": {
    "bool": {
      "filter": {
        "script": {
          "script": """
          def goals = doc['goals'];
          
          // 计算总和
          def sum = 0;
          for (def goal : goals) {
            sum += goal;
          }
          
          return sum > 30;
          """
        }
      }
    }
  }
}

过滤查询出总和大于 30 的数据。结果符合预期,如下图所示:

那,如何调试呢?

3.2.2 explain API 调试文档是否满足条件

极简单的方式,可以借助:explain 解读。也就是说:使用 _explain API 来探究并调试一个脚本查询。

细节参见:

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-explain.html

执行命令如下:

代码语言:javascript
复制
POST /hockey/_explain/1
{
  "query": {
    "bool": {
      "filter": {
        "script": {
          "script": """
          def goals = doc['goals'];
          //Debug.explain(goals);
          // 计算总和
          def sum = 0;
          for (def goal : goals) {
            sum += goal;
          }
          
          return sum > 30;
          """
        }
      }
    }
  }
}

之前咱们讲过 explain 可以详尽展开评分计算细节。

而此处还展示了:matched与否标记,如果条件满足则返回 true;如果不满足则返回 false。

显然,咱们的文档1符合查询条件。

3.2.3 Debug.explain 使用细节调试

还不够,doc['goals'] 到底来自哪里呢?

如下 DSL 仅在 3.2.2 基础上加了 Debug.explain 。

代码语言:javascript
复制
POST /hockey/_explain/1
{
  "query": {
    "bool": {
      "filter": {
        "script": {
          "script": """
          def goals = doc['goals'];
          Debug.explain(doc['goals']);
          // 计算总和
          def sum = 0;
          for (def goal : goals) {
            sum += goal;
          }
          
          return sum > 30;
          """
        }
      }
    }
  }
}

执行结果截图如下:

该错误指出在调用 Debug.explain(doc['goals']); 时发生了运行时错误。

Debug.explain 是一个调试方法,用于在脚本中输出变量的信息。然而,这种方法在某些上下文中可能不被允许,或者 doc['goals'] 字段的类型 ScriptDocValues.Longs 导致了这个问题。

其实,我们还能得到如下有价值信息:

  • (1):"to_string": "[1, 9, 27]" 显示了 doc['goals'] 字段的值,即一个包含 [1, 9, 27] 的数组。
  • (2):"painless_class": "org.elasticsearch.index.fielddata.ScriptDocValues.Longs" 指出导致错误的类是 ScriptDocValues.Longs。这是一个表示长整型字段值的类。

关于这个类的官方文档,可以参见:

根据:org.elasticsearch.index.fielddata.ScriptDocValues.Longs

找到 fielddata 子模块,进而找到文档,参见下图。

https://www.elastic.co/guide/en/elasticsearch/painless/8.11/painless-api-reference-shared-org-elasticsearch-index-fielddata.html#painless-api-reference-shared-ScriptDocValues-Longs

其实,这些 API 就是我们使用脚本的依据和参考。

这里,往往也是被问最多的地方:Elasticsearch 脚本细节运算的 API 在哪里查?支持哪些方法?

有了它,我们进一步可以执行脚本了,举例:sort 使用如下:

代码语言:javascript
复制
POST /hockey/_search
{
  "script_fields": {
    "sorted_goals": {
      "script": {
        "lang": "painless",
        "source": """
          // 获取 goals 数组,并复制到一个新的列表中
          def goals = new ArrayList(doc['goals']);

          // 定义排序比较器,从大到小排序
          goals.sort((a, b) -> b.compareTo(a));

          // 返回排序后的数组
          return goals;
        """
      }
    }
  }
}

goals.sort((a, b) -> b.compareTo(a)); ——语法的核心是使用 Java 8 的 lambda 表达式和 Comparator 接口来定义排序规则。

b.compareTo(a) 是对 b 和 a 进行比较的方法调用。compareTo 方法返回一个整数,用于指示元素的顺序:

  • 如果返回负数,则表示 b 小于 a。
  • 如果返回零,则表示 b 等于 a。
  • 如果返回正数,则表示 b 大于 a。仔细看来,这是意外的收获!

4、小结

篇幅原因,本文只给出了Painless 脚本的第一种调试方式:Debug.explain 的详尽解读。

相信对你的脚本调试也会有帮助,如果你有脚本调试疑问,欢迎留言交流哈。

关于 Kibana Painless Lab 工具调试 ,且听下回分解。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-07-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 铭毅天下Elasticsearch 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、 抛出问题
  • 2、脚本调试方式分类
    • 2.1 调试方案 1:Elasticsearch Debug.Explain 调试
      • 2.2 调试方案 2:Kibana Painless Lab 工具调试
      • 3、Debug.Explain 调试实战案例
        • 3.1 官方样例解读
          • 3.2 延伸详细解读
            • 3.2.1 脚本过滤检索
            • 3.2.2 explain API 调试文档是否满足条件
            • 3.2.3 Debug.explain 使用细节调试
        • 4、小结
        相关产品与服务
        Elasticsearch Service
        腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档