[[modules-scripting-painless]]
=== Painless Scripting Language

experimental[The Painless scripting language is new and is still marked as experimental. The syntax or API may be changed in the future in non-backwards compatible ways if required.]

_Painless_ is a simple, secure scripting language available in Elasticsearch
by default. It is designed specifically for use with Elasticsearch and can
safely be used with `inline` and `stored` scripting, which is enabled by
default.

The Painless syntax is similar to http://groovy-lang.org/index.html[Groovy].

You can use Painless anywhere a script can be used in Elasticsearch--simply set the `lang` parameter
to `painless`.

[[painless-features]]
[float]
== Painless Features

* Fast performance: https://benchmarks.elastic.co/index.html#search_qps_scripts[several times faster] than the alternatives.

* Safety: Fine-grained <<painless-api, whitelist>> with method call/field granularity.

* Optional typing: Variables and parameters can use explicit types or the dynamic `def` type.

* Syntax: Extends Java's syntax with a subset of Groovy for ease of use. See the <<modules-scripting-painless-syntax, Syntax Overview>>.

* Optimizations: Designed specifically for Elasticsearch scripting.

[[painless-examples]]
[float]
== Painless Examples

To illustrate how Painless works, let's load some hockey stats into an Elasticsearch index:

[source,js]
----------------------------------------------------------------
PUT hockey/player/_bulk?refresh
{"index":{"_id":1}}
{"first":"johnny","last":"gaudreau","goals":[9,27,1],"assists":[17,46,0],"gp":[26,82,1]}
{"index":{"_id":2}}
{"first":"sean","last":"monohan","goals":[7,54,26],"assists":[11,26,13],"gp":[26,82,82]}
{"index":{"_id":3}}
{"first":"jiri","last":"hudler","goals":[5,34,36],"assists":[11,62,42],"gp":[24,80,79]}
{"index":{"_id":4}}
{"first":"micheal","last":"frolik","goals":[4,6,15],"assists":[8,23,15],"gp":[26,82,82]}
{"index":{"_id":5}}
{"first":"sam","last":"bennett","goals":[5,0,0],"assists":[8,1,0],"gp":[26,1,0]}
{"index":{"_id":6}}
{"first":"dennis","last":"wideman","goals":[0,26,15],"assists":[11,30,24],"gp":[26,81,82]}
{"index":{"_id":7}}
{"first":"david","last":"jones","goals":[7,19,5],"assists":[3,17,4],"gp":[26,45,34]}
{"index":{"_id":8}}
{"first":"tj","last":"brodie","goals":[2,14,7],"assists":[8,42,30],"gp":[26,82,82]}
{"index":{"_id":39}}
{"first":"mark","last":"giordano","goals":[6,30,15],"assists":[3,30,24],"gp":[26,60,63]}
{"index":{"_id":10}}
{"first":"mikael","last":"backlund","goals":[3,15,13],"assists":[6,24,18],"gp":[26,82,82]}
{"index":{"_id":11}}
{"first":"joe","last":"colborne","goals":[3,18,13],"assists":[6,20,24],"gp":[26,67,82]}
----------------------------------------------------------------
// CONSOLE
// TESTSETUP

[float]
=== Accessing Doc Values from Painless

Document values can be accessed from a `Map` named `doc`.

For example, the following script calculates a player's total goals. This example uses a strongly typed `int` and a `for` loop.

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

Alternatively, you could do the same thing using a script field instead of a function score:

[source,js]
----------------------------------------------------------------
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;"
      }
    }
  }
}
----------------------------------------------------------------
// CONSOLE

The following example uses a Painless script to sort the players by their combined first and last names. The names are accessed using
`doc['first'].value` and `doc['last'].value`.

[source,js]
----------------------------------------------------------------
GET hockey/_search
{
  "query": {
    "match_all": {}
  },
  "sort": {
    "_script": {
      "type": "string",
      "order": "asc",
      "script": {
        "lang": "painless",
        "inline": "doc['first.keyword'].value + ' ' + doc['last.keyword'].value"
      }
    }
  }
}
----------------------------------------------------------------
// CONSOLE

[float]
=== Updating Fields with Painless

You can also easily update fields. You access the original source for a field as `ctx._source.<field-name>`.

