From ad28fb9ec0c102df1ba7743b6bcb86037507afde Mon Sep 17 00:00:00 2001 From: debadair Date: Fri, 18 Mar 2016 14:07:43 -0700 Subject: [PATCH] Docs: Adding Painless to the Scripting documentation. --- docs/reference/modules.asciidoc | 9 +- docs/reference/modules/painless.asciidoc | 693 ++++++++++++++++++ docs/reference/modules/scripting.asciidoc | 1 - .../modules/scripting/scripting.asciidoc | 78 +- 4 files changed, 740 insertions(+), 41 deletions(-) create mode 100644 docs/reference/modules/painless.asciidoc diff --git a/docs/reference/modules.asciidoc b/docs/reference/modules.asciidoc index 5ef8a41d3f5..b71d1224e7e 100644 --- a/docs/reference/modules.asciidoc +++ b/docs/reference/modules.asciidoc @@ -45,6 +45,10 @@ The modules in this section are: <>:: A Java node client joins the cluster, but doesn't hold data or act as a master node. + +<>:: + + A built-in scripting language for Elasticsearch that's designed to be as secure as possible. <>:: @@ -53,7 +57,8 @@ The modules in this section are: <>:: Custom scripting available in Lucene Expressions, Groovy, Python, and - Javascript. + Javascript. You can also write scripts in the built-in scripting language, + <>. <>:: @@ -89,6 +94,8 @@ include::modules/network.asciidoc[] include::modules/node.asciidoc[] +include::modules/painless.asciidoc[] + include::modules/plugins.asciidoc[] include::modules/scripting.asciidoc[] diff --git a/docs/reference/modules/painless.asciidoc b/docs/reference/modules/painless.asciidoc new file mode 100644 index 00000000000..7c2bcdd9750 --- /dev/null +++ b/docs/reference/modules/painless.asciidoc @@ -0,0 +1,693 @@ +[[modules-scripting-painless]] +== Painless Scripting Language + +_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]. + +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 <> + +* 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] +---------------------------------------------------------------- +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]}' +---------------------------------------------------------------- + +[float] +==== Accessing Doc Values from Painless + +All Painless scripts take in a `Map` of values called `input`. Document values can be accessed through another `Map` 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] +---------------------------------------------------------------- +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" + } + } + } + } +}' +---------------------------------------------------------------- + +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" + } + } + } +}' +---------------------------------------------------------------- + +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] +---------------------------------------------------------------- +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" + } + } +}' +---------------------------------------------------------------- + +[float] +==== Updating Fields with Painless + +You can also easily update fields. You access the original source for a field as `input.ctx._source.`. + +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 } + } +}' +---------------------------------------------------------------- + +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" + } +}' +---------------------------------------------------------------- + +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" + } +}' +---------------------------------------------------------------- + +[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] +---------------------------------------------------------------- +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" + } + } + } +}' +---------------------------------------------------------------- + +[[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 + () +----- + +----- +ArrayList extends List + () +----- + +----- +ArrayList extends List + () +----- + +----- +ArrayList extends List + () +----- + +----- +Boolean extends Object + (boolean) + static Boolean valueOf(boolean) + boolean booleanValue() +----- + +----- +Character extends Object + (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 extends Object + boolean add(Object) + void clear() + boolean contains(Object) + boolean isEmpty() + Iterator iterator() + boolean remove(Object) + int size() +----- + +----- +Collection extends Object + boolean add(String) + void clear() + boolean contains(String) + boolean isEmpty() + Iterator iterator() + boolean remove(String) + int size() +----- + +----- +Double extends Number + (double) + static Double valueOf(double) + static double MIN_VALUE + static double MAX_VALUE +----- + +----- +Exception extends Object + String getMessage() +----- + +----- +Float extends Number + (float) + static Float valueOf(float) + static float MIN_VALUE + static float MAX_VALUE +----- + +----- +HashMap extends Map + () +----- + +----- +HashMap extends Map + () +----- + +----- +HashMap extends Map + () +----- + +----- +HashMap extends Map + () +----- + +----- +IllegalArgument extends Exception + () +----- + +----- +IllegalState extends Exception + () +----- + +----- +Integer extends Number + (int) + static Integer valueOf(int) + static int MIN_VALUE + static int MAX_VALUE +----- + +----- +Iterator extends Object + boolean hasNext() + def next() + void remove() +----- + +----- +Iterator extends Object + boolean hasNext() + String next() + void remove() +----- + +----- +List extends Collection + def set(int, def) + def get(int) + def remove(int) +----- + +----- +List extends Collection + Object set(int, Object) + Object get(int) + Object remove(int) +----- + +----- +List extends Collection + String set(int, String) + String get(int) + String remove(int) +----- + +----- +Long extends Number + (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 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 extends Object + def put (String, def) + def get (String) + def remove (String) + boolean isEmpty() + int size() + boolean containsKey(String) + boolean containsValue(def) + Set keySet() + Collection values() +----- + +----- +Map extends Object + Object put (String, Object) + Object get (String) + Object remove (String) + boolean isEmpty() + int size() + boolean containsKey(String) + boolean containsValue(Object) + Set 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 extends Collection +----- + +----- +Set extends Collection +----- + +----- +Short extends Number + (short) + static Short valueOf(short) + static short MIN_VALUE + static short MAX_VALUE +----- + +----- +String extends CharSequence + (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 + () +----- + +----- +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) +----- \ No newline at end of file diff --git a/docs/reference/modules/scripting.asciidoc b/docs/reference/modules/scripting.asciidoc index f4374a0f9b3..114740306ee 100644 --- a/docs/reference/modules/scripting.asciidoc +++ b/docs/reference/modules/scripting.asciidoc @@ -3,4 +3,3 @@ include::scripting/scripting.asciidoc[] include::scripting/advanced-scripting.asciidoc[] include::scripting/security.asciidoc[] - diff --git a/docs/reference/modules/scripting/scripting.asciidoc b/docs/reference/modules/scripting/scripting.asciidoc index 4f9d84f34f8..12524407900 100644 --- a/docs/reference/modules/scripting/scripting.asciidoc +++ b/docs/reference/modules/scripting/scripting.asciidoc @@ -1,27 +1,48 @@ [[modules-scripting]] == Scripting -The scripting module allows to use scripts in order to evaluate custom -expressions. For example, scripts can be used to return "script fields" -as part of a search request, or can be used to evaluate a custom score -for a query and so on. +The scripting module enables you to use scripts to evaluate custom +expressions. For example, you could use a script to return "script fields" +as part of a search request or evaluate a custom score for a query. -The scripting module uses by default http://groovy-lang.org/[groovy] -(previously http://mvel.codehaus.org/[mvel] in 1.3.x and earlier) as the -scripting language with some extensions. Groovy is used since it is extremely -fast and very simple to use. +TIP: Elasticsearch now has a built-in scripting language called _Painless_ +that provides a more secure alternative for implementing +scripts for Elasticsearch. We encourage you to try it out-- +for more information, see <>. + +The default scripting language is http://groovy-lang.org/[groovy] +(http://mvel.codehaus.org/[mvel] was the default in 1.3.x and earlier). + +Additional `lang` plugins enable you to run scripts written in other languages. +Everywhere a script can be used, you can include a `lang` parameter +to specify the language of the script. Plugins are available for following languages: + +[cols="<,<,<",options="header",] +|======================================================================= +|Language |Sandboxed |Required plugin +|groovy |no |built-in +|expression |yes |built-in +|mustache |yes |built-in +/painless /yes /built-in (module) +|javascript |no |{plugins}/lang-javascript.html[elasticsearch-lang-javascript] +|python |no |{plugins}/lang-python.html[elasticsearch-lang-python] +|======================================================================= .Groovy dynamic scripting off by default from v1.4.3 [IMPORTANT] =================================================== -Groovy dynamic scripting is off by default, preventing dynamic Groovy scripts -from being accepted as part of a request or retrieved from the special -`.scripts` index. You will still be able to use Groovy scripts stored in files -in the `config/scripts/` directory on every node. +Groovy dynamic scripting is off by default. This prevents Groovy scripts +from being accepted as part of a request or retrieved from the +`.scripts` index. You can still use Groovy file scripts stored in +the `config/scripts/` directory on every node. -To convert an inline script to a file, take this simple script -as an example: +To convert an inline script to a file-based script, save the contents +of the `inline` field to a file with the `.groovy` extension and +store it in the `config/scripts` directory on every data node in your +cluster. + +For example, if you have the following inline script: [source,js] ----------------------------------- @@ -38,15 +59,9 @@ GET /_search } ----------------------------------- -Save the contents of the `inline` field as a file called `config/scripts/my_script.groovy` -on every data node in the cluster: +Save `1 + my_var` in a file called `config/scripts/my_script.groovy`. -[source,js] ------------------------------------ -1 + my_var ------------------------------------ - -Now you can access the script by file name (without the extension): +To use the script in a request, specify its name (without the `.groovy` extension) in the `file` field: [source,js] ----------------------------------- @@ -67,21 +82,8 @@ GET /_search =================================================== - -Additional `lang` plugins are provided to allow to execute scripts in -different languages. All places where a script can be used, a `lang` parameter -can be provided to define the language of the script. The following are the -supported scripting languages: - -[cols="<,<,<",options="header",] -|======================================================================= -|Language |Sandboxed |Required plugin -|groovy |no |built-in -|expression |yes |built-in -|mustache |yes |built-in -|javascript |no |{plugins}/lang-javascript.html[elasticsearch-lang-javascript] -|python |no |{plugins}/lang-python.html[elasticsearch-lang-python] -|======================================================================= +[float] +=== File-based Scripts To increase security, Elasticsearch does not allow you to specify scripts for non-sandboxed languages with a request. Instead, scripts must be placed in the @@ -219,8 +221,6 @@ Indexed scripts can be deleted by: curl -XDELETE localhost:9200/_scripts/groovy/indexedCalculateScore ----------------------------------- - - [float] [[enable-dynamic-scripting]] === Enabling dynamic scripting