painless-syntax.asciidoc 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. [[modules-scripting-painless-syntax]]
  2. === Painless Syntax
  3. 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.]
  4. [float]
  5. [[painless-types]]
  6. === Variable types
  7. Painless supports all of https://docs.oracle.com/javase/tutorial/java/nutsandbolts/variables.html[Java's types],
  8. including array types, but adds some additional built-in types.
  9. [float]
  10. [[painless-def]]
  11. ==== Def
  12. The dynamic type `def` serves as a placeholder for any other type. It adopts the behavior
  13. of whatever runtime type it represents.
  14. [float]
  15. [[painless-strings]]
  16. ==== String
  17. String constants can be declared with single quotes, to avoid escaping horrors with JSON:
  18. [source,painless]
  19. ---------------------------------------------------------
  20. def mystring = 'foo';
  21. ---------------------------------------------------------
  22. [float]
  23. [[painless-arrays]]
  24. ==== Arrays
  25. Arrays can be subscripted starting from `0` for traditional array access or with
  26. negative numbers to starting from the back of the array. So the following
  27. returns `2`.
  28. [source,painless]
  29. ---------------------------------------------------------
  30. int[] x = new int[5];
  31. x[0]++;
  32. x[-5]++;
  33. return x[0];
  34. ---------------------------------------------------------
  35. [float]
  36. [[painless-lists]]
  37. ==== List
  38. Lists can be created explicitly (e.g. `new ArrayList()`) or initialized similar to Groovy:
  39. [source,painless]
  40. ---------------------------------------------------------
  41. def list = [1,2,3];
  42. ---------------------------------------------------------
  43. Lists can also be accessed similar to arrays. They support `.length` and
  44. subscripts, including negative subscripts to read from the back of the list:
  45. [source,painless]
  46. ---------------------------------------------------------
  47. def list = [1,2,3];
  48. list[-1] = 5
  49. return list[0]
  50. ---------------------------------------------------------
  51. [float]
  52. [[painless-maps]]
  53. ==== Map
  54. Maps can be created explicitly (e.g. `new HashMap()`) or initialized similar to Groovy:
  55. [source,painless]
  56. ---------------------------------------------------------
  57. def person = ['name': 'Joe', 'age': 63];
  58. ---------------------------------------------------------
  59. Map keys can also be accessed as properties.
  60. [source,painless]
  61. ---------------------------------------------------------
  62. def person = ['name': 'Joe', 'age': 63];
  63. person.retired = true;
  64. return person.name
  65. ---------------------------------------------------------
  66. Map keys can also be accessed via subscript (for keys containing special characters):
  67. [source,painless]
  68. ---------------------------------------------------------
  69. return map['something-absurd!']
  70. ---------------------------------------------------------
  71. [float]
  72. [[painless-pattern]]
  73. ==== Pattern
  74. Regular expression constants are directly supported:
  75. [source,painless]
  76. ---------------------------------------------------------
  77. Pattern p = /[aeiou]/
  78. ---------------------------------------------------------
  79. Patterns can only be created via this mechanism. This ensures fast performance, regular expressions
  80. are always constants and compiled efficiently a single time.
  81. [float]
  82. [[modules-scripting-painless-regex-flags]]
  83. ==== Pattern flags
  84. You can define flags on patterns in Painless by adding characters after the
  85. trailing `/` like `/foo/i` or `/foo \w #comment/iUx`. Painless exposes all the
  86. flags from
  87. https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html[Java's Pattern class]
  88. using these characters:
  89. [cols="<,<,<",options="header",]
  90. |=======================================================================
  91. | Character | Java Constant | Example
  92. |`c` | CANON_EQ | `'å' ==~ /å/c` (open in hex editor to see)
  93. |`i` | CASE_INSENSITIVE | `'A' ==~ /a/i`
  94. |`l` | LITERAL | `'[a]' ==~ /[a]/l`
  95. |`m` | MULTILINE | `'a\nb\nc' =~ /^b$/m`
  96. |`s` | DOTALL (aka single line) | `'a\nb\nc' =~ /.b./s`
  97. |`U` | UNICODE_CHARACTER_CLASS | `'Ɛ' ==~ /\\w/U`
  98. |`u` | UNICODE_CASE | `'Ɛ' ==~ /ɛ/iu`
  99. |`x` | COMMENTS (aka extended) | `'a' ==~ /a #comment/x`
  100. |=======================================================================
  101. [float]
  102. [[painless-deref]]
  103. === Dereferences
  104. Like lots of languages, Painless uses `.` to reference fields and call methods:
  105. [source,painless]
  106. ---------------------------------------------------------
  107. String foo = 'foo';
  108. TypeWithGetterOrPublicField bar = new TypeWithGetterOrPublicField()
  109. return foo.length() + bar.x
  110. ---------------------------------------------------------
  111. Like Groovy, Painless uses `?.` to perform null-safe references, with the
  112. result being `null` if the left hand side is null:
  113. [source,painless]
  114. ---------------------------------------------------------
  115. String foo = null;
  116. return foo?.length() // Returns null
  117. ---------------------------------------------------------
  118. Unlike Groovy, Painless doesn't support writing to null values with this
  119. operator:
  120. [source,painless]
  121. ---------------------------------------------------------
  122. TypeWithSetterOrPublicField foo = null;
  123. foo?.x = 'bar' // Compile error
  124. ---------------------------------------------------------
  125. [float]
  126. [[painless-operators]]
  127. === Operators
  128. All of Java's https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html[operators] are
  129. supported with the same precedence, promotion, and semantics.
  130. There are only a few minor differences and add-ons:
  131. * `==` behaves as Java's for numeric types, but for non-numeric types acts as https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-[`Object.equals()`]
  132. * `===` and `!==` support exact reference comparison (e.g. `x === y`)
  133. * `=~` true if a portion of the text matches a pattern (e.g. `x =~ /b/`)
  134. * `==~` true if the entire text matches a pattern (e.g. `x ==~ /[Bb]ob/`)
  135. The `?:` (aka Elvis) operator coalesces null values. So `x ?: 0` is `0` if `x`
  136. is `null` and whatever value `x` has otherwise. It is a convenient way to write
  137. default values like `doc['x'].value ?: 0` which is 0 if `x` is not in the
  138. document being processed. It can also work with null safe dereferences to
  139. efficiently handle null in chains. For example,
  140. `doc['foo.keyword'].value?.length() ?: 0` is 0 if the document being processed
  141. doesn't have a `foo.keyword` field but is the length of that field if it does.
  142. Lastly, `?:` is lazy so the right hand side is not evaluated at all if the left
  143. hand side isn't null.
  144. NOTE: Unlike Groovy, Painless' `?:` operator only coalesces `null`, not `false`
  145. or http://groovy-lang.org/semantics.html#Groovy-Truth[falsy] values. Strictly
  146. speaking Painless' `?:` is more like Kotlin's `?:` than Groovy's `?:`.
  147. NOTE: The result of `?.` and `?:` can't be assigned to primitives. So
  148. `int[] someArray = null; int l = someArray?.length` and
  149. `int s = params.size ?: 100` don't work. Do
  150. `def someArray = null; def l = someArray?.length` and
  151. `def s = params.size ?: 100` instead.
  152. [float]
  153. [[painless-control-flow]]
  154. === Control flow
  155. Java's https://docs.oracle.com/javase/tutorial/java/nutsandbolts/flow.html[control flow statements] are supported, with the exception
  156. of the `switch` statement.
  157. In addition to Java's `enhanced for` loop, the `for in` syntax from groovy can also be used:
  158. [source,painless]
  159. ---------------------------------------------------------
  160. for (item : list) {
  161. ...
  162. }
  163. ---------------------------------------------------------
  164. [float]
  165. [[painless-functions]]
  166. === Functions
  167. Functions can be declared at the beginning of the script, for example:
  168. [source,painless]
  169. ---------------------------------------------------------
  170. boolean isNegative(def x) { x < 0 }
  171. ...
  172. if (isNegative(someVar)) {
  173. ...
  174. }
  175. ---------------------------------------------------------
  176. [float]
  177. [[painless-lambda-expressions]]
  178. === Lambda expressions
  179. Lambda expressions and method references work the same as https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html[Java's].
  180. [source,painless]
  181. ---------------------------------------------------------
  182. list.removeIf(item -> item == 2);
  183. list.removeIf((int item) -> item == 2);
  184. list.removeIf((int item) -> { item == 2 });
  185. list.sort((x, y) -> x - y);
  186. list.sort(Integer::compare);
  187. ---------------------------------------------------------
  188. Method references to functions within the script can be accomplished using `this`, e.g. `list.sort(this::mycompare)`.