730 lines
19 KiB
Plaintext
730 lines
19 KiB
Plaintext
[[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.
|
|
|
|
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
|
|
to `painless`.
|
|
|
|
[[painless-features]]
|
|
[float]
|
|
== Painless Features
|
|
|
|
* Control flow: `for` loops, `while` loops, `do/while` loops, `if/else`
|
|
|
|
* Fully Typed: all available types/methods described in <<painless-api, Painless API>>
|
|
|
|
* Arithmetic operators: multiplication `*`, division `/`, addition `+`, subtraction `-`, precedence `( )`
|
|
|
|
* Comparison operators: less than `<`, less than or equal to `<=`, greater than `>`, greater than or equal to `>=`, equal to `==`, and not equal to `!=`, reference equals `===`, reference not equals `!==`
|
|
|
|
* Boolean operators: not `!`, and `&&`, or `||`
|
|
|
|
* Bitwise operators: shift left `<<`, shift right `>>`, unsigned shift `>>>`, and `&`, or `|`, xor `^`, not `~`
|
|
|
|
* Shortcuts for list, map access using the dot `.` operator
|
|
|
|
|
|
[[painless-examples]]
|
|
[float]
|
|
== Painless Examples
|
|
|
|
To illustrate how Painless works, let's load some hockey stats into an Elasticsearch index:
|
|
|
|
[source,sh]
|
|
----------------------------------------------------------------
|
|
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
|
|
|
|
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.
|
|
|
|
[source,sh]
|
|
----------------------------------------------------------------
|
|
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]
|
|
----------------------------------------------------------------
|
|
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.
|
|
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`.
|
|
|
|
[source,sh]
|
|
----------------------------------------------------------------
|
|
GET /hockey-stats/_search
|
|
{
|
|
"query": {
|
|
"match_all": {}
|
|
},
|
|
"sort": {
|
|
"_script": {
|
|
"type": "keyword",
|
|
"order": "asc",
|
|
"script": {
|
|
"lang": "painless",
|
|
"inline": "input.doc.first.0 + \" \" + input.doc.last.0"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
----------------------------------------------------------------
|
|
// AUTOSENSE
|
|
|
|
[float]
|
|
=== Updating Fields with Painless
|
|
|
|
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]
|
|
----------------------------------------------------------------
|
|
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:
|
|
|
|
[source,sh]
|
|
----------------------------------------------------------------
|
|
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
|
|
the player's nickname, _hockey_.
|
|
|
|
[source,sh]
|
|
----------------------------------------------------------------
|
|
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
|
|
|
|
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]
|
|
----------------------------------------------------------------
|
|
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
|
|
|
|
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
|
|
|
|
* `def` (This type can be used to represent any other type.)
|
|
|
|
[float]
|
|
=== Basic Types
|
|
|
|
* `void`
|
|
* `boolean`
|
|
* `short`
|
|
* `char`
|
|
* `int`
|
|
* `long`
|
|
* `float`
|
|
* `double`
|
|
|
|
[float]
|
|
=== Complex Types
|
|
|
|
Non-static methods/members in superclasses are available to subclasses.
|
|
Generic types with unspecified generic parameters are parameters of type `def`.
|
|
|
|
-----
|
|
ArithmeticException extends Exception
|
|
<init>()
|
|
-----
|
|
|
|
-----
|
|
ArrayList extends List
|
|
<init>()
|
|
-----
|
|
|
|
-----
|
|
ArrayList<Object> extends List<Object>
|
|
<init>()
|
|
-----
|
|
|
|
-----
|
|
ArrayList<String> extends List<String>
|
|
<init>()
|
|
-----
|
|
|
|
-----
|
|
Boolean extends Object
|
|
<init>(boolean)
|
|
static Boolean valueOf(boolean)
|
|
boolean booleanValue()
|
|
-----
|
|
|
|
-----
|
|
Character extends Object
|
|
<init>(char)
|
|
static Character valueOf(char)
|
|
char charValue()
|
|
static char MIN_VALUE
|
|
static char MAX_VALUE
|
|
-----
|
|
|
|
-----
|
|
CharSequence extends Object
|
|
char charAt(int)
|
|
int length()
|
|
-----
|
|
|
|
-----
|
|
Collection extends Object
|
|
boolean add(def)
|
|
void clear()
|
|
boolean contains(def)
|
|
boolean isEmpty()
|
|
Iterator iterator()
|
|
boolean remove(def)
|
|
int size()
|
|
-----
|
|
|
|
-----
|
|
Collection<Object> extends Object
|
|
boolean add(Object)
|
|
void clear()
|
|
boolean contains(Object)
|
|
boolean isEmpty()
|
|
Iterator iterator()
|
|
boolean remove(Object)
|
|
int size()
|
|
-----
|
|
|
|
-----
|
|
Collection<String> extends Object
|
|
boolean add(String)
|
|
void clear()
|
|
boolean contains(String)
|
|
boolean isEmpty()
|
|
Iterator iterator()
|
|
boolean remove(String)
|
|
int size()
|
|
-----
|
|
|
|
-----
|
|
Double extends Number
|
|
<init>(double)
|
|
static Double valueOf(double)
|
|
static double MIN_VALUE
|
|
static double MAX_VALUE
|
|
-----
|
|
|
|
-----
|
|
Exception extends Object
|
|
String getMessage()
|
|
-----
|
|
|
|
-----
|
|
Float extends Number
|
|
<init>(float)
|
|
static Float valueOf(float)
|
|
static float MIN_VALUE
|
|
static float MAX_VALUE
|
|
-----
|
|
|
|
-----
|
|
HashMap extends Map
|
|
<init>()
|
|
-----
|
|
|
|
-----
|
|
HashMap<Object,Object> extends Map<Object,Object>
|
|
<init>()
|
|
-----
|
|
|
|
-----
|
|
HashMap<String,def> extends Map<String,def>
|
|
<init>()
|
|
-----
|
|
|
|
-----
|
|
HashMap<String,Object> extends Map<String,Object>
|
|
<init>()
|
|
-----
|
|
|
|
-----
|
|
IllegalArgument extends Exception
|
|
<init>()
|
|
-----
|
|
|
|
-----
|
|
IllegalState extends Exception
|
|
<init>()
|
|
-----
|
|
|
|
-----
|
|
Integer extends Number
|
|
<init>(int)
|
|
static Integer valueOf(int)
|
|
static int MIN_VALUE
|
|
static int MAX_VALUE
|
|
-----
|
|
|
|
-----
|
|
Iterator extends Object
|
|
boolean hasNext()
|
|
def next()
|
|
void remove()
|
|
-----
|
|
|
|
-----
|
|
Iterator<String> extends Object
|
|
boolean hasNext()
|
|
String next()
|
|
void remove()
|
|
-----
|
|
|
|
-----
|
|
List extends Collection
|
|
def set(int, def)
|
|
def get(int)
|
|
def remove(int)
|
|
-----
|
|
|
|
-----
|
|
List<Object> extends Collection
|
|
Object set(int, Object)
|
|
Object get(int)
|
|
Object remove(int)
|
|
-----
|
|
|
|
-----
|
|
List<String> extends Collection
|
|
String set(int, String)
|
|
String get(int)
|
|
String remove(int)
|
|
-----
|
|
|
|
-----
|
|
Long extends Number
|
|
<init>(long)
|
|
static Long valueOf(long)
|
|
static long MIN_VALUE
|
|
static long MAX_VALUE
|
|
-----
|
|
|
|
-----
|
|
Map extends Object
|
|
def put (def, def)
|
|
def get (def)
|
|
def remove (def)
|
|
boolean isEmpty()
|
|
int size()
|
|
boolean containsKey(def)
|
|
boolean containsValue(def)
|
|
Set keySet()
|
|
Collection values()
|
|
-----
|
|
|
|
-----
|
|
Map<Object,Object> extends Object
|
|
Object put (Object, Object)
|
|
Object get (Object)
|
|
Object remove (Object)
|
|
boolean isEmpty()
|
|
int size()
|
|
boolean containsKey(Object)
|
|
boolean containsValue(Object)
|
|
Set keySet()
|
|
Collection values()
|
|
-----
|
|
|
|
-----
|
|
Map<String,def> extends Object
|
|
def put (String, def)
|
|
def get (String)
|
|
def remove (String)
|
|
boolean isEmpty()
|
|
int size()
|
|
boolean containsKey(String)
|
|
boolean containsValue(def)
|
|
Set<String> keySet()
|
|
Collection values()
|
|
-----
|
|
|
|
-----
|
|
Map<String,Object> extends Object
|
|
Object put (String, Object)
|
|
Object get (String)
|
|
Object remove (String)
|
|
boolean isEmpty()
|
|
int size()
|
|
boolean containsKey(String)
|
|
boolean containsValue(Object)
|
|
Set<String> keySet()
|
|
Collection values()
|
|
-----
|
|
|
|
-----
|
|
Number extends Object
|
|
short shortValue()
|
|
short shortValue()
|
|
int intValue()
|
|
long longValue()
|
|
float floatValue()
|
|
double doubleValue()
|
|
-----
|
|
|
|
-----
|
|
Object
|
|
String toString()
|
|
boolean equals(Object)
|
|
int hashCode()
|
|
-----
|
|
|
|
-----
|
|
Set extends Collection
|
|
-----
|
|
|
|
-----
|
|
Set<Object> extends Collection<Object>
|
|
-----
|
|
|
|
-----
|
|
Set<String> extends Collection<String>
|
|
-----
|
|
|
|
-----
|
|
Short extends Number
|
|
<init>(short)
|
|
static Short valueOf(short)
|
|
static short MIN_VALUE
|
|
static short MAX_VALUE
|
|
-----
|
|
|
|
-----
|
|
String extends CharSequence
|
|
<init>(String)
|
|
int codePointAt(int)
|
|
int compareTo(String)
|
|
String concat(String)
|
|
boolean endsWith(String)
|
|
int indexOf(String, int)
|
|
boolean isEmpty()
|
|
String replace(CharSequence, CharSequence)
|
|
boolean startsWith(String)
|
|
String substring(int, int)
|
|
char[] toCharArray()
|
|
String trim()
|
|
-----
|
|
|
|
-----
|
|
NumberFormatException extends Exception
|
|
<init>()
|
|
-----
|
|
|
|
-----
|
|
Void extends Object
|
|
-----
|
|
|
|
[float]
|
|
==== Utility Classes
|
|
|
|
-----
|
|
Math
|
|
static double abs(double)
|
|
static float fabs(float)
|
|
static long labs(long)
|
|
static int iabs(int)
|
|
static double acos(double)
|
|
static double asin(double)
|
|
static double atan(double)
|
|
static double atan2(double)
|
|
static double cbrt(double)
|
|
static double ceil(double)
|
|
static double cos(double)
|
|
static double cosh(double)
|
|
static double exp(double)
|
|
static double expm1(double)
|
|
static double floor(double)
|
|
static double hypt(double, double)
|
|
static double abs(double)
|
|
static double log(double)
|
|
static double log10(double)
|
|
static double log1p(double)
|
|
static double max(double, double)
|
|
static float fmax(float, float)
|
|
static long lmax(long, long)
|
|
static int imax(int, int)
|
|
static double min(double, double)
|
|
static float fmin(float, float)
|
|
static long lmin(long, long)
|
|
static int imin(int, int)
|
|
static double pow(double, double)
|
|
static double random()
|
|
static double rint(double)
|
|
static long round(double)
|
|
static double sin(double)
|
|
static double sinh(double)
|
|
static double sqrt(double)
|
|
static double tan(double)
|
|
static double tanh(double)
|
|
static double toDegrees(double)
|
|
static double toRadians(double)
|
|
-----
|
|
|
|
-----
|
|
Utility
|
|
static boolean NumberToboolean(Number)
|
|
static char NumberTochar(Number)
|
|
static Boolean NumberToBoolean(Number)
|
|
static Short NumberToShort(Number)
|
|
static Character NumberToCharacter(Number)
|
|
static Integer NumberToInteger(Number)
|
|
static Long NumberToLong(Number)
|
|
static Float NumberToFloat(Number)
|
|
static Double NumberToDouble(Number)
|
|
static byte booleanTobyte(boolean)
|
|
static short booleanToshort(boolean)
|
|
static char booleanTochar(boolean)
|
|
static int booleanToint(boolean)
|
|
static long booleanTolong(boolean)
|
|
static float booleanTofloat(boolean)
|
|
static double booleanTodouble(boolean)
|
|
static Integer booleanToInteger(boolean)
|
|
static byte BooleanTobyte(Boolean)
|
|
static short BooleanToshort(Boolean)
|
|
static char BooleanTochar(Boolean)
|
|
static int BooleanToint(Boolean)
|
|
static long BooleanTolong(Boolean)
|
|
static float BooleanTofloat(Boolean)
|
|
static double BooleanTodouble(Boolean)
|
|
static Byte BooleanToByte(Boolean)
|
|
static Short BooleanToShort(Boolean)
|
|
static Character BooleanToCharacter(Boolean)
|
|
static Integer BooleanToInteger(Boolean)
|
|
static Long BooleanToLong(Boolean)
|
|
static Float BooleanToFloat(Boolean)
|
|
static Double BooleanToDouble(Boolean)
|
|
static boolean byteToboolean(byte)
|
|
static Short byteToShort(byte)
|
|
static Character byteToCharacter(byte)
|
|
static Integer byteToInteger(byte)
|
|
static Long byteToLong(byte)
|
|
static Float byteToFloat(byte)
|
|
static Double byteToDouble(byte)
|
|
static boolean ByteToboolean(Byte)
|
|
static char ByteTochar(Byte)
|
|
static boolean shortToboolean(short)
|
|
static Byte shortToByte(short)
|
|
static Character shortToCharacter(short)
|
|
static Integer shortToInteger(short)
|
|
static Long shortToLong(short)
|
|
static Float shortToFloat(short)
|
|
static Double shortToDouble(short)
|
|
static boolean ShortToboolean(Short)
|
|
static char ShortTochar(Short)
|
|
static boolean charToboolean(char)
|
|
static Byte charToByte(char)
|
|
static Short charToShort(char)
|
|
static Integer charToInteger(char)
|
|
static Long charToLong(char)
|
|
static Float charToFloat(char)
|
|
static Double charToDouble(char)
|
|
static boolean CharacterToboolean(Character)
|
|
static byte CharacterTobyte(Character)
|
|
static short CharacterToshort(Character)
|
|
static int CharacterToint(Character)
|
|
static long CharacterTolong(Character)
|
|
static float CharacterTofloat(Character)
|
|
static double CharacterTodouble(Character)
|
|
static Boolean CharacterToBoolean(Character)
|
|
static Byte CharacterToByte(Character)
|
|
static Short CharacterToShort(Character)
|
|
static Integer CharacterToInteger(Character)
|
|
static Long CharacterToLong(Character)
|
|
static Float CharacterToFloat(Character)
|
|
static Double CharacterToDouble(Character)
|
|
static boolean intToboolean(int)
|
|
static Byte intToByte(int)
|
|
static Short intToShort(int)
|
|
static Character intToCharacter(int)
|
|
static Long intToLong(int)
|
|
static Float intToFloat(int)
|
|
static Double intToDouble(int)
|
|
static boolean IntegerToboolean(Integer)
|
|
static char IntegerTochar(Integer)
|
|
static boolean longToboolean(long)
|
|
static Byte longToByte(long)
|
|
static Short longToShort(long)
|
|
static Character longToCharacter(long)
|
|
static Integer longToInteger(long)
|
|
static Float longToFloat(long)
|
|
static Double longToDouble(long)
|
|
static boolean LongToboolean(Long)
|
|
static char LongTochar(Long)
|
|
static boolean floatToboolean(float)
|
|
static Byte floatToByte(float)
|
|
static Short floatToShort(float)
|
|
static Character floatToCharacter(float)
|
|
static Integer floatToInteger(float)
|
|
static Long floatToLong(float)
|
|
static Double floatToDouble(float)
|
|
static boolean FloatToboolean(Float)
|
|
static char FloatTochar(Float)
|
|
static boolean doubleToboolean(double)
|
|
static Byte doubleToByte(double)
|
|
static Short doubleToShort(double)
|
|
static Character doubleToCharacter(double)
|
|
static Integer doubleToInteger(double)
|
|
static Long doubleToLong(double)
|
|
static Float doubleToFloat(double)
|
|
static boolean DoubleToboolean(Double)
|
|
static char DoubleTochar(Double)
|
|
-----
|
|
|
|
-----
|
|
Def
|
|
static boolean defToboolean(def)
|
|
static byte defTobyte(def)
|
|
static short defToshort(def)
|
|
static char defTochar(def)
|
|
static int defToint(def)
|
|
static long defTolong(def)
|
|
static float defTofloat(def)
|
|
static double defTodouble(def)
|
|
static Boolean defToBoolean(def)
|
|
static Byte defToByte(def)
|
|
static Character defToCharacter(def)
|
|
static Integer defToInteger(def)
|
|
static Long defToLong(def)
|
|
static Float defToFloat(def)
|
|
static Double defToDouble(def)
|
|
----- |