[[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 <> 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 <>. * 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.`. 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 <> 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 <> 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