First, let's look at the source data for a player by submitting the following request:

[source,js]
----------------------------------------------------------------
GET hockey/_search
{
  "stored_fields": [
    "_id",
    "_source"
  ],
  "query": {
    "term": {
      "_id": 1
    }
  }
}
----------------------------------------------------------------
// CONSOLE

To change player 1's last name to `hockey`, simply set `ctx._source.last` to the new value:

[source,js]
----------------------------------------------------------------
POST hockey/player/1/_update
{
  "script": {
    "lang": "painless",
    "inline": "ctx._source.last = params.last",
    "params": {
      "last": "hockey"
    }
  }
}
----------------------------------------------------------------
// CONSOLE

You can also add fields to a document. For example, this script adds a new field that contains
the player's nickname,  _hockey_.

[source,js]
----------------------------------------------------------------
POST hockey/player/1/_update
{
  "script": {
    "lang": "painless",
    "inline": "ctx._source.last = params.last; ctx._source.nick = params.nick",
    "params": {
      "last": "gaudreau",
      "nick": "hockey"
    }
  }
}
----------------------------------------------------------------
// CONSOLE

[float]
[[modules-scripting-painless-regex]]
=== Regular expressions

Painless's native support for regular expressions has syntax constructs:

* `/pattern/`: Pattern literals create patterns. This is the only way to create
a pattern in painless. The pattern inside the `/`s are just
http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html[Java regular expressions].
See <<modules-scripting-painless-regex-flags>> for more.
* `=~`: The find operator return a `boolean`, `true` if a subsequence of the
text matches, `false` otherwise.
* `==~`: The match operator returns a `boolean`, `true` if the text matches,
`false` if it doesn't.

Using the find operator (`=~`) you can update all hockey players with "b" in
their last name:

[source,js]
----------------------------------------------------------------
POST hockey/player/_update_by_query
{
  "script": {
    "lang": "painless",
    "inline": "if (ctx._source.last =~ /b/) {ctx._source.last += \"matched\"} else {ctx.op = 'noop'}"
  }
}
----------------------------------------------------------------
// CONSOLE

Using the match operator (`==~`) you can update all the hockey players who's
names start with a consonant and end with a vowel:

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

Or you can use the `Pattern.matcher` directory to get a `Matcher` instance and
remove all of the vowels in all of their names:

[source,js]
----------------------------------------------------------------
POST hockey/player/_update_by_query
{
  "script": {
    "lang": "painless",
    "inline": "ctx._source.last = /[aeiou]/.matcher(ctx._source.last).replaceAll('')"
  }
}
----------------------------------------------------------------
// CONSOLE


Note: all of the `_update_by_query` examples above could really do with a
`query` to limit the data that they pull back. While you *could* use a
<<query-dsl-script-query>> it wouldn't be as efficient as using any other query
because script queries aren't able to use the inverted index to limit the
documents that they have to check.

[float]
[[painless-api]]
== Painless API

The following Java packages are available for use in the Painless language:

* https://docs.oracle.com/javase/8/docs/api/java/lang/package-summary.html[java.lang] 
* https://docs.oracle.com/javase/8/docs/api/java/math/package-summary.html[java.math] 
* https://docs.oracle.com/javase/8/docs/api/java/text/package-summary.html[java.text] 
* https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html[java.time] 
* https://docs.oracle.com/javase/8/docs/api/java/time/chrono/package-summary.html[java.time.chrono] 
* https://docs.oracle.com/javase/8/docs/api/java/time/format/package-summary.html[java.time.format] 
* https://docs.oracle.com/javase/8/docs/api/java/time/temporal/package-summary.html[java.time.temporal] 
* https://docs.oracle.com/javase/8/docs/api/java/time/zone/package-summary.html[java.time.zone] 
* https://docs.oracle.com/javase/8/docs/api/java/util/package-summary.html[java.util] 
* https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html[java.util.function] 
* https://docs.oracle.com/javase/8/docs/api/java/util/regex/package-summary.html[java.util.regex] 
* https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html[java.util.stream] 

Note that unsafe classes and methods are not included, there is no support for:

* Manipulation of processes and threads
* Input/Output
* Reflection
