123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916 |
- [[painless-execute-api]]
- === Painless execute API
- experimental::[]
- The Painless execute API runs a script and returns a result.
- [[painless-execute-api-request]]
- ==== {api-request-title}
- `POST /_scripts/painless/_execute`
- [[painless-execute-api-desc]]
- ==== {api-description-title}
- Use this API to build and test scripts, such as when defining a script for a
- {ref}/runtime.html[runtime field]. This API requires very few dependencies, and is
- especially useful if you don't have permissions to write documents on a cluster.
- The API uses several _contexts_, which control how scripts are executed, what
- variables are available at runtime, and what the return type is.
- Each context requires a script, but additional parameters depend on the context
- you're using for that script.
- [[painless-execute-api-request-body]]
- ==== {api-request-body-title}
- `script`:: (Required, object)
- The Painless script to execute.
- +
- .Properties of `script`
- --
- include::../painless-contexts/painless-runtime-fields-context.asciidoc[tag=runtime-field-emit]
- --
- [[_contexts]]
- `context`:: (Optional, string)
- The context that the script should run in. Defaults to `painless_test` if no
- context is specified.
- +
- .Properties of `context`
- [%collapsible%open]
- ====
- `painless_test`::
- The default context if no other context is specified. See
- <<painless-execute-test,test context>>.
- `filter`::
- Treats scripts as if they were run inside a `script` query. See
- <<painless-execute-filter-context,filter context>>.
- `score`::
- Treats scripts as if they were run inside a `script_score` function in a
- `function_score` query. See <<painless-execute-core-context,score context>>.
- [[painless-execute-runtime-context]]
- .Field contexts
- [%collapsible%open]
- =====
- --
- The following options are specific to the field contexts.
- NOTE: Result ordering in the field contexts is not guaranteed.
- --
- ****
- `boolean_field`::
- The context for {ref}/boolean.html[`boolean` fields]. The script returns a `true`
- or `false` response. See
- <<painless-runtime-boolean,boolean_field context>>.
- `date_field`::
- The context for {ref}/date.html[`date` fields]. `emit` takes a `long` value and
- the script returns a sorted list of dates. See
- <<painless-runtime-datetime,date_time context>>.
- `double_field`::
- The context for `double` {ref}/number.html[numeric fields]. The script returns a
- sorted list of `double` values. See
- <<painless-runtime-double,double_field context>>.
- `geo_point_field`::
- The context for {ref}/geo-point.html[`geo-point` fields]. `emit` takes two double
- parameters, the latitude and longitude values, and the script returns an object in
- GeoJSON format containing the coordinates for the geo point. See
- <<painless-runtime-geo,geo_point_field context>>.
- `ip_field`::
- The context for {ref}/ip.html[`ip` fields]. The script returns a sorted list of IP
- addresses. See
- <<painless-runtime-ip,ip_field context>>.
- `keyword_field`::
- The context for {ref}/keyword.html[`keyword` fields]. The script returns a sorted
- list of `string` values. See
- <<painless-runtime-keyword,keyword_field context>>.
- `long_field`::
- The context for `long` {ref}/number.html[numeric fields]. The script returns a
- sorted list of `long` values. See <<painless-runtime-long,long_field context>>.
- `composite_field`::
- The context for `composite` {ref}/runtime.html[runtime fields]. The script returns a
- map of values. See <<painless-runtime-composite,composite_field context>>.
- ****
- =====
- ====
- `context_setup`:: (Required, object)
- Additional parameters for the `context`.
- +
- NOTE: This parameter is required for all contexts except `painless_test`,
- which is the default if no value is provided for `context`.
- +
- .Properties of `context_setup`
- [%collapsible%open]
- ====
- `document`:: (Required, string)
- Document that's temporarily indexed in-memory and accessible from the script.
- `index`:: (Required, string)
- Index containing a mapping that's compatible with the indexed document.
- ====
- `params`:: (`Map`, read-only)
- Specifies any named parameters that are passed into the script as variables.
- `query`:: (Optional, object)
- NOTE: This parameter only applies when `score` is specified as the script
- `context`.
- +
- Use this parameter to specify a query for computing a score. Besides deciding
- whether or not the document matches, the
- {ref}/query-filter-context.html#query-context[query clause] also calculates a
- relevance score in the `_score` metadata field.
- [[painless-execute-test]]
- ==== Test context
- The `painless_test` context runs scripts without additional parameters. The only
- variable that is available is `params`, which can be used to access user defined
- values. The result of the script is always converted to a string.
- Because the default context is `painless_test`, you don't need to specify the
- `context` or `context_setup`.
- ===== Request
- [source,console]
- ----
- POST /_scripts/painless/_execute
- {
- "script": {
- "source": "params.count / params.total",
- "params": {
- "count": 100.0,
- "total": 1000.0
- }
- }
- }
- ----
- ===== Response
- [source,console-result]
- ----
- {
- "result": "0.1"
- }
- ----
- [[painless-execute-filter-context]]
- ==== Filter context
- The `filter` context treats scripts as if they were run inside a `script` query.
- For testing purposes, a document must be provided so that it will be temporarily
- indexed in-memory and is accessible from the script. More precisely, the
- `_source`, stored fields and doc values of such a document are available to the
- script being tested.
- ===== Request
- [source,console]
- ----
- PUT /my-index-000001
- {
- "mappings": {
- "properties": {
- "field": {
- "type": "keyword"
- }
- }
- }
- }
- ----
- [source,console]
- ----
- POST /_scripts/painless/_execute
- {
- "script": {
- "source": "doc['field'].value.length() <= params.max_length",
- "params": {
- "max_length": 4
- }
- },
- "context": "filter",
- "context_setup": {
- "index": "my-index-000001",
- "document": {
- "field": "four"
- }
- }
- }
- ----
- // TEST[continued]
- ===== Response
- [source,console-result]
- ----
- {
- "result": true
- }
- ----
- [[painless-execute-core-context]]
- ==== Score context
- The `score` context treats scripts as if they were run inside a `script_score`
- function in a `function_score` query.
- ===== Request
- [source,console]
- ----
- PUT /my-index-000001
- {
- "mappings": {
- "properties": {
- "field": {
- "type": "keyword"
- },
- "rank": {
- "type": "long"
- }
- }
- }
- }
- ----
- [source,console]
- ----
- POST /_scripts/painless/_execute
- {
- "script": {
- "source": "doc['rank'].value / params.max_rank",
- "params": {
- "max_rank": 5.0
- }
- },
- "context": "score",
- "context_setup": {
- "index": "my-index-000001",
- "document": {
- "rank": 4
- }
- }
- }
- ----
- // TEST[continued]
- ===== Response
- [source,console-result]
- ----
- {
- "result": 0.8
- }
- ----
- [[painless-execute-runtime-field-context]]
- ==== Field contexts
- The field contexts treat scripts as if they were run inside the
- {ref}/runtime-search-request.html[`runtime_mappings` section] of a search query.
- You can use field contexts to test scripts for different field types, and then
- include those scripts anywhere that they're supported, such as <<painless-runtime-fields,runtime fields>>.
- Choose a field context based on the data type you want to return.
- [[painless-runtime-boolean]]
- ===== `boolean_field`
- Use the `boolean_field` field context when you want to return a `true`
- or `false` value from a script valuation. {ref}/boolean.html[Boolean fields]
- accept `true` and `false` values, but can also accept strings that are
- interpreted as either true or false.
- Let's say you have data for the top 100 science fiction books of all time. You
- want to write scripts that return a boolean response such as whether books
- exceed a certain page count, or if a book was published after a specific year.
- Consider that your data is structured like this:
- [source,console]
- ----
- PUT /my-index-000001
- {
- "mappings": {
- "properties": {
- "name": {
- "type": "keyword"
- },
- "author": {
- "type": "keyword"
- },
- "release_date": {
- "type": "date"
- },
- "page_count": {
- "type": "double"
- }
- }
- }
- }
- ----
- You can then write a script in the `boolean_field` context that indicates
- whether a book was published before the year 1972:
- [source,console]
- ----
- POST /_scripts/painless/_execute
- {
- "script": {
- "source": """
- emit(doc['release_date'].value.year < 1972);
- """
- },
- "context": "boolean_field",
- "context_setup": {
- "index": "my-index-000001",
- "document": {
- "name": "Dune",
- "author": "Frank Herbert",
- "release_date": "1965-06-01",
- "page_count": 604
- }
- }
- }
- ----
- // TEST[continued]
- Because _Dune_ was published in 1965, the result returns as `true`:
- [source,console-result]
- ----
- {
- "result" : [
- true
- ]
- }
- ----
- Similarly, you could write a script that determines whether the first name of
- an author exceeds a certain number of characters. The following script operates
- on the `author` field to determine whether the author's first name contains at
- least one character, but is less than five characters:
- [source,console]
- ----
- POST /_scripts/painless/_execute
- {
- "script": {
- "source": """
- int space = doc['author'].value.indexOf(' ');
- emit(space > 0 && space < 5);
- """
- },
- "context": "boolean_field",
- "context_setup": {
- "index": "my-index-000001",
- "document": {
- "name": "Dune",
- "author": "Frank Herbert",
- "release_date": "1965-06-01",
- "page_count": 604
- }
- }
- }
- ----
- // TEST[continued]
- Because `Frank` is five characters, the response returns `false` for the script
- valuation:
- [source,console-result]
- ----
- {
- "result" : [
- false
- ]
- }
- ----
- [[painless-runtime-datetime]]
- ===== `date_time`
- Several options are available for using
- <<painless-datetime,using datetime in Painless>>. In this example, you'll
- estimate when a particular author starting writing a book based on its release
- date and the writing speed of that author. The example makes some assumptions,
- but shows to write a script that operates on a date while incorporating
- additional information.
- Add the following fields to your index mapping to get started:
- [source,console]
- ----
- PUT /my-index-000001
- {
- "mappings": {
- "properties": {
- "name": {
- "type": "keyword"
- },
- "author": {
- "type": "keyword"
- },
- "release_date": {
- "type": "date"
- },
- "page_count": {
- "type": "long"
- }
- }
- }
- }
- ----
- The following script makes the incredible assumption that when writing a book,
- authors just write each page and don't do research or revisions. Further, the
- script assumes that the average time it takes to write a page is eight hours.
- The script retrieves the `author` and makes another fantastic assumption to
- either divide or multiply the `pageTime` value based on the author's perceived
- writing speed (yet another wild assumption).
- The script subtracts the release date value (in milliseconds) from the
- calculation of `pageTime` times the `page_count` to determine approximately
- (based on numerous assumptions) when the author began writing the book.
- [source,console]
- ----
- POST /_scripts/painless/_execute
- {
- "script": {
- "source": """
- String author = doc['author'].value;
- long pageTime = 28800000; <1>
- if (author == 'Robert A. Heinlein') {
- pageTime /= 2; <2>
- } else if (author == 'Alastair Reynolds') {
- pageTime *= 2; <3>
- }
- emit(doc['release_date'].value.toInstant().toEpochMilli() - pageTime * doc['page_count'].value);
- """
- },
- "context": "date_field",
- "context_setup": {
- "index": "my-index-000001",
- "document": {
- "name": "Revelation Space",
- "author": "Alastair Reynolds",
- "release_date": "2000-03-15",
- "page_count": 585
- }
- }
- }
- ----
- //TEST[continued]
- <1> Eight hours, represented in milliseconds
- <2> Incredibly fast writing from Robert A. Heinlein
- <3> Alastair Reynolds writes space operas at a much slower speed
- In this case, the author is Alastair Reynolds. Based on a release date of
- `2000-03-15`, the script calculates that the author started writing
- `Revelation Space` on 19 February 1999. Writing a 585 page book in just over one
- year is pretty impressive!
- [source,console-result]
- ----
- {
- "result" : [
- "1999-02-19T00:00:00.000Z"
- ]
- }
- ----
- [[painless-runtime-double]]
- ===== `double_field`
- Use the `double_field` context for {ref}/number.html[numeric data] of type
- `double`. For example, let's say you have sensor data that includes a `voltage`
- field with values like 5.6. After indexing millions of documents, you discover
- that the sensor with model number `QVKC92Q` is under reporting its voltage by a
- factor of 1.7. Rather than reindex your data, you can fix it with a
- runtime field.
- You need to multiply this value, but only for
- sensors that match a specific model number.
- Add the following fields to your index mapping. The `voltage` field is a
- sub-field of the `measures` object.
- [source,console]
- ----
- PUT my-index-000001
- {
- "mappings": {
- "properties": {
- "@timestamp": {
- "type": "date"
- },
- "model_number": {
- "type": "keyword"
- },
- "measures": {
- "properties": {
- "voltage": {
- "type": "double"
- }
- }
- }
- }
- }
- }
- ----
- The following script matches on any documents where the `model_number` equals
- `QVKC92Q`, and then multiplies the `voltage` value by `1.7`. This script is
- useful when you want to select specific documents and only operate on values
- that match the specified criteria.
- [source,console]
- ----
- POST /_scripts/painless/_execute
- {
- "script": {
- "source": """
- if (doc['model_number'].value.equals('QVKC92Q'))
- {emit(1.7 * params._source['measures']['voltage']);}
- else{emit(params._source['measures']['voltage']);}
- """
- },
- "context": "double_field",
- "context_setup": {
- "index": "my-index-000001",
- "document": {
- "@timestamp": 1516470094000,
- "model_number": "QVKC92Q",
- "measures": {
- "voltage": 5.6
- }
- }
- }
- }
- ----
- // TEST[continued]
- The result includes the calculated voltage, which was determined by multiplying
- the original value of `5.6` by `1.7`:
- [source,console-result]
- ----
- {
- "result" : [
- 9.52
- ]
- }
- ----
- [[painless-runtime-geo]]
- ===== `geo_point_field`
- {ref}/geo-point.html[Geo-point] fields accept latitude-longitude pairs. You can
- define a geo-point field in several ways, and include values for latitude and
- longitude in the document for your script.
- If you already have a known geo-point, it's simpler to clearly state the
- positions of `lat` and `lon` in your index mappings.
- [source,console]
- ----
- PUT /my-index-000001/
- {
- "mappings": {
- "properties": {
- "lat": {
- "type": "double"
- },
- "lon": {
- "type": "double"
- }
- }
- }
- }
- ----
- You can then use the `geo_point_field` runtime field context to write a script
- that retrieves the `lat` and `lon` values.
- [source,console]
- ----
- POST /_scripts/painless/_execute
- {
- "script": {
- "source": """
- emit(doc['lat'].value, doc['lon'].value);
- """
- },
- "context": "geo_point_field",
- "context_setup": {
- "index": "my-index-000001",
- "document": {
- "lat": 41.12,
- "lon": -71.34
- }
- }
- }
- ----
- // TEST[continued]
- Because you're working with a geo-point field type, the response includes
- results that are formatted as `coordinates`.
- [source,console-result]
- ----
- {
- "result" : [
- {
- "coordinates" : [
- -71.34000004269183,
- 41.1199999647215
- ],
- "type" : "Point"
- }
- ]
- }
- ----
- [NOTE]
- The emit function for {ref}/geo-point.html[geo-point] fields takes two parameters ordered with
- `lat` before `lon`, but the output GeoJSON format orders the `coordinates` as `[ lon, lat ]`.
- [[painless-runtime-ip]]
- ===== `ip_field`
- The `ip_field` context is useful for data that includes IP addresses of type
- {ref}/ip.html[`ip`]. For example, let's say you have a `message` field from an Apache
- log. This field contains an IP address, but also other data that you don't need.
- You can add the `message` field to your index mappings as a `wildcard` to accept
- pretty much any data you want to put in that field.
- [source,console]
- ----
- PUT /my-index-000001/
- {
- "mappings": {
- "properties": {
- "message": {
- "type": "wildcard"
- }
- }
- }
- }
- ----
- You can then define a runtime script with a grok pattern that extracts
- structured fields out of the `message` field.
- The script matches on the `%{COMMONAPACHELOG}` log pattern, which understands
- the structure of Apache logs. If the pattern matches, the script emits the
- value matching the IP address. If the pattern doesn’t match
- (`clientip != null`), the script just returns the field value without crashing.
- [source,console]
- ----
- POST /_scripts/painless/_execute
- {
- "script": {
- "source": """
- String clientip=grok('%{COMMONAPACHELOG}').extract(doc["message"].value)?.clientip;
- if (clientip != null) emit(clientip);
- """
- },
- "context": "ip_field",
- "context_setup": {
- "index": "my-index-000001",
- "document": {
- "message": "40.135.0.0 - - [30/Apr/2020:14:30:17 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"
- }
- }
- }
- ----
- // TEST[continued]
- The response includes only the IP address, ignoring all of the other data in the
- `message` field.
- [source,console-result]
- ----
- {
- "result" : [
- "40.135.0.0"
- ]
- }
- ----
- [[painless-runtime-keyword]]
- ===== `keyword_field`
- {ref}/keyword.html[Keyword fields] are often used in sorting, aggregations, and
- term-level queries.
- Let's say you have a timestamp. You want to calculate the day of the week based
- on that value and return it, such as `Thursday`. The following request adds a
- `@timestamp` field of type `date` to the index mappings:
- [source,console]
- ----
- PUT /my-index-000001
- {
- "mappings": {
- "properties": {
- "@timestamp": {
- "type": "date"
- }
- }
- }
- }
- ----
- To return the equivalent day of week based on your timestamp, you can create a
- script in the `keyword_field` runtime field context:
- [source,console]
- ----
- POST /_scripts/painless/_execute
- {
- "script": {
- "source": """
- emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ROOT));
- """
- },
- "context": "keyword_field",
- "context_setup": {
- "index": "my-index-000001",
- "document": {
- "@timestamp": "2020-04-30T14:31:43-05:00"
- }
- }
- }
- ----
- // TEST[continued]
- The script operates on the value provided for the `@timestamp` field to
- calculate and return the day of the week:
- [source,console-result]
- ----
- {
- "result" : [
- "Thursday"
- ]
- }
- ----
- [[painless-runtime-long]]
- ===== `long_field`
- Let's say you have sensor data that a `measures` object. This object includes
- a `start` and `end` field, and you want to calculate the difference between
- those values.
- The following request adds a `measures` object to the mappings with two fields,
- both of type `long`:
- [source,console]
- ----
- PUT /my-index-000001/
- {
- "mappings": {
- "properties": {
- "measures": {
- "properties": {
- "start": {
- "type": "long"
- },
- "end": {
- "type": "long"
- }
- }
- }
- }
- }
- }
- ----
- You can then define a script that assigns values to the `start` and `end` fields
- and operate on them. The following script extracts the value for the `end`
- field from the `measures` object and subtracts it from the `start` field:
- [source,console]
- ----
- POST /_scripts/painless/_execute
- {
- "script": {
- "source": """
- emit(doc['measures.end'].value - doc['measures.start'].value);
- """
- },
- "context": "long_field",
- "context_setup": {
- "index": "my-index-000001",
- "document": {
- "measures": {
- "voltage": "4.0",
- "start": "400",
- "end": "8625309"
- }
- }
- }
- }
- ----
- // TEST[continued]
- The response includes the calculated value from the script valuation:
- [source,console-result]
- ----
- {
- "result" : [
- 8624909
- ]
- }
- ----
- [[painless-runtime-composite]]
- ===== `composite_field`
- Let's say you have logging data with a raw `message` field which you want to split
- in multiple sub-fields that can be accessed separately.
- The following request adds a `message` field to the mappings of type `keyword`:
- [source,console]
- ----
- PUT /my-index-000001/
- {
- "mappings": {
- "properties": {
- "message": {
- "type" : "keyword"
- }
- }
- }
- }
- ----
- You can then define a script that splits such message field into subfields using
- the grok function:
- [source,console]
- ----
- POST /_scripts/painless/_execute
- {
- "script": {
- "source": "emit(grok(\"%{COMMONAPACHELOG}\").extract(doc[\"message\"].value));"
- },
- "context": "composite_field",
- "context_setup": {
- "index": "my-index-000001",
- "document": {
- "timestamp":"2020-04-30T14:31:27-05:00",
- "message":"252.0.0.0 - - [30/Apr/2020:14:31:27 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"
- }
- }
- }
- ----
- // TEST[continued]
- The response includes the values that the script emitted:
- [source,console-result]
- ----
- {
- "result" : {
- "composite_field.timestamp" : [
- "30/Apr/2020:14:31:27 -0500"
- ],
- "composite_field.auth" : [
- "-"
- ],
- "composite_field.response" : [
- "200"
- ],
- "composite_field.ident" : [
- "-"
- ],
- "composite_field.httpversion" : [
- "1.0"
- ],
- "composite_field.verb" : [
- "GET"
- ],
- "composite_field.bytes" : [
- "24736"
- ],
- "composite_field.clientip" : [
- "252.0.0.0"
- ],
- "composite_field.request" : [
- "/images/hm_bg.jpg"
- ]
- }
- }
- ----
|