精华内容
参与话题
问答
  • Elasticsearch:Painless scripting

    千次阅读 2020-02-07 13:49:55
    Painless使用类似于Groovy的Java样式语法。实际上,大多数Painless脚本也是有效的Groovy,而简单的Groovy脚本通常是有效的Painless脚本。 (本规范假定您至少对Java和相关语言有一定的了解。) Painless本质上是...

    Painless 使用类似于 Groovy 的 Java 样式语法。实际上,大多数 Painless 脚本也是有效的 Groovy,而简单的 Groovy 脚本通常是有效的 Painless 脚本。 (本规范假定您至少对 Java 和相关语言有一定的了解。)

    Painless 本质上是 Java 的子集,具有一些其他脚本语言功能,使脚本更易于编写。但是,有一些重要的差异,尤其是在铸造模型上。使用 ANTLR4 和 ASM 库来解析和编译 Painless 脚本。Painless 脚本直接编译为 Java 字节码,并针对标准 Java 虚拟机执行。该规范使用 ANTLR4 语法符号来描述允许的语法。但是,实际的 Painless 语法比此处显示的更为紧凑。Painless 是一种专门用于 Elasticsearch 的简单安全的脚本语言。它是 Elasticsearch 的默认脚本语言,可以安全地用于 inline 和 stored 脚本。

    我们可以在 Elasticsearch 中可以使用脚本的任何地方使用 Painless。 Painless 功能包括:

    • 快速的性能:Painless 脚本的运行速度比其他脚本快几倍。
    • 语法:扩展 Java 语法以提供 Groovy 风格的脚本语言功能,使脚本更易于编写。
    • 安全性:具有方法调用/字段粒度的细粒度白名单。 (有关可用类和方法的完整列表,请参阅《 Painless API参考》。)
    • 可选类型:变量和参数可以使用显式类型或动态 def 类型。
    • 优化:专为 Elasticsearch 脚本设计。

    让我们通过将一些学术统计数据加载到 Elasticsearch 索引中来说明 Painless的 工作方式:

    PUT academics/_bulk
    {"index":{"_id":1}}
    {"first":"Agatha","last":"Christie","base_score":[9,27,1],"target_score":[17,46,0],"grade_point_index":[26,82,1],"born":"1978/08/13"}
    {"index":{"_id":2}}
    {"first":"Alan","last":"Moore","base_score":[7,54,26],"target_score":[11,26,13],"grade_point_index":[26,82,82],"born":"1976/10/12"}
    {"index":{"_id":3}}
    {"first":"jiri","last":"Ibsen","base_score":[5,34,36],"target_score":[11,62,42],"grade_point_index":[24,80,79],"born":"1983/01/04"}
    {"index":{"_id":4}}
    {"first":"William","last":"Blake","base_score":[4,6,15],"target_score":[8,23,15],"grade_point_index":[26,82,82],"born":"1990/02/17"}
    {"index":{"_id":5}}
    {"first":"Shaun","last":"Tan","base_score":[5,0,0],"target_score":[8,1,0],"grade_point_index":[26,1,0],"born":"1993/06/20"}
    {"index":{"_id":6}}
    {"first":"Peter","last":"Hitchens","base_score":[0,26,15],"target_score":[11,30,24],"grade_point_index":[26,81,82],"born":"1969/03/20"}
    {"index":{"_id":7}}
    {"first":"Raymond","last":"Carver","base_score":[7,19,5],"target_score":[3,17,4],"grade_point_index":[26,45,34],"born":"1963/08/10"}
    {"index":{"_id":8}}
    {"first":"Lee","last":"Child","base_score":[2,14,7],"target_score":[8,42,30],"grade_point_index":[26,82,82],"born":"1992/06/07"}
    {"index":{"_id":39}}
    {"first":"Joseph","last":"Heller","base_score":[6,30,15],"target_score":[3,30,24],"grade_point_index":[26,60,63],"born":"1984/10/03"}
    {"index":{"_id":10}}
    {"first":"Harper","last":"Lee","base_score":[3,15,13],"target_score":[6,24,18],"grade_point_index":[26,82,82],"born":"1976/03/17"}
    {"index":{"_id":11}}
    {"first":"Ian","last":"Fleming","base_score":[3,18,13],"target_score":[6,20,24],"grade_point_index":[26,67,82],"born":"1972/01/30"}

    在上面,我们已经创建好一个叫做 academics 的 Elasticsearch 索引。在下面,我们通过 Painless 的脚本来对这个索引进行操作。

     

    通过 Painless 访问 doc 值

    可以从名为 doc 的 map 访问文档值。 例如,以下脚本计算学生的总体目标。 本示例使用强类型的 int 和 for 循环。

    GET academics/_search
    {
      "query": {
        "function_score": {
          "script_score": {
            "script": {
              "lang": "painless",
              "source": """
                 int total = 0; 
                 for (int i = 0; i < doc['base_score'].length; ++i) { 
                   total += doc['base_score'][i]; 
                 } 
                 return total;
              """
            }
          }
        }
      }
    }

    在上面请注意,我们使用了两个 """ 包含我们的 Painless 代码。这是为了能够让我们写出来比较好看格式的代码。必须指出的是:在上面,我们使用了 doc['base_score'] 的相似来访问数据,但是其实,我们也可以使用 . 的形式来进行访问,比如 doc.base_score。这样的写法也非常直接。但是对于一些特殊的字段,比如 @timestamp 或一些汉字的字段,这个并不适用。比如 doc.@timestamp 这个是不被接受的。上面的代码我们可以写作为:

    GET academics/_search
    {
      "query": {
        "function_score": {
          "script_score": {
            "script": {
              "lang": "painless",
              "source": """
                 int total = 0; 
                 for (int i = 0; i < doc.base_score.length; ++i) { 
                   total += doc.base_score[i]; 
                 } 
                 return total;
              """
            }
          }
        }
      }
    }

    从上面的代码我们可以看出来,代码和 Java 代码非常相似。通过这个循环,我们访问 doc 的 map 值,从而计算出 base_score 的总值。返回的结果为:

        "hits" : [
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "2",
            "_score" : 87.0,
            "_source" : {
              "first" : "Alan",
              "last" : "Moore",
              "base_score" : [
                7,
                54,
                26
              ],
              "target_score" : [
                11,
                26,
                13
              ],
              "grade_point_index" : [
                26,
                82,
                82
              ],
              "born" : "1976/10/12"
            }
          },
         ...
      ]

    在上面我们可以看出来我们的 _score 的分数为 base_score 的总和7 + 54 + 26 = 87。

    另外,我们可以使用 script_fields 而不是 function_score 来做同样的事情:

    GET academics/_search
    {
      "query": {
        "match_all": {}
      },
      "script_fields": {
        "total_goals": {
          "script": {
            "lang": "painless",
            "source": """
               int total = 0; 
               for (int i = 0; i < doc['base_score'].length; ++i) { 
                 total += doc['base_score'][i]; 
                } 
                return total;
            """
          }
        }
      }
    }

    返回结果:

        "hits" : [
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 1.0,
            "fields" : {
              "total_goals" : [
                37
              ]
            }
          },
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "2",
            "_score" : 1.0,
            "fields" : {
              "total_goals" : [
                87
              ]
            }
          },
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "3",
            "_score" : 1.0,
            "fields" : {
              "total_goals" : [
                75
              ]
            }
         ...
     ]

    在这里我们可以看到有一个叫做 total_goals 的 field,它包含了所有文档的 base_score 的分数相加的结果。细心的读者可能已经看出来了,在返回的结果里,没有 _source 这个字段,为了能够得到 _source 这个字段,我们必须在请求中特别提出来:

    GET academics/_search
    {
      "_source" : [],
      "query": {
        "match_all": {}
      },
      "script_fields": {
        "total_goals": {
          "script": {
            "lang": "painless",
            "source": """
               int total = 0; 
               for (int i = 0; i < doc['base_score'].length; ++i) { 
                 total += doc['base_score'][i]; 
                } 
                return total;
            """
          }
        }
      }
    }

    在上面,我们可以看出来,我特意添加了 "_source": [] 在请求中,这样,我们可以得到如下的响应:

        "hits" : [
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "5",
            "_score" : 1.0,
            "_source" : {
              "base_score" : [
                5,
                0,
                0
              ],
              "last" : "Tn",
              "born" : "1993/06/20",
              "target_score" : [
                8,
                1,
                0
              ],
              "first" : "Shaun",
              "grade_point_index" : [
                26,
                1,
                0
              ]
            },
            "fields" : {
              "total_goals" : [
                5
              ]
            }
          },
      ...
     ]

    我们可以看到这次除了新增加的 total_goals 之外,我们所需要的 _source 也在返回的数据中。

    脚本字段还可以使用 params ['_ source'] 访问实际的 _source 文档,并提取要从中返回的特定元素。上面的请求也可以表述为:

    GET academics/_search
    {
      "query": {
        "match_all": {}
      },
      "script_fields": {
        "total_goals": {
          "script": {
            "lang": "painless",
            "source": """
               int total = 0; 
               for (int i = 0; i < params['_source']['base_score'].length; ++i) { 
                 total += params['_source']['base_score'][i]; 
               } 
               return total
            """
          }
        }
      }
    }

    了解 doc['my_field'] .value 和 params['_ source'] ['my_field'] 之间的区别很重要。 第一个使用 doc 关键字,将导致将该字段的术语加载到内存中(缓存),这将导致执行速度更快,但会占用更多内存。 另外,doc [...] 符号仅允许使用简单值字段(你不能从中返回 json 对象),并且仅对未分析或基于单个术语的字段有意义。 但是,仍然建议使用 doc(即使有可能)从文档访问值的方式,因为 _source 每次使用时都必须加载和解析。 使用 _source 非常慢。

    我们也注意到所有文档的分数都是 1.0,并且返回的结果不是按照我们想要的分数从高到底进行排序的。我们无法通过 sort 多 total_goals 进行排序,因为这个字段不是在 source 里的字段。但是我们可以进行如下的方法来进行排序:

    GET academics/_search
    {
      "query": {
        "match_all": {}
      },
      "script_fields": {
        "total_goals": {
          "script": {
            "lang": "painless",
            "source": """
               int total = 0; 
               for (int i = 0; i < doc['base_score'].length; ++i) { 
                 total += doc['base_score'][i]; } 
                 return total;
            """
          }
        }
      },
      "sort": {
        "_script": {
          "type": "string",
          "order": "asc",
          "script": {
            "lang": "painless",
            "source": """
              return doc['first.keyword'].value + ' ' + doc['last.keyword'].value
            """
          }
        }
      }
    }

    以上示例使用 Painless 脚本按学生的姓和名对学生进行排序。 使用 doc['first.keyword'].value 和 doc['last.keyword'].value 访问名称。在上面,我们通过 doc['first.keyword'].value + ' ' + doc['last.keyword'].value 来重新组合一个新的字段,并按照这个字段按照升序进行排序。这种排序的结果是分数变为 null:

        "hits" : [
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : null,
            "fields" : {
              "total_goals" : [
                37
              ]
            },
            "sort" : [
              "Agatha Christie"
            ]
          },
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "2",
            "_score" : null,
            "fields" : {
              "total_goals" : [
                87
              ]
            },
            "sort" : [
              "Alan Moore"
            ]
          },
        ...
      ]

     

    使用 Painless 来更新字段

    通过访问 ctx._source。<field-name> 字段的原始源,我们还可以轻松更新字段。

    首先,我们通过提交以下请求来查看学生的源数据:

    GET academics/_search
    {
     "query": {
       "term": {
         "_id": 1
       }
     }
    }

    它显示的结果是:

        "hits" : [
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 1.0,
            "_source" : {
              "first" : "Agatha",
              "last" : "Christie",
              "base_score" : [
                9,
                27,
                1
              ],
              "target_score" : [
                17,
                46,
                0
              ],
              "grade_point_index" : [
                26,
                82,
                1
              ],
              "born" : "1978/08/13"
            }
          }
        ]

    要将 id 为 1 的学生的姓氏更改为 “Frost”,只需将 ctx._source.last 设置为新值:

    POST academics/_doc/1/_update
    {
      "script": {
        "lang": "painless",
        "inline": "ctx._source.last = params.last",
        "params": {
          "last": "Frost"
        }
      }
    }

    在上面我们通过 ctx._source.last = params.last 把 _source.last 改为在 params 里定义的 Frost。请注意,有的开发者可能认为如下的脚本会更加直接:

    ctx._source.last = “Frost"

    因为脚本在每次执行的时候都会被编译。一旦编译就会存于被缓存以便以后引用。编译一个脚本也会花时间的。如果一个脚本不发生变化就不会被重新编译,而引用之前编译好的脚本。如果我们使用上面的直接引用 Frost 的方式,那么如果接下来对其它的 id 来进行同样的操作,那么脚本将发生改变,那么新的脚本将被重新编译,从而浪费 CPU 的时间。这也就是我们为啥使用 params 来传输参数,而保持脚本不变。

    我们还可以将字段添加到文档中。 例如,此脚本添加了一个新字段,其中包含学生的昵称 “JS”。

    POST academics/_doc/1/_update
    {
      "script": {
        "lang": "painless",
        "inline": "ctx._source.last = params.last; ctx._source.nick = params.nick",
        "params": {
          "last": "Smith",
          "nick": "JS"
        }
      }
    }

    在上面我们使用脚本 cxt._source.nick = params.nick 来为我们的文档添加了一个叫做 nick 的新字段。我们可以通过如下的方法来查询:

    GET academics/_search
    {
     "query": {
       "term": {
         "_id": 1
       }
     }
    }

    返回结果:

        "hits" : [
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "1",
            "_score" : 1.0,
            "_source" : {
              "first" : "Agatha",
              "last" : "Smith",
              "base_score" : [
                9,
                27,
                1
              ],
              "target_score" : [
                17,
                46,
                0
              ],
              "grade_point_index" : [
                26,
                82,
                1
              ],
              "born" : "1978/08/13",
              "nick" : "JS"
            }
          }
        ]

    我们可看到 _id 为 1 的文档多了一个 nick 的字段,同时它的l ast 也修改为 Smith。

     

    Dates 

    日期字段显示为 ReadableDateTime,因此它们支持诸如 getYear,getDayOfWeek 和 getMillis 之类的方法。 例如,以下请求返回每个大学生的出生年份:

    GET academics/_search
    {
      "script_fields": {
        "birth_year": {
          "script": {
            "source": "doc.born.value.year"
          }
        }
      }
    }

    返回结果为:

        "hits" : [
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "2",
            "_score" : 1.0,
            "fields" : {
              "birth_year" : [
                1976
              ]
            }
          },
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "3",
            "_score" : 1.0,
            "fields" : {
              "birth_year" : [
                1983
              ]
            }
          },
       ...
     ]

    在上面我可以看出来有一个 birth_year 含有该学生的出生年份。

     

    Regular Expressions

    默认情况下,正则表达式是禁用的,因为它们绕过了 Painless 对长时间运行且占用大量内存的脚本的保护。 更糟糕的是,即使看起来无害的正则表达式也可能具有惊人的性能和堆栈深度行为。 它们仍然是一个了不起的强大工具,但太可怕了,无法默认启用。 在elasticsearch.yml 中将 script.painless.regex.enabled 设置为 true 以启用它们。

    Painless 对正则表达式的本机支持具有语法构造:

    /pattern/:模式文字可创建模式。 这是 Painless 创建模式的唯一方法。 /.../ 内的模式只是 Java 正则表达式。

    • =〜:find运算符返回一个布尔值,如果文本的子序列匹配,则返回 true,否则返回 false。
    • ==〜:match 运算符返回一个布尔值,如果文本匹配则返回 true,否则返回 false。

    使用查找运算符(=〜),我们可以更新所有姓氏中带有“b”的学术学生:

    POST academics/_update_by_query
    {
      "script": {
        "lang": "painless",
        "source": """
           if (ctx._source.last =~ /b/) {
             ctx._source.last += "matched"
           } else {
             ctx.op = 'noop'
           }
        """
      }
    }

    我们查看一下我们的文档:

    GET academics/_search
    {
      "query": {
        "regexp": {
          "last": ".*matched"
        }
      }
    }

    返回结果:

        "hits" : [
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "3",
            "_score" : 1.0,
            "_source" : {
              "base_score" : [
                5,
                34,
                36
              ],
              "last" : "Ibsenmatched",
              "born" : "1983/01/04",
              "target_score" : [
                11,
                62,
                42
              ],
              "first" : "jiri",
              "grade_point_index" : [
                24,
                80,
                79
              ]
            }
          }
        ]

    在上面的结果中,我们可以看出来 last 为 Ibsen 的后面加上了一个 matched。

    使用匹配运算符(==〜),我们可以更新所有名称以辅音开头并以元音结尾的文档:

    POST academics/_update_by_query
    {
      "script": {
        "lang": "painless",
        "source": """
          if (ctx._source.last ==~ /[^aeiou].*[aeiou]/) {
            ctx._source.last += "matched"
          } else {
            ctx.op = 'noop'
          }
        """
      }
    }

    我们再次重新查询一下:

    GET academics/_search
    {
      "query": {
        "regexp": {
          "last": ".*matched"
        }
      }
    }

    我们可以看到:

          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "2",
            "_score" : 1.0,
            "_source" : {
              "base_score" : [
                7,
                54,
                26
              ],
              "last" : "Moorematched",
              "born" : "1976/10/12",
              "target_score" : [
                11,
                26,
                13
              ],
              "first" : "Alan",
              "grade_point_index" : [
                26,
                82,
                82
              ]
            }
          },
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "4",
            "_score" : 1.0,
            "_source" : {
              "base_score" : [
                4,
                6,
                15
              ],
              "last" : "Blakematched",
              "born" : "1990/02/17",
              "target_score" : [
                8,
                23,
                15
              ],
              "first" : "William",
              "grade_point_index" : [
                26,
                82,
                82
              ]
            }
          },
        ...
      ]

    在上面,我们看到 Blake 是复合条件的一个 last。它是辅音 b 开头并以元音 e 结尾。它的 last 最后修改为 Moorematched。

    我们可以直接使用 Pattern.matcher 获取 Matcher 实例,并删除所有姓氏中的所有元音:

    POST academics/_update_by_query
    {
      "script": {
        "lang": "painless",
        "source": "ctx._source.last = /[aeiou]/.matcher(ctx._source.last).replaceAll('')"
      }
    }

    我们再次查询一下:

    GET academics/_search
    {
      "_source": ["last"]
    }

    显示结果:

        "hits" : [
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "5",
            "_score" : 1.0,
            "_source" : {
              "last" : "Tn"
            }
          },
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "6",
            "_score" : 1.0,
            "_source" : {
              "last" : "Htchns"
            }
          },
          {
            "_index" : "academics",
            "_type" : "_doc",
            "_id" : "7",
            "_score" : 1.0,
            "_source" : {
              "last" : "Crvr"
            }
          }
      ...
     ]

    我们可以看到,在上的结果中,索引的 last 里都没有一个是属于 [aeiou] 的字母。

    Matcher.replaceAll 只是对 Java Matcher 的 replaceAll 方法的调用,因此它支持 $1 和 \ 1 进行替换:

    POST academics/_update_by_query
    {
      "script": {
        "lang": "painless",
        "source": "ctx._source.last = /n([aeiou])/.matcher(ctx._source.last).replaceAll('$1')"
      }
    }

    在这之前,我也有另外一篇文章 “Elasticsearch: Painless script编程”。它和本文章有些地方是重复的地方。供大家从不同的角度来学习。

    展开全文
  • elasticsearch painless最强教程

    万次阅读 多人点赞 2017-09-29 15:19:41
    何为painless painless的特性 简单的例子 具体例子 初始化数据 用painless获取doc的值 通过painless更新对象值 单条记录更新 批量更新 Dates

    记得以前写过一个postman的最强教程,只可惜因为工作的原因,没再继续做测试,最终也没有写完。有点丧。。。这段时间在研究实时日志分析系统ELK,发现围绕ES这个搜索引擎或者说document数据库而开发的各种工具和周边居然是令人震惊的完善。ES真的不单单是一个分词,搜索的引擎,在各种功能和应用场景的完备性上已经不输很多数据库产品,比如mongoDB。本文着重介绍的ES 5.0版本后推出painless。(不过看来是个冷门,没人用啊)

    何为painless

    ElasticStack在升级到5.0版本之后,带来了一个新的脚本语言,painless。这里说“新的“是相对与已经存在groove而言的。还记得Groove脚本的漏洞吧,Groove脚本开启之后,如果被人误用可能带来各种漏洞,为什么呢,主要是这些外部的脚本引擎太过于强大,什么都能做,用不好或者设置不当就会引起安全风险,基于安全和性能方面,所以elastic.co开发了一个新的脚本引擎,名字就叫Painless,顾名思义,简单安全,无痛使用,和Groove的沙盒机制不一样,Painless使用白名单来限制函数与字段的访问,针对es的场景来进行优化,只做es数据的操作,更加轻量级,速度要快好几倍,并且支持Java静态类型,语法保持Groove类似,还支持Java的lambda表达式。

    painless的特性

    painless可以用在所有可以使用script的场景下,并具有以下特性:

    • 高性能。painless在es的运行速度是其他语言的数倍。这里写图片描述
    • 安全。使用白名单来限制函数与字段的访问,避免了可能的安全隐患
    • 可选类型。你可以在脚本当中使用强类型的编程方式或者动态类型的编程方式。
    • 语法。扩展了java的基本语法以兼容groove风格的脚本语言特性,使得plainless易读易写
    • 有针对的优化。这门语言是为elasticsearch专门定制的。

    简单的例子

    要了解这门东西,肯定要先看看它能做到什么才能激发起兴趣。先简单看一下例子,和各种groove,python,js们,没有什么区别,但要特别注意,使用强类型编程方式可以极大的加快运行速率

    #动态类型的写法
    def first = input.doc.first_name.0;
    
    def last  = input.doc.last_name.0;
    
    return first + " " + last;
    
    #强类型(10倍速度于上面的动态类型)
    String first = (String)((List)((Map)input.get("doc")).get("first_name")).get(0);
    
    String last  = (String)((List)((Map)input.get("doc")).get("last_name")).get(0);
    
    return first + " " + last;

    具体例子

    初始化数据

    我们先输入一串曲棍球的数据到ES当中。

    PUT hockey/player/_bulk?refresh
    {"index":{"_id":1}}
    {"first":"johnny","last":"gaudreau","goals":[9,27,1],"assists":[17,46,0],"gp":[26,82,1],"born":"1993/08/13"}
    {"index":{"_id":2}}
    {"first":"sean","last":"monohan","goals":[7,54,26],"assists":[11,26,13],"gp":[26,82,82],"born":"1994/10/12"}
    {"index":{"_id":3}}
    {"first":"jiri","last":"hudler","goals":[5,34,36],"assists":[11,62,42],"gp":[24,80,79],"born":"1984/01/04"}
    {"index":{"_id":4}}
    {"first":"micheal","last":"frolik","goals":[4,6,15],"assists":[8,23,15],"gp":[26,82,82],"born":"1988/02/17"}
    {"index":{"_id":5}}
    {"first":"sam","last":"bennett","goals":[5,0,0],"assists":[8,1,0],"gp":[26,1,0],"born":"1996/06/20"}
    {"index":{"_id":6}}
    {"first":"dennis","last":"wideman","goals":[0,26,15],"assists":[11,30,24],"gp":[26,81,82],"born":"1983/03/20"}
    {"index":{"_id":7}}
    {"first":"david","last":"jones","goals":[7,19,5],"assists":[3,17,4],"gp":[26,45,34],"born":"1984/08/10"}
    {"index":{"_id":8}}
    {"first":"tj","last":"brodie","goals":[2,14,7],"assists":[8,42,30],"gp":[26,82,82],"born":"1990/06/07"}
    {"index":{"_id":39}}
    {"first":"mark","last":"giordano","goals":[6,30,15],"assists":[3,30,24],"gp":[26,60,63],"born":"1983/10/03"}
    {"index":{"_id":10}}
    {"first":"mikael","last":"backlund","goals":[3,15,13],"assists":[6,24,18],"gp":[26,82,82],"born":"1989/03/17"}
    {"index":{"_id":11}}
    {"first":"joe","last":"colborne","goals":[3,18,13],"assists":[6,20,24],"gp":[26,67,82],"born":"1990/01/30"}

    这里极其建议在练习的时候使用kibana上的Dev Tools,这个东西有多好用谁用谁知道,它可以自动补齐es的各种query语法,牛不牛?而且就像markdown一样,做到左右分屏,所见即所得。

    这里写图片描述

    用painless获取doc的值

    下面的例子中,我们通过function_score::script_score更新每个document的score。其中用到了for循环,和强类型定义int
    可以看到运行之后,_score的值,编程了goals值的sum。
    这里写图片描述

    以下是更多的取值的例子:

    GET hockey/_search
    {
      "query": {
        "match_all": {}
      },
      "script_fields": {
        "total_goals": {
          "script": {
            "lang": "painless",
            "inline": "int total = 0; for (int i = 0; i < doc['goals'].length; ++i) { total += doc['goals'][i]; } return total;"
          }
        }
      }
    }
    GET hockey/_search
    {
      "query": {
        "match_all": {}
      },
      "sort": {
        "_script": {
          "type": "string",
          "order": "asc",
          "script": {
            "lang": "painless",
            "inline": "doc['first.keyword'].value + ' ' + doc['last.keyword'].value"
          }
        }
      }
    }

    这里需要注意几点:

    • 这里都是_search操作,多个操作之间会形成管道,既query::match_all的输出会作为script_fields或者sort的输入。
    • _search操作中所有的返回值,都可以通过一个map类型变量doc获取。和所有其他脚本语言一样,用[]获取map中的值。这里要强调的是,doc只可以在_search中访问到。在下一节的例子中,你将看到,使用的是ctx
    • _search操作是不会改变document的值的,即便是script_fields,你只能在当次查询是能看到script输出的值。
    • doc['first.keyword']这样的写法是因为doc[]返回有可能是分词之后的value,所以你想要某个field的完整值时,请使用keyword

    通过painless更新对象值

    上一节讲了如何读取值,在读取值的时候,query的response虽然能够读到一个新值,但这个值并没有写入document当中。要更新值,需要通过_updateAPI。

    单条记录更新

    如下图,通过_updateAPI的script,我们可以增加一个新的field:nick:
    这里写图片描述
    可以更改一个值:
    这里写图片描述

    这里需要注意的是:我们不再使用doc来访问对象,而是用ctx。

    批量更新

    在大多数应用场景下,我们是很少用到_updateAPI的。(当然,这里不包括你用python, java等语言用for循环多条更新)。在批量更新的场景,我推荐的是_update_by_queryAPI。

    这里写图片描述

    这里需要注意的是:

    • 虽然_update_by_queryAPI在批量更新时,和我们第一个例子很像,先query,再update,通过管道修改全部的值。但这里仍然只能用ctx,而不是doc。如果用doc,会抛出nullpointerException。
    • 另外,在例子中,我用的是match_all query,但实际上你可以用各种query,来划定精确的query范围,只修改你想修改的值。

    Dates

    Date类型的field会被解析成ReadableDateTime。所以它可以支持 getYear, getDayOfWeek等方法。 例如,要取milliseconds,就屌用getMillis方法。下面的例子,取每个球员是哪年出生的:
    这里写图片描述

    展开全文
  • elasticsearch painless脚本

    千次阅读 2019-04-23 11:46:45
    来源: ...https://www.elastic.co/guide/en/elasticsearch/painless/6.5/painless-exampl...

    来源: https://www.elastic.co/guide/en/elasticsearch/reference/6.5/modules-scripting-fields.html#_search_and_aggregation_scripts
    https://www.elastic.co/guide/en/elasticsearch/painless/6.5/painless-examples.html
    前半部分用的es版本是5.5,script中是inline字段
    后半部分用的es版本是6.5,script中是source字段

    脚本

    语言 沙盒 所需插件
    painless yes 内置
    groovy no 内置
    javascript no lang-javascript
    python no lang-javascript
    语言 沙盒 所需插件

    本文只介绍painless和JavaAPI

    语法模式

     "script": {
     	"lang": "...", (1)
    	"inline" | "stored" | "file": "...", (2)
    	"params": { ... } (3)
    }
    

    (1)写入脚本的语言,默认为painless。
    (2)脚本可以指定为 inline, stored, or file.
    (3)传递给脚本的命名参数。

    简单实例测试:

    PUT my_index/my_type/1
    {
    	"my_field": 5
    }
    
    GET my_index/_search
    {
      "script_fields": {
        "my_doubled_field": {
          "script": {
            "lang": "expression",
            "inline": "doc['my_field'] * multiplier",
            "params": {
              "multiplier": 2
            }
          }
        }
      }
    }
    //结果
    {
      "took": 176,
      "timed_out": false,
      "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
      },
      "hits": {
        "total": 1,
        "max_score": 1.0,
        "hits": [
          {
            "_index": "my_index",
            "_type": "my_type",
            "_id": "1",
            "_score": 1.0,
            "fields": {
              "my_doubled_field": [
                10.0
              ]
            }
          }
        ]
      }
    }
    

    参数

    lang

    指定脚本写入的语言,你可以设置任何脚本语言,可以通过改变config 文件下 elasticsearch.yml 设置成script.default_lang 合适的语言

    inline, stored, file

    指定脚本的源 inline脚本入上面eg所示 stored 指定存储在集群(see Stored Scripts 超连接)并从config / scripts目录中的文件检索文件脚本(请参阅文件脚本)。
    虽然众多表达式和painless 语言可以作为inline或存储的脚本配置即可用,但其他例如groovy的语言只能被指定为文件,除非您首先调整默认的脚本安全设置。

    params

    指定作为变量传递到脚本中的任何命名参数
    ps:如果您需要将变量传递到脚本中,那么您应该将它们作为命名参数传递,而不是将其转换为脚本本身的编码

    //错误写法!!!!!
    "inline": "doc['my_field'] * 2"
    //命名参数传递:
    "inline": "doc['my_field'] * multiplier",
    "params": {
    "multiplier": 2
    }
    

    脚本存储

    脚本可以使用_scripts终点存储在集群状态并从集群状态中检索。

    自动脚本重载

    脚本目录将每60秒重新扫描一次(可以使用resource.reload.interval设置进行配置),新脚本,已更改或已删除的脚本将从脚本缓存中进行编译,更新或删除。
    脚本重新加载可以通过将script.auto_reload_enabled设置为false来完全禁用。

    弃用的命名空间

    使用lang和id作为唯一标识符的存储脚本的命名空间已被弃用。存储脚本的新命名空间只能使用id。存储的脚本具有相同的id,但不同的lang的将不再允许在6.0。要遵守存储脚本的新命名空间,现有存储的脚本应该被删除并重新放置。任何共享id但具有不同“lang”的脚本将需要重新命名。例如,请执行以下操作:
    “id”:“example”,“lang”:“painless”“id”:“example”,“lang”:“表达式”
    上述脚本将在新的命名空间下冲突,因为id是相同的。至少有一个必须被重新命名才能遵守id的新命名空间。
    作为最后的注意事项,存储的搜索模板和存储的脚本共享相同的命名空间,因此,如果搜索模板与存储的脚本具有相同的标识,则必须使用delete和put请求重新命名其中的一个。

    /_scripts/{id}
    备注:id为唯一值
    
    //在群集状态中存储一个名为calculate-score的 painless 脚本:
    POST _scripts/calculate-score
    {
        "script": {
        "lang": "painless",
        "code": "Math.log(_score * 2) + params.my_modifier"
        }
    }
    //获取刚刚存储的脚本
    GET _scripts/calculate-score
    //删除脚本
    DELETE _scripts/calculate-score
    

    查询聚合脚本

    除了每个搜索命中执行一次的脚本字段外,搜索和聚合中使用的脚本将针对可能与查询或聚合匹配的每个文档执行一次。 根据你有多少文件,这可能意味着数百万或数十亿的执行:这些脚本需要快!
    可以使用文档值或存储的字段或_source字段从脚本访问字段值,这些位置如下所述。
    脚本还可以访问文档的相关性_score,并通过实验_index变量访问高级文本评分的术语统计信息。
    访问score(评分)文档的脚本
    在function_score查询,基于脚本的排序或聚合中使用的脚本可以访问表示文档当前相关性分数的_score变量。
    以下是使用function_score查询中的脚本更改每个文档的相关性_score的示例:

    PUT my_index/my_type/1
    {
      "text": "quick brown fox",
      "popularity": 1
    }
    
    PUT my_index/my_type/2
    {
      "text": "quick fox",
      "popularity": 5
    }
    
    GET my_index/_search
    {
      "query": {
        "function_score": {
          "query": {
            "match": {
              "text": "quick brown fox"
            }
          },
          "script_score": {
            "script": {
              "lang": "expression",
              "inline": "_score * doc['popularity']"
            }
          }
        }
      }
    }
    

    以下是自己翻译的,如有问题希望指正!

    doc值和文本字段(Doc values and text fields)

    The doc[‘field’] syntax can also be used for analyzed text fields if fielddata is enabled,
    doc[‘field’]这样的语法同样也可以在fielddata允许的情况下对分析文本字段使用

    but BEWARE: enabling fielddata on a text field requires loading all of the terms into the JVM heap, which can be very expensive both in terms of memory and CPU. It seldom makes sense to access text fields from scripts.
    但是注意:允许fielddata在text字段需要将所有的项都载入JVM堆中,这样就会消耗大量的cpu和内存。所以很少从脚本中获得text字段是有道理的

    存储字段和_source

    Stored fields — fields explicitly marked as “store”: true — can be accessed using the_fields[‘field_name’].value or _fields[‘field_name’].values syntax.
    存储字段—指那些明确被标记“store”:true的字段----可以使用_fields[‘field_name’].value或者是_fields[‘field_name’].values这样的语法来获取字段的值

    The document _source, which is really just a special stored field, can be accessed using the _source.field_name syntax. The _source is loaded as a map-of-maps, so properties within object fields can be accessed as, for example, _source.name.first.
    文档的_source只是一个特殊的存储字段,可以通过_source.field_name 来使用,_source会被载入成一个map-of-maps,所以对象字段的内容可以使用下面的形式来获取:_source.name.first

    ps:Prefer doc-values to stored fields
    更倾向使用doc-values而不是存储字段

    They are optimised for returning several fields per result, while doc values are optimised for accessing the value of a specific field in many documents.
    存储字段优化用于返回每个结果几个字段,而doc values优化用于获得一个在多个文档中的特定的字段

    It makes sense to use _source or stored fields when generating a script field for the top ten hits from a search result but, for other search and aggregation use cases, always prefer using doc values.
    当生成脚本字段去命中最优的10结果使用_source或者存储字段都是可以的,但是对于其他搜索和聚合使用案例,总是更倾向去使用doc-values

    PUT my_index
    {
      "mappings": {
        "_doc": {
          "properties": {
            "title": { // 1、这个字段没有明确被指明是stored并且不能使用_fields[]语法获取
              "type": "text"
            },
            "first_name": {
              "type": "text",
              "store": true
            },
            "last_name": {
              "type": "text",
              "store": true
            }
          }
        }
      }
    }
    
    PUT my_index/_doc/1?refresh
    {
      "title": "Mr",
      "first_name": "Barry",
      "last_name": "White"
    }
    
    GET my_index/_search
    {
      "script_fields": {
        "source": {
          "script": {
            "lang": "painless",
            //title字段仍然可以通过_source获取
            "source": "params._source.title + ' ' + params._source.first_name + ' ' + params._source.last_name" 
          }
        },
        "stored_fields": {
          "script": {
            "lang": "painless",
            "source": "params._fields['first_name'].value + ' ' + params._fields['last_name'].value"
          }
        }
      }
    }
    

    tip:Stored vs _source
    The _source field is just a special stored field, so the performance is similar to that of other stored fields.
    _source字段只是一个特殊的存储字段,因此表现和其他的存储字段非常相似。

    The _source provides access to the original document body that was indexed (including the ability to distinguish null values from empty fields, single-value arrays from plain scalars, etc).
    _source字段提供了获取那些被索引的原始文件体的方法(包括能够从空字段,单一值数组中区别null值的能力等等)

    The only time it really makes sense to use stored fields instead of the _source field is when the _source is very large and it is less costly to access a few small stored fields instead of the entire _source.
    唯一值得使用存储字段代替_source字段是在当_source非常大而且获取少量的存储字段代替整个_source这样的做法更廉价

    简单的脚本

    //简单形式
    "script": "ctx._source.likes++"
    //标准形式
    "script": {
        "source": "ctx._source.likes++"
    }
    

    脚本转化时间示例

    //转化时间
    {
      "size": 1, 
      "script_fields": {
        "birth_year": {
          "script": {
            "inline": "DateTimeFormatter dtf = DateTimeFormatter.ofPattern('yyyy-MM-dd HH:mm:ss');LocalDateTime ldt1 =LocalDateTime.parse(doc['public_data'].value,dtf);LocalDateTime ldt2 = LocalDateTime.parse(doc['create_data'].value,dtf);return Duration.between(ldt1,ldt2).toDays(); "
          }
        }
      }
    }
    
    //filter查询 脚本
    GET evening/_search
    {
        "query": {
            "bool" : {
                "filter" : {
                    "script" : {
                        "script" : {
                            "source" : "doc['sold'].value == false && doc['cost'].value < params.cost",
                            "params" : {
                                "cost" : 18
                            }
                        }
                    }
                }
            }
        }
    }
    

    Java脚本聚合:

            Script allWeightScript = new Script(ScriptType.INLINE, "painless",
                    "Integer.parseInt(doc['meterage_weight'].value)",//将meterage_weight的值(原值为keyword)转成int
                    Collections.emptyMap());
    
            AggregationBuilder allWeightAgg = AggregationBuilders.filter("allTicket", QueryBuilders.rangeQuery("meterage_weight").from("0")).subAggregation(
                    AggregationBuilders.sum("allWeight").script(allWeightScript)
            );
    
            Script weightUp20KGScript = new Script(ScriptType.INLINE, "painless",
                    "int weight = Integer.parseInt(doc['meterage_weight'].value);if(weight>=20){return weight;}else{return 0;}",//只计算大于20的meterage_weight
                    Collections.emptyMap());
    
    		AggregationBuilder weightUp20KGAgg = AggregationBuilders.filter("up20KGTicket", QueryBuilders.rangeQuery("meterage_weight").from("20"))
                    .subAggregation(AggregationBuilders.sum("up20KGWeight").script(allWeightScript));
    
    展开全文
  • 写在前面 painless是一个较新的脚本语言,毕竟不是一加一等于二那么简单,开始不懂是很正常的,如果看不懂 请看第二遍第三遍乃至N次 相信我 一定能看得懂的,书读百遍,其义自见 es5以上版本推出了简单安全快捷的...

    • 写在前面    
    • painless是一个较新的脚本语言,毕竟不是一加一等于二那么简单,开始不懂是很正常的,如果看不懂 请看第二遍第三遍乃至N次  相信我 一定能看得懂的,书读百遍,其义自见

     es5以上版本推出了简单安全快捷的painless脚本来替代原有的一些脚本语言,最近正好需要过滤查询一些逻辑相对复杂的数据并对原有的groovy脚本进行升级,所以对painless进行了学习,发现网上对这个脚本的说明非常少, 官网有英文版的说明,所以特将学习结果分享出来。

         painless安全且高效,书写方法和java类似,安全是因为painless提供了一个方法的白名单(链接:painless支持的类),即链接地址里的所有类和方法,除了这些方法以外的所有方法都不允许使用,而不是像groovy一样 提供一个较浅的沙盒很容易被利用(groovy漏洞分析),从而保证了安全性;高效是因为painless是由es团队自己开发的脚本,且不支持重载方法,从而保证了高效性(不支持重载方法,当一个def动态参数被定义立即就能获得这个参数对应的静态类,而不需要一个一个去遍历所有的重载方法,这正是比groovy高效的地方),且无需安装其他插件,即写即用,容易上手。

          以下为es+painless脚本筛选数据举例及分析:

    • 举个例子

    首先设想一个场景:

    一个球队,评选最有潜力球员,简单按进球数多少排序很好实现。但助攻和普通进攻也可以作为一个评选指标,最后需求为进球数权重5,助攻权重3,普通进攻权重2的方式为球员排名这怎么排序呢?单单靠dsl写起来很复杂。可用脚本定义好逻辑,让es去调用返回结果即可

    准备一个球队的数据,配置好elasticsearch,并启动

    工具:postman(这是一个可以模拟请求的url工具,且可返回结果)

    在postman中按下图输入地址:127.0.0.1:8200/football/player/_bulk?refresh

    选中Body-->raw-->JSON……

    并在Body中键入球队的队员信息内容(球队信息内容来自网络,侵删):

    {"index":{"_id":1}} 

    {"first":"johnny","last":"gaudreau","goals":[9,27,1],"assists":[17,46,0],"gp":[26,82,1],"born":"1993/08/13"} 
    {"index":{"_id":2}} 
    {"first":"sean","last":"monohan","goals":[7,54,26],"assists":[11,26,13],"gp":[26,82,82],"born":"1994/10/12"} 
    {"index":{"_id":3}} 
    {"first":"jiri","last":"hudler","goals":[5,34,36],"assists":[11,62,42],"gp":[24,80,79],"born":"1984/01/04"} 
    {"index":{"_id":4}} 
    {"first":"micheal","last":"frolik","goals":[4,6,15],"assists":[8,23,15],"gp":[26,82,82],"born":"1988/02/17"} 
    {"index":{"_id":5}} 
    {"first":"sam","last":"bennett","goals":[5,0,0],"assists":[8,1,0],"gp":[26,1,0],"born":"1996/06/20"} 
    {"index":{"_id":6}} 
    {"first":"dennis","last":"wideman","goals":[0,26,15],"assists":[11,30,24],"gp":[26,81,82],"born":"1983/03/20"} 
    {"index":{"_id":7}} 
    {"first":"david","last":"jones","goals":[7,19,5],"assists":[3,17,4],"gp":[26,45,34],"born":"1984/08/10"} 
    {"index":{"_id":8}} 
    {"first":"tj","last":"brodie","goals":[2,14,7],"assists":[8,42,30],"gp":[26,82,82],"born":"1990/06/07"} 
    {"index":{"_id":39}} 
    {"first":"mark","last":"giordano","goals":[6,30,15],"assists":[3,30,24],"gp":[26,60,63],"born":"1983/10/03"} 
    {"index":{"_id":10}} 
    {"first":"mikael","last":"backlund","goals":[3,15,13],"assists":[6,24,18],"gp":[26,82,82],"born":"1989/03/17"} 
    {"index":{"_id":11}} 

    {"first":"joe","last":"colborne","goals":[3,18,13],"assists":[6,20,24],"gp":[26,67,82],"born":"1990/01/30"} 

    查询进球总数大于50个的球员信息:

    核心代码为:

    "script":{
        	"inline":"int total = 0; for (int i = 0; i < doc['goals'].length; ++i) { total += doc['goals'][i]; }  if(total>50) return total;",
        	"lang":"painless"
        	}

    其中inline表示脚本写在查询语句内,"lang":"painless"表示脚本语言为painless

    全部查询语句见下图


    多条件联合查询:查询普通进攻总数(gp)大于50且进球数大于50,且firstname中包含‘sean’的球员信息

    {
      
      "query": {
         "bool":{
        	"must":{
        		"script":{
        			"script":{
        				"inline":"int total = 0; for (int i = 0; i < doc['gp'].length; ++i) { total += doc['gp'][i]; }  if(total>30) return total;",
        				"lang":"painless"
        			},
        			"script":{
        				"inline":"int total = 0; for (int i = 0; i < doc['goals'].length; ++i) { total += doc['goals'][i]; }  if(total>50) return total;",
        				"lang":"painless"
        			},
        			"script":{
        				"inline":"if('sean'.equals(doc['first.keyword'].value))   return true;",
        				"lang":"painless"
        			}
        		}
        	}
        }
      }

    其实简单来说就是在inline内输入过滤语句 lang 后面加入脚本名称即可

    但是如果查询条件相当复杂,需要多种判断,循环才能组成过滤条件,用inline的方式编写脚本显然不适合,那就需要将脚本代码逻辑放在file文件内(文件后缀为.painless),将file放在es安装目录下的\config\scripts文件夹下(如下图),然后调用,方式如下

     "script": {
            "lang": "painless",
            "file": "football_score"//这里将inline改为file   后面跟脚本名称football_score
          }


    football_score的逻辑为:

    进球数权重5,助攻权重3,普通进攻权重2返回总得分


    • 脚本化field

    在上面的查询中,只能查询出相应结果,但是如果想要对某些字段进行重新处理,比如对进球数汇总,将first\last合并为全名这时就不光只用到查询了,还需要对field进行脚本化

    就像下面这样

    执行后的结果


    全部过程如下


    脚本化field完成,还有一个问题,这些都知识针对固定的字段得出的结果,如果要传参怎么办,我想传一个外部的信息,然后和es中的数据做匹配,上面的内容又支持不了了,所以接着看下面吧

    • painless传参    

    核心脚本代码


    totalgoalbs.painless文件中写法:params.param1为参数值,return 0或false时过滤,  1或true匹配


    当然也可以传递多个参数

    甚至传递list,只要后台写好解析就行了,像这样

     

    在painless中写好自己的解析逻辑即可

    • java+es+painless   先看一下java调用painless的api是怎么写的

    inline方式的脚本,在java中直接用上面的new Script即可

    但是java调用 一般都是要传递好多参数的,所以一般还是把painless写进file里


    api中的写法如上,当然 还是有人不懂是怎么用的 

    我来举个例子,比如我要对一些数据匹配ip,筛选出相应的ip

    java部分

    Map<String, Object> params = new HashMap<String, Object>();//存放参数的map
    params.put("resourceMapList", resourceMapList);//其他一些匹配信息List<Map<isnot;ip>>,isnot及ip的含义见下面脚本代码
    params.put("fieldName", fieldName);//ip对应的字段名称
    Script script =new Script(ScriptType.FILE, "painless", "function_ip_resources",params);//脚本文件名称,脚本类型
    ScriptQueryBuilder filterBuilder = QueryBuilders.scriptQuery(script);//创建scriptquery
    QueryBuilder q=QueryBuilders.boolQuery().filter(filterBuilder); //将scriptQueryc存入过滤条件

    painless脚本:名称为function_ip_resources.painless

    def ipStand = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
    def flag = false;//是否匹配标记
    def resourceMapList=params.resourceMapList;//其他匹配信息
    String filedName=params.fieldName;//ip对应的字段名称,这个字段类型用的是keyword
    String filedIp=doc[filedName+'.keyword'].value;//取值
    if(!(ipStand.matcher(filedIp).matches())){//正则匹配ipv4
    	return false;//不符合ip格式 直接返回false
    }else{
    	for(ipResMap in resourceMapList){ //遍历其他匹配信息
    		def isNot = ipResMap.get('isnot');//取反参数,isnot为true:取与下面一行ip不匹配的所有ip  ;isnot为false 取与下面一行ip相同的所有ip
    		String leftVal = ipResMap.get('ip'); //匹配值
    		if(filedIp.equals(leftVal)){
    			//如果不是取反,filedIp与IP参数相同表明匹配成功
    			if("false".equals(isNot)){
    			    flag=true;
    			} 
    			}else{
    				//取反,ip不包含在ip参数内时匹配成功
    				 if(!("false".equals(isNot))){
    				  flag=true;
    				 } 
    		} 
    	} 
    }
     
    return flag;
    

    执行后可正常筛选出符合条件的ip,上面那部分只是应用script部分的代码,至于如何连接es(elastisearch之java api Transportclient创建连接),如何查询或操作就是另一部分的学习内容了,如果有问题 欢迎留言~

     


    展开全文
  • painless 简介

    2019-01-25 14:02:27
    painless是一种为安全和性能而设计的脚本语言。 语法与Java语法类似,可用于所有可以使用script的场景下。 painless提供: 快速性能:脚本的运行速度比其他脚本快几倍。 安全性:具有方法调用/字段粒度的细粒度白...
  • elasticsearch painless 用法

    千次阅读 2019-09-28 16:36:06
    我们一般在添加一个需要计算后的字段或者通过某种规则计算后的值来排序是会使用到elasticsearch的脚本功能,es在5.x版本以后默认使用的就是painless无痛脚本 script_fields用法: sort用法: 二、painless...
  • elasticsearch painless脚本评分

    千次阅读 2018-08-30 15:34:31
    painless是一种新支持的脚本语言,语言格式和java十分类似。可以参考以下文档: painless语言介绍 painless api painless 实例 脚本参数 score_mode计算functions中的分数形式,加减乘除,boost_...
  • Elasticsearch: Painless script编程

    千次阅读 2019-09-06 11:43:04
    我们之前看见了在Elasticsearch里的ingest node里,我们可以通过以下processor的处理帮我们处理我们的一些数据。...在Elasticsearc中,它使用了一个叫做Painless的语言。它是专门为Elasticsearch而建立...
  • Painless execute API 允许执行任意脚本并返回结果。请注意的是:这个 API 是新的,请求方式及响应在未来可能会有所改变。... 唯一可用的变量是 params,可用于访问用户定义的值。 脚本的结果总是转.
  • #########Demo for Painless############### Painless 简介 ● ⾃ Elasticsearch 5.x 后引⼊,专⻔为 Elasticsearch 设计,扩展了 Java 的语法。 ● 6.0 开始,ES 只⽀持 Painless。Groovy, JavaScript 和 Python ...
  • ElasticSearch Painless 脚本 简单使用

    千次阅读 2019-08-06 18:59:26
    ElasticSearch Painless 脚本 简单使用 数据准备 使用for循环---改变搜索评分 使用for循环---对数组求和 对拼接字段排序 使用脚本更新文档 使用脚本添加新字段 使用脚本对日期操作 正则表达式使用 1、数据...
  • The script is built for painless language. However, I always get the runtime error. <p>I cannot get this to work, what could I be doing wrong? <pre><code> 'function_score' => [ 'query' => $...
  • Elasticsearch:Painless scripting 高级编程

    千次阅读 2020-02-08 14:05:52
    它还涵盖了一些最佳实践,例如,为什么使用参数,何时访问文档字段时何时使用“doc”值而不是“ _source”以及如何动态创建字段等。 之前的文章: Elasticsearch:Painless scripting Elasticsearch: Painless ...
  • ElasticSearch scripted_metric 去重计数脚本计算高效率解决问题(二) 11月12日解决问题记录 计数后排序问题暂未解决 话不多说直接贴代码 : init_script 定义 map_script 操作 判断 combine_script 操作返回记录 ...
  • Elasticsearch脚本使用

    2019-12-14 15:10:07
    Elasticsearch默认脚本语言为Painless。其他lang插件使您可以运行以其他语言编写的脚本。 语言 沙盒 是否需要插件 备注 painless 是 内置的 默认脚本语言 expression 是 内置的 快速的自定义排名和排序 ...
  • elasticsearch painless最强教程 二

    万次阅读 2017-09-29 16:37:54
    painless的正则匹配 painless查找操作符 painless匹配操作符 painless pattern matcher painless CharSequence 在上一篇文章elasticsearch painless最强教程中,已经介绍了一些painless的基本例子,喜欢动脑子的...
  • es(elasticsearch)5.4下params参数的使用

    千次阅读 2017-06-08 14:08:28
    参考《ES权威指南》时,用到类似于下面的参数进行修改文档的属性时,遇到了问题。报错信息:Variable [new_tag] is not defined.
  • 菜鸡一只,国庆真是玩了好几天,等到快上班的时候才开始又学习,找状态 本文来讲讲ES中的Join方案! 在数据库中,join是非常常见的操作! 其实就是将两张表的数据合并到一起,然后查询出结果数据,当然最后可能还...
  • 由于网上没有找到5.5版本的资料,很多资料都是2.x版本的,更新时语法差异较大,所以我这里翻译一下官方文档的这一部分内容。翻译时,示例代码均替换为我实践中的真实代码,这样确保了本文档的准确性。...
  • (四)ElasticSearch修改数据 方式:直接修改、通过脚本修改 直接修改 (1)修改id为1的数据 (2)查看 脚本修改 ... "inline":"ctx._source.age += 10" } } 其它方式注入参数 { "script":{ "la...
  • 这里lang默认的值为"painless"。在实际的使用中可以不设置,除非有第二种语言供使用 source可以为inline脚本,或者是一个id,那么这个id对应于一个stored脚本 任何有名字的参数,可以被用于脚本的输入参数
  • Elasticsearch操作

    2015-04-20 19:12:29
    {  "script": "ctx._source.publish_count += count",  "params": {  "count": 2  },  "lang": "groovy" } 不存在记录时,默认值 ... "script": "ctx._source.remove(\"params\")",  "lang":
  • 首先,文档格式 { "_index" : "test_index", "_type" : "_doc", "_id" : "1", ... "_source" : { "id" : "1", "name" : "哈哈哈", "pageView" : 0, "personCerts" : [ { "certNo" : "333
  • 0、题记除了官方文档,其他能找到的介绍Elasticsearch脚本(Scripting)的资料少之又少。一方面:性能问题。官方文档性能优化中明确指出使用脚本会导致性能低...
  • 在上一篇mongo中提过,我们有增量更新的需求,mongo因为深层嵌套,更新麻烦,于是采用先删除后新增的方法。同样的,es也存在深层嵌套的数据。这部分数据同样不易更新,也是在开始的时候尝试过,虽然后来和mongo同样...
  • Elasticsearch Painless Script入门教程

    千次阅读 2020-03-22 21:07:30
    Elasticsearch Painless Script入门教程 前面几篇文章已经陆续提到了Elasticsearch 脚本,但总感觉不够系统,本文带你系统地学习下Painless Script。 Painless 脚本介绍 自Elasticsearch 5.x 引入Painless,使得...
  • Elasticsearch:Painless 编程调试

    千次阅读 2020-08-31 10:54:12
    Painless 也就是无痛的意思。这是一个专为 Elasticsearch 而设计的。当初的设计人员取名为 “Painless”,表达的意思的是在编程的时候没有疼痛感,很方便设计人员使用。由于这是一个脚本的语言,在实际的使用中,...
  • Kibana 提供了一些强大的方法,用于搜索和可视化 Elasticsearch 中存储的数据。为了实现可视化,Kibana 会搜索 Elasticsearch mapping 中定义的 field,并以图表的形式将它们作为选项呈现给用户。...
  • Kibana 提供了一些强大的方法,用于搜索和可视化 Elasticsearch 中存储的数据。为了实现可视化,Kibana 会搜索 Elasticsearch mapping 中定义的 field,并以图表的形式将它们作为选项呈现给用户。...
  • ElasticSearch 条件更新 删除

    千次阅读 2018-07-19 13:00:32
    ElasticSearch根据匹配某个条件,局部更新文档 首先声明版本为ES 6.0。 index中有很多文档,要更新这些文档中符合某个条件的所有documents,可以使用ES的_update_by_query的及脚本方式完成:POST请求:... { ......

空空如也

1 2 3 4 5 ... 20
收藏数 68,474
精华内容 27,389
关键字:

painless