Tidied the Painless docs and added the experimental tag

This commit is contained in:
Clinton Gormley 2016-03-24 18:34:23 +01:00
parent 584ba6133d
commit 08903f1ed8
1 changed files with 167 additions and 132 deletions

View File

@ -1,19 +1,21 @@
[[modules-scripting-painless]]
== Painless Scripting Language
_Painless_ is a simple, secure scripting language built in to Elasticsearch as a module.
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 built in to Elasticsearch as a module.
It is designed specifically for use with Elasticsearch and can safely be used dynamically.
A Painless script is essentially a single function. Painless does not provide support
for defining multiple functions within a script. The Painless syntax is similar to
http://groovy-lang.org/index.html[Groovy].
A Painless script is essentially a single function. Painless does not provide support
for defining multiple functions within a script. 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
You can use Painless anywhere a script can be used in Elasticsearch--simply set the `lang` parameter
to `painless`.
[[painless-features]]
[float]
=== Painless Features
== Painless Features
* Control flow: `for` loops, `while` loops, `do/while` loops, `if/else`
@ -32,196 +34,229 @@ to `painless`.
[[painless-examples]]
[float]
=== Painless Examples
== Painless Examples
To illustrate how Painless works, let's load some hockey stats into an Elasticsearch index:
[source,sh]
----------------------------------------------------------------
curl -XDELETE http://localhost:9200/hockey-stats
curl -XPUT http://localhost:9200/hockey-stats
curl -XPUT http://localhost:9200/hockey-stats/player/1 -d '{"first":"johnny", "last":"gaudreau", "goals":[9, 27, 1], "assists":[17, 46, 0], "gp":[26, 82, 1]}'
curl -XPUT http://localhost:9200/hockey-stats/player/2 -d '{"first":"sean", "last":"monohan", "goals":[7, 54, 26], "assists":[11, 26, 13], "gp":[26, 82, 82]}'
curl -XPUT http://localhost:9200/hockey-stats/player/3 -d '{"first":"jiri", "last":"hudler", "goals":[5, 34, 36], "assists":[11, 62, 42], "gp":[24, 80, 79]}'
curl -XPUT http://localhost:9200/hockey-stats/player/4 -d '{"first":"micheal", "last":"frolik", "goals":[4, 6, 15], "assists":[8, 23, 15], "gp":[26, 82, 82]}'
curl -XPUT http://localhost:9200/hockey-stats/player/5 -d '{"first":"sam", "last":"bennett", "goals":[5, 0, 0], "assists":[8, 1, 0], "gp":[26, 1, 0]}'
curl -XPUT http://localhost:9200/hockey-stats/player/6 -d '{"first":"dennis", "last":"wideman", "goals":[0, 26, 15], "assists":[11, 30, 24], "gp":[26, 81, 82]}'
curl -XPUT http://localhost:9200/hockey-stats/player/7 -d '{"first":"david", "last":"jones", "goals":[7, 19, 5], "assists":[3, 17, 4], "gp":[26, 45, 34]}'
curl -XPUT http://localhost:9200/hockey-stats/player/8 -d '{"first":"tj", "last":"brodie", "goals":[2, 14, 7], "assists":[8, 42, 30], "gp":[26, 82, 82]}'
curl -XPUT http://localhost:9200/hockey-stats/player/9 -d '{"first":"mark", "last":"giordano", "goals":[6, 30, 15], "assists":[3, 30, 24], "gp":[26, 60, 63]}'
curl -XPUT http://localhost:9200/hockey-stats/player/10 -d '{"first":"mikael", "last":"backlund", "goals":[3, 15, 13], "assists":[6, 24, 18], "gp":[26, 82, 82]}'
curl -XPUT http://localhost:9200/hockey-stats/player/11 -d '{"first":"joe", "last":"colborne", "goals":[3, 18, 13], "assists":[6, 20, 24], "gp":[26, 67, 82]}'
DELETE /hockey-stats
PUT /hockey-stats/player/_bulk
{"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]}
----------------------------------------------------------------
// AUTOSENSE
[float]
==== Accessing Doc Values from Painless
=== Accessing Doc Values from Painless
All Painless scripts take in a `Map<String,def>` of values called `input`. Document values can be accessed through another `Map<String,def>` within the `input` variable.
All Painless scripts take in a `Map<String,def>` of values called `input`. Document values can be accessed through another `Map<String,def>` within the `input` variable.
For example, the following script calculates a player's total goals. This example uses a strongly typed `int` and a `for` loop.
For example, the following script calculates a player's total goals. This example uses a strongly typed `int` and a `for` loop.
[source,sh]
----------------------------------------------------------------
curl -XGET http://localhost:9200/hockey-stats/_search -d '{
"query": {
"function_score": {
"script_score" : {
"script" : {
"inline":
"int total = 0; for (int i = 0; i < input.doc.goals.size(); ++i) { total += input.doc.goals[i]; } return total;",
"lang": "painless"
}
GET /hockey-stats/_search
{
"query": {
"function_score": {
"script_score": {
"script": {
"lang": "painless",
"inline": "int total = 0; for (int i = 0; i < input.doc.goals.size(); ++i) { total += input.doc.goals[i]; } return total;"
}
}
}
}
}'
}
}
----------------------------------------------------------------
// AUTOSENSE
Alternatively, you could do the same thing using a script field instead of a function score:
[source,sh]
----------------------------------------------------------------
curl -XGET http://localhost:9200/hockey-stats/_search -d '{
"query": {
"match_all": {}},
"script_fields": {
"total_goals": {
"script": {
"inline": "int total = 0; for (int i = 0; i < input.doc.goals.size(); ++i) { total += input.doc.goals[i]; } return total;",
"lang": "painless"
}
}
GET /hockey-stats/_search
{
"query": {
"match_all": {}
},
"script_fields": {
"total_goals": {
"script": {
"lang": "painless",
"inline": "int total = 0; for (int i = 0; i < input.doc.goals.size(); ++i) { total += input.doc.goals[i]; } return total;"
}
}
}'
}
}
----------------------------------------------------------------
// AUTOSENSE
You must always specify the index of the field value you want, even if there's only a single item in the field.
You must always specify the index of the field value you want, even if there's only a single item in the field.
All fields in Elasticsearch are multi-valued and Painless does not provide a `.value` shortcut. The following example uses a Painless script to sort the players by their combined first and last names. The names are accessed using
`input.doc.first.0` and `input.doc.last.0`.
`input.doc.first.0` and `input.doc.last.0`.
[source,sh]
----------------------------------------------------------------
curl -XGET http://localhost:9200/hockey-stats/_search -d '{
"query" : {
"match_all": {}},
"sort" : {
"_script" : {
"type" : "string",
"script" : {"inline": "input.doc.first.0 + \" \" + input.doc.last.0",
"lang": "painless"},
"order" : "asc"
}
GET /hockey-stats/_search
{
"query": {
"match_all": {}
},
"sort": {
"_script": {
"type": "string",
"order": "asc",
"script": {
"lang": "painless",
"inline": "input.doc.first.0 + \" \" + input.doc.last.0"
}
}
}'
}
}
----------------------------------------------------------------
// AUTOSENSE
[float]
==== Updating Fields with Painless
=== Updating Fields with Painless
You can also easily update fields. You access the original source for a field as `input.ctx._source.<field-name>`.
You can also easily update fields. You access the original source for a field as `input.ctx._source.<field-name>`.
First, let's look at the source data for a player by submitting the following request:
[source,sh]
----------------------------------------------------------------
curl -XGET http://localhost:9200/hockey-stats/_search -d '{
"fields" : ["_id", "_source"], "query" : {
"term" : { "_id" : 1 }
GET /hockey-stats/_search
{
"fields": [
"_id",
"_source"
],
"query": {
"term": {
"_id": 1
}
}'
}
}
----------------------------------------------------------------
// AUTOSENSE
To change player 1's last name to _hockey_, simply set `input.ctx._source.last` to the new value:
To change player 1's last name to `hockey`, simply set `input.ctx._source.last` to the new value:
[source,sh]
----------------------------------------------------------------
curl -XPOST http://localhost:9200/hockey-stats/player/1/_update -d '{
"script": {
"inline": "input.ctx._source.last = input.last",
"params": {"last": "hockey"},
"lang": "painless"
}
}'
POST /hockey-stats/player/1/_update
{
"script": {
"lang": "painless",
"inline": "input.ctx._source.last = input.last",
"params": {
"last": "hockey"
}
}
}
----------------------------------------------------------------
// AUTOSENSE
You can also add fields to a document. For example, this script adds a new field that contains
You can also add fields to a document. For example, this script adds a new field that contains
the player's nickname, _hockey_.
[source,sh]
----------------------------------------------------------------
curl -XPOST http://localhost:9200/hockey-stats/player/1/_update -d '{
"script": {
"inline": "input.ctx._source.last = input.last input.ctx._source.nick = input.nick",
"params": {"last": "gaudreau", "nick": "hockey"},
"lang": "painless"
}
}'
POST /hockey-stats/player/1/_update
{
"script": {
"lang": "painless",
"inline": "input.ctx._source.last = input.last input.ctx._source.nick = input.nick",
"params": {
"last": "gaudreau",
"nick": "hockey"
}
}
}
----------------------------------------------------------------
// AUTOSENSE
[float]
==== Writing Type-Safe Scripts to Improve Performance
=== Writing Type-Safe Scripts to Improve Performance
If you explicitly specify types, the compiler doesn't have to perform type lookups at runtime, which can significantly
improve performance. For example, the following script performs the same first name, last name sort we showed before,
If you explicitly specify types, the compiler doesn't have to perform type lookups at runtime, which can significantly
improve performance. For example, the following script performs the same first name, last name sort we showed before,
but it's fully type-safe.
[source,sh]
----------------------------------------------------------------
curl -XGET http://localhost:9200/hockey-stats/_search -d '{
"query": {
"match_all": {}
},
"script_fields": {
"full_name_dynamic": {
"script": {
"inline": "def first = input.doc.first.0; def last = input.doc.last.0; return first + \" \" + last;",
"lang": "painless"
}
},
"full_name_static": {
"script": {
"inline":
"String first = (String)((List)((Map)input.get(\"doc\")).get(\"first\")).get(0); String last = (String)((List)((Map)input.get(\"doc\")).get(\"last\")).get(0); return first + \" \" + last;",
"lang": "painless"
}
}
GET /hockey-stats/_search
{
"query": {
"match_all": {}
},
"script_fields": {
"full_name_dynamic": {
"script": {
"lang": "painless",
"inline": "def first = input.doc.first.0; def last = input.doc.last.0; return first + \" \" + last;"
}
},
"full_name_static": {
"script": {
"lang": "painless",
"inline": "String first = (String)((List)((Map)input.get(\"doc\")).get(\"first\")).get(0); String last = (String)((List)((Map)input.get(\"doc\")).get(\"last\")).get(0); return first + \" \" + last;"
}
}
}'
}
}
----------------------------------------------------------------
// AUTOSENSE
[[painless-api]]
[float]
=== Painless API
== Painless API
The following types are available for use in the Painless language. Most types and methods map directly to their Java equivalents--for more information, see the corresponding https://docs.oracle.com/javase/8/docs/api/java/lang/package-summary.html[Javadoc].
[float]
==== Dynamic Types
=== Dynamic Types
`def` (This type can be used to represent any other type.)
* `def` (This type can be used to represent any other type.)
[float]
==== Basic Types
=== Basic Types
`void`
`boolean`
`short`
`char`
`int`
`long`
`float`
`double`
* `void`
* `boolean`
* `short`
* `char`
* `int`
* `long`
* `float`
* `double`
[float]
==== Complex Types
=== Complex Types
Non-static methods/members in superclasses are available to subclasses.
Generic types with unspecified generic parameters are parameters of type `def`.
@ -242,7 +277,7 @@ ArrayList<Object> extends List<Object>
-----
-----
ArrayList<String> extends List<String>
ArrayList<String> extends List<String>
<init>()
-----
@ -254,13 +289,13 @@ Boolean extends Object
-----
-----
Character extends Object
Character extends Object
<init>(char)
static Character valueOf(char)
char charValue()
static char MIN_VALUE
static char MAX_VALUE
-----
-----
-----
CharSequence extends Object
@ -311,7 +346,7 @@ Double extends Number
-----
Exception extends Object
String getMessage()
String getMessage()
-----
-----
@ -324,7 +359,7 @@ Float extends Number
-----
HashMap extends Map
<init>()
<init>()
-----
-----
@ -334,14 +369,14 @@ HashMap<Object,Object> extends Map<Object,Object>
-----
HashMap<String,def> extends Map<String,def>
<init>()
<init>()
-----
-----
HashMap<String,Object> extends Map<String,Object>
<init>()
-----
-----
IllegalArgument extends Exception
<init>()
@ -349,7 +384,7 @@ IllegalArgument extends Exception
-----
IllegalState extends Exception
<init>()
<init>()
-----
-----
@ -413,7 +448,7 @@ Map extends Object
boolean containsKey(def)
boolean containsValue(def)
Set keySet()
Collection values()
Collection values()
-----
-----
@ -549,7 +584,7 @@ Math
static double min(double, double)
static float fmin(float, float)
static long lmin(long, long)
static int imin(int, int)
static int imin(int, int)
static double pow(double, double)
static double random()
static double rint(double)