ElasticSearch笔记-脚本(script)


使用场景

用于解决复杂业务问题,如:自定义字段、自定义评分、自定义更新、自定义聚合分析等

缺点

性能问题。官方文档性能优化中明确指出使用脚本会导致性能低;
如非必要,不要使用脚本,尽量用其他方式替换,如下:
使用脚本进行前缀查询:

 1POST seats/_search
 2{
 3 "query": {
 4  "bool":{
 5    "filter": { 
 6    "script":{
 7      "script":{
 8        "lang":"painless",
 9        "source": "doc['theatre'].value.startsWith('Down')"
10      }
11    }
12    }
13  }
14 }
15}

可以使用prefix前缀匹配,性能提升5倍

script模板

"script": {
    "lang":   "...",  
    "source" | "id": "...", 
    "params": { ... } 
}

lang:代表language脚本语言,默认指定为:painless。
source:脚本的核心部分,id应用于:stored script。
params:传递给脚本使用的变量参数。

Painless Scripting 简介

Painless是一种简单,安全的脚本语言,专为与Elasticsearch一起使用而设计。从ES5.0开始,它是Elasticsearch的默认脚本语言,可以安全地用于内联和存储脚本。
Painless特点:
性能牛逼:Painless脚本运行速度比备选方案(包括Groovy)快几倍。
安全性强:使用白名单来限制函数与字段的访问,避免了可能的安全隐患。
可选输入:变量和参数可以使用显式类型或动态def类型。
上手容易:扩展了java的基本语法,并兼容groove风格的脚本语言特性。
特定优化:是ES官方专为Elasticsearch脚本编写而设计。

存储脚本

脚本可以使用_scripts端点存储在集群状态中并从集群状态检索。
下面是使用位于/_scripts/{id}的存储脚本的示例。
首先,在集群状态下创建名为calculate-score的脚本:

POST _scripts/calculate-score
{
  "script": {
    "lang": "painless",
    "source": "Math.log(_score * 2) + params.my_modifier"
  }
}

同样的获取脚本可以用:

GET _scripts/calculate-score

存储的脚本可以通过如下方式指定id参数来使用:

GET _search
{
  "query": {
    "script": {
      "script": {
        "id": "calculate-score",
        "params": {
          "my_modifier": 2
        }
      }
    }
  }
}

删除脚本

DELETE _scripts/calculate-score

案例

自定义字段

返回原有Mapping未定义的字段值。
1.1、以idplus返回id字段的翻倍后的结果。

GET index6/_search
{
  "script_fields": {
    "idplus": {
      "script": {
        "lang": "expression",
       "source": "doc['id'] * multiplier",
         "params": {
           "multiplier": 2
        }
      }
    }
  }
}

结果

{
省略...
    "hits" : [
      {
        "_index" : "index6",
        "_type" : "_doc",
        "_id" : "2001",
        "_score" : 1.0,
        "fields" : {
          "idplus" : [
            4002.0
          ]
        }
      },
省略...
    ]
}

1.2、返回日期字段中的“年”或“月”或“日”等。

 1GET hockey/_search
 2{
 3  "script_fields": {
 4    "birth_year": {
 5      "script": {
 6        "source": "doc.born.value.year"
 7      }
 8    }
 9  }
10}

自定义评分

1GET my_index/_search
 2{
 3  "query": {
 4    "function_score": {
 5      "query": {
 6        "match": {
 7          "text": "quick brown fox"
 8        }
 9      },
10      "script_score": {
11        "script": {
12          "lang": "expression",
13          "source": "_score * doc['popularity']"
14        }
15      }
16    }
17  }
18}

自定义更新

1.1、_update:将已有字段值赋值给其他字段。

 POST index6/_update/1
 {
   "script": {
     "lang": "painless",
     "source": """
       ctx._source.firstName = params.firstName;
       ctx._source.lastName = params.lastName
     """,
     "params": {
      "firstName": "aa",
      "lastName": "bb"
    }
  }
}

1.2、Update_by_query:满足b开头(注意正则)的字段,末尾添加matched。

 1POST hockey/_update_by_query
 2{
 3  "script": {
 4    "lang": "painless",
 5    "source": """
 6      if (ctx._source.last =~ /b/) {
 7        ctx._source.last += "matched";
 8      } else {
 9        ctx.op = "noop";
10      }
11    """
12  }
13}

自定义reindex

Elasticsearch认证考试题:
有index_a包含一些文档, 要求创建索引index_b,通过reindex api将index_a的文档索引到index_b。
要求:
1)增加一个整形字段,value是index_a的field_x的字符长度;
2)再增加一个数组类型的字段,value是field_y的词集合。
(field_y是空格分割的一组词,比方"foo bar",索引到index_b后,要求变成["foo", "bar"])

1POST _reindex
 2{
 3  "conflicts": "proceed",
 4  "source": {
 5    "index": "index_a"
 6  },
 7  "dest": {
 8    "index": "index_b"
 9  },
10  "script": {
11    "source": "ctx._source.parts = / /.split(ctx._source.address); ctx._source.tag = ctx._source.city.length();"
12  }
13}

自定义聚合

 1GET /_search
 2{
 3    "aggs" : {
 4        "genres" : {
 5            "terms" : {
 6                "script" : {
 7                    "source": "doc['genre'].value",
 8                    "lang": "painless"
 9                }
10            }
11        }
12    }
13
14}