大家好,又见面了,我是你们的朋友全栈君。
Painless 也就是无痛的意思。这是一个专为 Elasticsearch 而设计的。当初的设计人员取名为 “Painless”,表达的意思的是在编程的时候没有疼痛感,很方便设计人员使用。由于这是一个脚本的语言,在实际的使用中,我们很难找到这些编程的方法及使用。在今天的教程中,我来讲述一下该如何来进行调试。
Debug.Explain
Painless 没有 REPL,很希望将来有一天会有,但它不会告诉你调试 Elasticsearch 中嵌入的 Painless 脚本的全部过程,因为脚本可以访问或 “上下文”访问数据非常重要。 目前,调试嵌入式脚本的最佳方法是在选择的位置抛出异常。 尽管你可以抛出自己的异常(抛出新的Exception(’whatever’)),但 Painless 的沙箱可阻止你访问有用的信息,例如对象的类型。 因此,Painless 具有实用程序方法 Debug.explain,它可以为你引发异常。 例如,你可以使用 _explain 探索脚本查询可用的上下文。
例子一
我们在 Kibana 中打入如下的命令:
PUT /hockey/_doc/1?refresh
{
"first": "johnny",
"last": "gaudreau",
"goals": [
9,
27,
1
],
"assists": [
17,
46,
0
],
"gp": [
26,
82,
1
],
"time": "2020-08-30"
}
上面的命令将生成如下的 mapping:
GET hockey/_mapping
{
"hockey" : {
"mappings" : {
"properties" : {
"assists" : {
"type" : "long"
},
"first" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"goals" : {
"type" : "long"
},
"gp" : {
"type" : "long"
},
"last" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"time" : {
"type" : "date"
}
}
}
}
}
如上图所示,我们可以看到有几种类型的数据:long, text, keyword 以及 date。那么现在问题来了,在实际的 script 编程中,我们该如何对这些数据进行操作。它们的数据类型有什么可以供我们使用的方法呢?
我们使用如下的 _explain 终点:
POST /hockey/_explain/1
{
"query": {
"script": {
"script": "Debug.explain(doc.goals)"
}
}
}
上面命令的相应结果为:
{
"error" : {
"root_cause" : [
{
"type" : "script_exception",
"reason" : "runtime error",
"painless_class" : "org.elasticsearch.index.fielddata.ScriptDocValues.Longs",
"to_string" : "[1, 9, 27]",
"java_class" : "org.elasticsearch.index.fielddata.ScriptDocValues$Longs",
"script_stack" : [
"Debug.explain(doc.goals)",
" ^---- HERE"
],
"script" : "Debug.explain(doc.goals)",
"lang" : "painless",
"position" : {
"offset" : 17,
"start" : 0,
"end" : 24
}
}
],
"type" : "script_exception",
"reason" : "runtime error",
"painless_class" : "org.elasticsearch.index.fielddata.ScriptDocValues.Longs",
"to_string" : "[1, 9, 27]",
"java_class" : "org.elasticsearch.index.fielddata.ScriptDocValues$Longs",
"script_stack" : [
"Debug.explain(doc.goals)",
" ^---- HERE"
],
"script" : "Debug.explain(doc.goals)",
"lang" : "painless",
"position" : {
"offset" : 17,
"start" : 0,
"end" : 24
},
"caused_by" : {
"type" : "painless_explain_error",
"reason" : null
}
},
"status" : 400
}
上面显示了一个 exception。同时它也显示了 doc.goals 是一个 这样类型的数据:
"painless_class" : "org.elasticsearch.index.fielddata.ScriptDocValues.Longs",
"java_class" : "org.elasticsearch.index.fielddata.ScriptDocValues$Longs",
接下来,我们来参考链接 Painless API Reference | Painless Scripting Language [7.0] | Elastic。我们寻找 org.elasticsearch.index.fielddata.ScriptDocValues.Longs:我们可以看到如下的一些说明:
Long get(int)
org.joda.time.ReadableDateTime getDate()
List getDates()
long getValue()
List getValues()
- Inherits methods from
Collection
,Iterable
,List
,Object
这里展示了它的一些可供使用的 API 接口。从上面,我们可以看出来这个数据是一个 List 类型的数据,我们可以使用如下的方法来进行统计:
GET hockey/_search
{
"query": {
"function_score": {
"script_score": {
"script": {
"lang": "painless",
"source": """
int total = 0;
for (int i = 0; i < doc['goals'].getLength(); ++i) {
total += doc['goals'].get(i);
}
return total;
"""
}
}
}
}
}
在这里,我们使用了 getLength() 以及 get(i)。这些方法都是 List 类型数据的方法。我们也可以使用精简的方法:
GET hockey/_search
{
"query": {
"function_score": {
"script_score": {
"script": {
"lang": "painless",
"source": """
int total = 0;
for (int i = 0; i < doc['goals'].length; ++i) {
total += doc['goals'][i];
}
return total;
"""
}
}
}
}
}
上面的显示的结果为:
{
"took" : 4,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 37.0,
"hits" : [
{
"_index" : "hockey",
"_type" : "_doc",
"_id" : "1",
"_score" : 37.0,
"_source" : {
"first" : "johnny",
"last" : "gaudreau",
"goals" : [
9,
27,
1
],
"assists" : [
17,
46,
0
],
"gp" : [
26,
82,
1
],
"time" : "2020-08-30"
}
}
]
}
}
例子二
接下来例子,我们可以使用 date 数据类型为例。在 Kibana 中运行如下的命令:
POST /hockey/_explain/1
{
"query": {
"script": {
"script": "Debug.explain(doc['time'])"
}
}
}
上面的命令将抛出如下的 exception:
{
"error" : {
"root_cause" : [
{
"type" : "script_exception",
"reason" : "runtime error",
"painless_class" : "org.elasticsearch.index.fielddata.ScriptDocValues.Dates",
"to_string" : "[2020-08-30T00:00:00.000Z]",
"java_class" : "org.elasticsearch.index.fielddata.ScriptDocValues$Dates",
"script_stack" : [
"Debug.explain(doc['time'])",
" ^---- HERE"
],
"script" : "Debug.explain(doc['time'])",
"lang" : "painless",
"position" : {
"offset" : 17,
"start" : 0,
"end" : 26
}
}
],
"type" : "script_exception",
"reason" : "runtime error",
"painless_class" : "org.elasticsearch.index.fielddata.ScriptDocValues.Dates",
"to_string" : "[2020-08-30T00:00:00.000Z]",
"java_class" : "org.elasticsearch.index.fielddata.ScriptDocValues$Dates",
"script_stack" : [
"Debug.explain(doc['time'])",
" ^---- HERE"
],
"script" : "Debug.explain(doc['time'])",
"lang" : "painless",
"position" : {
"offset" : 17,
"start" : 0,
"end" : 26
},
"caused_by" : {
"type" : "painless_explain_error",
"reason" : null
}
},
"status" : 400
}
从上面的 exception 中,我们可以看出来这个抛出的异常含有:
"painless_class" : "org.elasticsearch.index.fielddata.ScriptDocValues.Dates",
"java_class" : "org.elasticsearch.index.fielddata.ScriptDocValues$Dates",
它表明这个是一个 org.elasticsearch.index.fielddata.ScriptDocValues.Dates 类型的数据。我们查看 Painless API Reference | Painless Scripting Language [7.0] | Elastic, 并定位 org.elasticsearch.index.fielddata.ScriptDocValues.Dates,我们可以看到如下的方法描述:
org.elasticsearch.index.fielddata.ScriptDocValues.Dates
org.joda.time.ReadableDateTime get(int)
org.joda.time.ReadableDateTime getDate()
List getDates()
org.joda.time.ReadableDateTime getValue()
List getValues()
- Inherits methods from
Collection
,Iterable
,List
,Object
显然它是一个叫做 org.joda.time.ReadableDateTime 类型的数据。我们点击上面的链接,我们可以看到更多的关于这个类型的方法:
org.joda.time.ReadableDateTime
int getCenturyOfEra()
int getDayOfMonth()
int getDayOfWeek()
int getDayOfYear()
int getEra()
int getHourOfDay()
int getMillisOfDay()
int getMillisOfSecond()
int getMinuteOfDay()
int getMinuteOfHour()
int getMonthOfYear()
int getSecondOfDay()
int getSecondOfMinute()
int getWeekOfWeekyear()
int getWeekyear()
int getYear()
int getYearOfCentury()
int getYearOfEra()
String toString(String)
String toString(String, Locale)
- Inherits methods from
Comparable
,org.joda.time.ReadableInstant
我们从上面的列表中,我们看到非常丰富的方法供我们来使用。基于上面的理解,我们可以使用如下的脚本来进行搜索:
POST /hockey/_search
{
"query": {
"script": {
"script": """
doc['time'].value.getYear() > 2000
"""
}
}
}
在上面,我们使用了 getYear() 来获取年份。上面搜索返回的结果是:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "hockey",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"first" : "johnny",
"last" : "gaudreau",
"goals" : [
9,
27,
1
],
"assists" : [
17,
46,
0
],
"gp" : [
26,
82,
1
],
"time" : "2020-08-30"
}
}
]
}
}
当然,如果我们把搜索的年份修改为:
POST /hockey/_search
{
"query": {
"script": {
"script": """
doc['time'].value.getYear() < 2000
"""
}
}
}
我们将什么也搜索不到,这是因为文档的日期是 2020-08-30。
我们也可以直接使用最简洁的方法:
POST /hockey/_search
{
"query": {
"script": {
"script": """
return doc['time'].value.year > 1999
"""
}
}
}
这里,直接使用 year 作为一个属性来获取。
例子三
我们也可以直接对 _source 进行操作:
POST /hockey/_update/1
{
"script": "Debug.explain(ctx._source)"
}
上面的显示结果为:
{
"error" : {
"root_cause" : [
{
"type" : "illegal_argument_exception",
"reason" : "failed to execute script"
}
],
"type" : "illegal_argument_exception",
"reason" : "failed to execute script",
"caused_by" : {
"type" : "script_exception",
"reason" : "runtime error",
"painless_class" : "java.util.LinkedHashMap",
"to_string" : "{first=johnny, last=gaudreau, goals=[9, 27, 1], assists=[17, 46, 0], gp=[26, 82, 1], time=2020-08-30}",
"java_class" : "java.util.LinkedHashMap",
"script_stack" : [
"Debug.explain(ctx._source)",
" ^---- HERE"
],
"script" : "Debug.explain(ctx._source)",
"lang" : "painless",
"position" : {
"offset" : 17,
"start" : 0,
"end" : 26
},
"caused_by" : {
"type" : "painless_explain_error",
"reason" : null
}
}
},
"status" : 400
}
我们可以看到它是一个叫做 java.util.LinkedHashMap 类型的数据。我们可以直接查找 Java 的 API 文档来使用相应的方法。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/126513.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...