painless.asciidoc 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. [[modules-scripting-painless]]
  2. === Painless Scripting Language
  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. _Painless_ is a simple, secure scripting language available in Elasticsearch
  5. by default. It is designed specifically for use with Elasticsearch and can
  6. safely be used with `inline` and `stored` scripting, which is enabled by
  7. default.
  8. The Painless syntax is similar to http://groovy-lang.org/index.html[Groovy].
  9. You can use Painless anywhere a script can be used in Elasticsearch--simply set the `lang` parameter
  10. to `painless`.
  11. [[painless-features]]
  12. [float]
  13. == Painless Features
  14. * Fast performance: https://benchmarks.elastic.co/index.html#search_qps_scripts[several times faster] than the alternatives.
  15. * Safety: Fine-grained <<painless-api, whitelist>> with method call/field granularity.
  16. * Optional typing: Variables and parameters can use explicit types or the dynamic `def` type.
  17. * Syntax: Extends Java's syntax with a subset of Groovy for ease of use. See the <<modules-scripting-painless-syntax, Syntax Overview>>.
  18. * Optimizations: Designed specifically for Elasticsearch scripting.
  19. [[painless-examples]]
  20. [float]
  21. == Painless Examples
  22. To illustrate how Painless works, let's load some hockey stats into an Elasticsearch index:
  23. [source,js]
  24. ----------------------------------------------------------------
  25. PUT hockey/player/_bulk?refresh
  26. {"index":{"_id":1}}
  27. {"first":"johnny","last":"gaudreau","goals":[9,27,1],"assists":[17,46,0],"gp":[26,82,1]}
  28. {"index":{"_id":2}}
  29. {"first":"sean","last":"monohan","goals":[7,54,26],"assists":[11,26,13],"gp":[26,82,82]}
  30. {"index":{"_id":3}}
  31. {"first":"jiri","last":"hudler","goals":[5,34,36],"assists":[11,62,42],"gp":[24,80,79]}
  32. {"index":{"_id":4}}
  33. {"first":"micheal","last":"frolik","goals":[4,6,15],"assists":[8,23,15],"gp":[26,82,82]}
  34. {"index":{"_id":5}}
  35. {"first":"sam","last":"bennett","goals":[5,0,0],"assists":[8,1,0],"gp":[26,1,0]}
  36. {"index":{"_id":6}}
  37. {"first":"dennis","last":"wideman","goals":[0,26,15],"assists":[11,30,24],"gp":[26,81,82]}
  38. {"index":{"_id":7}}
  39. {"first":"david","last":"jones","goals":[7,19,5],"assists":[3,17,4],"gp":[26,45,34]}
  40. {"index":{"_id":8}}
  41. {"first":"tj","last":"brodie","goals":[2,14,7],"assists":[8,42,30],"gp":[26,82,82]}
  42. {"index":{"_id":39}}
  43. {"first":"mark","last":"giordano","goals":[6,30,15],"assists":[3,30,24],"gp":[26,60,63]}
  44. {"index":{"_id":10}}
  45. {"first":"mikael","last":"backlund","goals":[3,15,13],"assists":[6,24,18],"gp":[26,82,82]}
  46. {"index":{"_id":11}}
  47. {"first":"joe","last":"colborne","goals":[3,18,13],"assists":[6,20,24],"gp":[26,67,82]}
  48. ----------------------------------------------------------------
  49. // CONSOLE
  50. // TESTSETUP
  51. [float]
  52. === Accessing Doc Values from Painless
  53. Document values can be accessed from a `Map` named `doc`.
  54. For example, the following script calculates a player's total goals. This example uses a strongly typed `int` and a `for` loop.
  55. [source,js]
  56. ----------------------------------------------------------------
  57. GET hockey/_search
  58. {
  59. "query": {
  60. "function_score": {
  61. "script_score": {
  62. "script": {
  63. "lang": "painless",
  64. "inline": "int total = 0; for (int i = 0; i < doc['goals'].length; ++i) { total += doc['goals'][i]; } return total;"
  65. }
  66. }
  67. }
  68. }
  69. }
  70. ----------------------------------------------------------------
  71. // CONSOLE
  72. Alternatively, you could do the same thing using a script field instead of a function score:
  73. [source,js]
  74. ----------------------------------------------------------------
  75. GET hockey/_search
  76. {
  77. "query": {
  78. "match_all": {}
  79. },
  80. "script_fields": {
  81. "total_goals": {
  82. "script": {
  83. "lang": "painless",
  84. "inline": "int total = 0; for (int i = 0; i < doc['goals'].length; ++i) { total += doc['goals'][i]; } return total;"
  85. }
  86. }
  87. }
  88. }
  89. ----------------------------------------------------------------
  90. // CONSOLE
  91. The following example uses a Painless script to sort the players by their combined first and last names. The names are accessed using
  92. `doc['first'].value` and `doc['last'].value`.
  93. [source,js]
  94. ----------------------------------------------------------------
  95. GET hockey/_search
  96. {
  97. "query": {
  98. "match_all": {}
  99. },
  100. "sort": {
  101. "_script": {
  102. "type": "string",
  103. "order": "asc",
  104. "script": {
  105. "lang": "painless",
  106. "inline": "doc['first.keyword'].value + ' ' + doc['last.keyword'].value"
  107. }
  108. }
  109. }
  110. }
  111. ----------------------------------------------------------------
  112. // CONSOLE
  113. [float]
  114. === Updating Fields with Painless
  115. You can also easily update fields. You access the original source for a field as `ctx._source.<field-name>`.
  116. First, let's look at the source data for a player by submitting the following request:
  117. [source,js]
  118. ----------------------------------------------------------------
  119. GET hockey/_search
  120. {
  121. "stored_fields": [
  122. "_id",
  123. "_source"
  124. ],
  125. "query": {
  126. "term": {
  127. "_id": 1
  128. }
  129. }
  130. }
  131. ----------------------------------------------------------------
  132. // CONSOLE
  133. To change player 1's last name to `hockey`, simply set `ctx._source.last` to the new value:
  134. [source,js]
  135. ----------------------------------------------------------------
  136. POST hockey/player/1/_update
  137. {
  138. "script": {
  139. "lang": "painless",
  140. "inline": "ctx._source.last = params.last",
  141. "params": {
  142. "last": "hockey"
  143. }
  144. }
  145. }
  146. ----------------------------------------------------------------
  147. // CONSOLE
  148. You can also add fields to a document. For example, this script adds a new field that contains
  149. the player's nickname, _hockey_.
  150. [source,js]
  151. ----------------------------------------------------------------
  152. POST hockey/player/1/_update
  153. {
  154. "script": {
  155. "lang": "painless",
  156. "inline": "ctx._source.last = params.last; ctx._source.nick = params.nick",
  157. "params": {
  158. "last": "gaudreau",
  159. "nick": "hockey"
  160. }
  161. }
  162. }
  163. ----------------------------------------------------------------
  164. // CONSOLE
  165. [float]
  166. [[modules-scripting-painless-regex]]
  167. === Regular expressions
  168. Painless's native support for regular expressions has syntax constructs:
  169. * `/pattern/`: Pattern literals create patterns. This is the only way to create
  170. a pattern in painless. The pattern inside the `/`s are just
  171. http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html[Java regular expressions].
  172. See <<modules-scripting-painless-regex-flags>> for more.
  173. * `=~`: The find operator return a `boolean`, `true` if a subsequence of the
  174. text matches, `false` otherwise.
  175. * `==~`: The match operator returns a `boolean`, `true` if the text matches,
  176. `false` if it doesn't.
  177. Using the find operator (`=~`) you can update all hockey players with "b" in
  178. their last name:
  179. [source,js]
  180. ----------------------------------------------------------------
  181. POST hockey/player/_update_by_query
  182. {
  183. "script": {
  184. "lang": "painless",
  185. "inline": "if (ctx._source.last =~ /b/) {ctx._source.last += \"matched\"} else {ctx.op = 'noop'}"
  186. }
  187. }
  188. ----------------------------------------------------------------
  189. // CONSOLE
  190. Using the match operator (`==~`) you can update all the hockey players who's
  191. names start with a consonant and end with a vowel:
  192. [source,js]
  193. ----------------------------------------------------------------
  194. POST hockey/player/_update_by_query
  195. {
  196. "script": {
  197. "lang": "painless",
  198. "inline": "if (ctx._source.last ==~ /[^aeiou].*[aeiou]/) {ctx._source.last += \"matched\"} else {ctx.op = 'noop'}"
  199. }
  200. }
  201. ----------------------------------------------------------------
  202. // CONSOLE
  203. Or you can use the `Pattern.matcher` directory to get a `Matcher` instance and
  204. remove all of the vowels in all of their names:
  205. [source,js]
  206. ----------------------------------------------------------------
  207. POST hockey/player/_update_by_query
  208. {
  209. "script": {
  210. "lang": "painless",
  211. "inline": "ctx._source.last = /[aeiou]/.matcher(ctx._source.last).replaceAll('')"
  212. }
  213. }
  214. ----------------------------------------------------------------
  215. // CONSOLE
  216. Note: all of the `_update_by_query` examples above could really do with a
  217. `query` to limit the data that they pull back. While you *could* use a
  218. <<query-dsl-script-query>> it wouldn't be as efficient as using any other query
  219. because script queries aren't able to use the inverted index to limit the
  220. documents that they have to check.
  221. [float]
  222. [[painless-api]]
  223. == Painless API
  224. The following Java packages are available for use in the Painless language:
  225. * https://docs.oracle.com/javase/8/docs/api/java/lang/package-summary.html[java.lang]
  226. * https://docs.oracle.com/javase/8/docs/api/java/math/package-summary.html[java.math]
  227. * https://docs.oracle.com/javase/8/docs/api/java/text/package-summary.html[java.text]
  228. * https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html[java.time]
  229. * https://docs.oracle.com/javase/8/docs/api/java/time/chrono/package-summary.html[java.time.chrono]
  230. * https://docs.oracle.com/javase/8/docs/api/java/time/format/package-summary.html[java.time.format]
  231. * https://docs.oracle.com/javase/8/docs/api/java/time/temporal/package-summary.html[java.time.temporal]
  232. * https://docs.oracle.com/javase/8/docs/api/java/time/zone/package-summary.html[java.time.zone]
  233. * https://docs.oracle.com/javase/8/docs/api/java/util/package-summary.html[java.util]
  234. * https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html[java.util.function]
  235. * https://docs.oracle.com/javase/8/docs/api/java/util/regex/package-summary.html[java.util.regex]
  236. * https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html[java.util.stream]
  237. Note that unsafe classes and methods are not included, there is no support for:
  238. * Manipulation of processes and threads
  239. * Input/Output
  240. * Reflection