大家好,又见面了,我是你们的朋友全栈君。
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 文档来使用相应的方法。
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/126513.html原文链接:https://javaforall.net
