2016-03-18 17:07:43 -04:00
[[modules-scripting-painless]]
2016-05-04 12:17:10 -04:00
=== Painless Scripting Language
2016-03-18 17:07:43 -04:00
2016-03-24 13:34:23 -04:00
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.]
2016-05-04 12:17:10 -04:00
_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.
2016-03-18 17:07:43 -04:00
2016-06-24 12:06:41 -04:00
The Painless syntax is similar to http://groovy-lang.org/index.html[Groovy].
2016-03-18 17:07:43 -04:00
2016-03-24 13:34:23 -04:00
You can use Painless anywhere a script can be used in Elasticsearch--simply set the `lang` parameter
2016-03-18 17:07:43 -04:00
to `painless`.
[[painless-features]]
[float]
2016-03-24 13:34:23 -04:00
== Painless Features
2016-03-18 17:07:43 -04:00
2016-06-24 12:06:41 -04:00
* Fast performance: https://benchmarks.elastic.co/index.html#search_qps_scripts[several times faster] than the alternatives.
2016-03-18 17:07:43 -04:00
2016-06-24 12:06:41 -04:00
* Safety: Fine-grained <<painless-api, whitelist>> with method call/field granularity.
2016-03-18 17:07:43 -04:00
2016-06-24 12:06:41 -04:00
* Optional typing: Variables and parameters can use explicit types or the dynamic `def` type.
2016-03-18 17:07:43 -04:00
2016-06-24 12:06:41 -04:00
* Syntax: Extends Java's syntax with a subset of Groovy for ease of use. See the <<modules-scripting-painless-syntax, Syntax Overview>>.
2016-06-13 21:42:37 -04:00
2016-06-24 12:06:41 -04:00
* Optimizations: Designed specifically for Elasticsearch scripting.
2016-03-18 17:07:43 -04:00
[[painless-examples]]
[float]
2016-03-24 13:34:23 -04:00
== Painless Examples
2016-03-18 17:07:43 -04:00
To illustrate how Painless works, let's load some hockey stats into an Elasticsearch index:
2016-04-29 10:42:03 -04:00
[source,js]
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-04-29 10:42:03 -04:00
PUT hockey/player/_bulk?refresh
2016-03-24 13:34:23 -04:00
{"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]}
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-05-09 09:42:23 -04:00
// CONSOLE
2016-04-29 10:42:03 -04:00
// TESTSETUP
2016-03-18 17:07:43 -04:00
[float]
2016-03-24 13:34:23 -04:00
=== Accessing Doc Values from Painless
2016-03-18 17:07:43 -04:00
2016-06-24 12:06:41 -04:00
Document values can be accessed from a `Map` named `doc`.
2016-03-18 17:07:43 -04:00
2016-03-24 13:34:23 -04:00
For example, the following script calculates a player's total goals. This example uses a strongly typed `int` and a `for` loop.
2016-03-18 17:07:43 -04:00
2016-04-29 10:42:03 -04:00
[source,js]
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-04-29 10:42:03 -04:00
GET hockey/_search
2016-03-24 13:34:23 -04:00
{
"query": {
"function_score": {
"script_score": {
"script": {
"lang": "painless",
2016-05-11 21:32:10 -04:00
"inline": "int total = 0; for (int i = 0; i < doc['goals'].length; ++i) { total += doc['goals'][i]; } return total;"
2016-03-18 17:07:43 -04:00
}
2016-03-24 13:34:23 -04:00
}
2016-03-18 17:07:43 -04:00
}
2016-03-24 13:34:23 -04:00
}
}
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-05-09 09:42:23 -04:00
// CONSOLE
2016-03-18 17:07:43 -04:00
Alternatively, you could do the same thing using a script field instead of a function score:
2016-04-29 10:42:03 -04:00
[source,js]
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-04-29 10:42:03 -04:00
GET hockey/_search
2016-03-24 13:34:23 -04:00
{
"query": {
"match_all": {}
},
"script_fields": {
"total_goals": {
"script": {
"lang": "painless",
2016-05-11 21:32:10 -04:00
"inline": "int total = 0; for (int i = 0; i < doc['goals'].length; ++i) { total += doc['goals'][i]; } return total;"
2016-03-24 13:34:23 -04:00
}
2016-03-18 17:07:43 -04:00
}
2016-03-24 13:34:23 -04:00
}
}
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-05-09 09:42:23 -04:00
// CONSOLE
2016-03-18 17:07:43 -04:00
2016-05-05 18:31:48 -04:00
The following example uses a Painless script to sort the players by their combined first and last names. The names are accessed using
2016-05-11 21:32:10 -04:00
`doc['first'].value` and `doc['last'].value`.
2016-03-18 17:07:43 -04:00
2016-04-29 10:42:03 -04:00
[source,js]
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-04-29 10:42:03 -04:00
GET hockey/_search
2016-03-24 13:34:23 -04:00
{
"query": {
"match_all": {}
},
"sort": {
"_script": {
2016-04-29 10:42:03 -04:00
"type": "string",
2016-03-24 13:34:23 -04:00
"order": "asc",
"script": {
"lang": "painless",
2016-06-20 11:49:17 -04:00
"inline": "doc['first.keyword'].value + ' ' + doc['last.keyword'].value"
2016-03-24 13:34:23 -04:00
}
2016-03-18 17:07:43 -04:00
}
2016-03-24 13:34:23 -04:00
}
}
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-05-09 09:42:23 -04:00
// CONSOLE
2016-03-18 17:07:43 -04:00
[float]
2016-03-24 13:34:23 -04:00
=== Updating Fields with Painless
2016-03-18 17:07:43 -04:00
2016-05-11 21:32:10 -04:00
You can also easily update fields. You access the original source for a field as `ctx._source.<field-name>`.
2016-03-18 17:07:43 -04:00
First, let's look at the source data for a player by submitting the following request:
2016-04-29 10:42:03 -04:00
[source,js]
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-04-29 10:42:03 -04:00
GET hockey/_search
2016-03-24 13:34:23 -04:00
{
2016-06-21 05:27:27 -04:00
"stored_fields": [
2016-03-24 13:34:23 -04:00
"_id",
"_source"
],
"query": {
"term": {
"_id": 1
2016-03-18 17:07:43 -04:00
}
2016-03-24 13:34:23 -04:00
}
}
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-05-09 09:42:23 -04:00
// CONSOLE
2016-03-18 17:07:43 -04:00
2016-05-11 21:32:10 -04:00
To change player 1's last name to `hockey`, simply set `ctx._source.last` to the new value:
2016-03-18 17:07:43 -04:00
2016-04-29 10:42:03 -04:00
[source,js]
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-04-29 10:42:03 -04:00
POST hockey/player/1/_update
2016-03-24 13:34:23 -04:00
{
"script": {
"lang": "painless",
2016-05-11 21:32:10 -04:00
"inline": "ctx._source.last = params.last",
2016-03-24 13:34:23 -04:00
"params": {
"last": "hockey"
}
}
}
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-05-09 09:42:23 -04:00
// CONSOLE
2016-03-18 17:07:43 -04:00
2016-03-24 13:34:23 -04:00
You can also add fields to a document. For example, this script adds a new field that contains
2016-03-18 17:07:43 -04:00
the player's nickname, _hockey_.
2016-04-29 10:42:03 -04:00
[source,js]
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-04-29 10:42:03 -04:00
POST hockey/player/1/_update
2016-03-24 13:34:23 -04:00
{
"script": {
"lang": "painless",
2016-05-17 11:03:48 -04:00
"inline": "ctx._source.last = params.last; ctx._source.nick = params.nick",
2016-03-24 13:34:23 -04:00
"params": {
"last": "gaudreau",
"nick": "hockey"
}
}
}
2016-03-18 17:07:43 -04:00
----------------------------------------------------------------
2016-05-09 09:42:23 -04:00
// CONSOLE
2016-03-18 17:07:43 -04:00
2016-06-13 21:42:37 -04:00
[float]
2016-06-16 11:07:09 -04:00
[[modules-scripting-painless-regex]]
2016-06-13 21:42:37 -04:00
=== Regular expressions
Painless's native support for regular expressions has syntax constructs:
* `/pattern/`: Pattern literals create patterns. This is the only way to create
2016-06-16 11:07:09 -04:00
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.
2016-06-13 21:42:37 -04:00
* `=~`: 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.
2016-06-16 11:07:09 -04:00
[float]
2016-03-18 17:07:43 -04:00
[[painless-api]]
2016-03-24 13:34:23 -04:00
== Painless API
2016-03-18 17:07:43 -04:00
2016-06-24 12:06:41 -04:00
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