"Fossies" - the Fresh Open Source Software Archive

Member "elasticsearch-6.8.23/docs/painless/painless-contexts.asciidoc" (29 Dec 2021, 4731 Bytes) of package /linux/www/elasticsearch-6.8.23-src.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format (assuming AsciiDoc format). Alternatively you can here view or download the uninterpreted source code file. A member file download can also be achieved by clicking within a package contents listing on the according byte size field.

Painless contexts

A Painless script is evaluated within a context. Each context has values that are available as local variables, a whitelist that controls the available classes, and the methods and fields within those classes (API), and if and what type of value is returned.

A Painless script is typically executed within one of the contexts in the table below. Note this is not necessarily a comprehensive list as custom plugins and specialized code may define new ways to use a Painless script.

Name Painless Documentation Elasticsearch Documentation

Ingest processor

Painless Documentation

{ref}/script-processor.html[Elasticsearch Documentation]

Update

Painless Documentation

{ref}/docs-update.html[Elasticsearch Documentation]

Update by query

Painless Documentation

{ref}/docs-update-by-query.html[Elasticsearch Documentation]

Reindex

Painless Documentation

{ref}/docs-reindex.html[Elasticsearch Documentation]

Sort

Painless Documentation

{ref}/search-request-sort.html[Elasticsearch Documentation]

Similarity

Painless Documentation

{ref}/index-modules-similarity.html[Elasticsearch Documentation]

Weight

Painless Documentation

{ref}/index-modules-similarity.html[Elasticsearch Documentation]

Score

Painless Documentation

{ref}/query-dsl-function-score-query.html[Elasticsearch Documentation]

Field

Painless Documentation

{ref}/search-request-script-fields.html[Elasticsearch Documentation]

Filter

Painless Documentation

{ref}/query-dsl-script-query.html[Elasticsearch Documentation]

Minimum should match

Painless Documentation

{ref}/query-dsl-terms-set-query.html[Elasticsearch Documentation]

Metric aggregation initialization

Painless Documentation

{ref}/search-aggregations-metrics-scripted-metric-aggregation.html[Elasticsearch Documentation]

Metric aggregation map

Painless Documentation

{ref}/search-aggregations-metrics-scripted-metric-aggregation.html[Elasticsearch Documentation]

Metric aggregation combine

Painless Documentation

{ref}/search-aggregations-metrics-scripted-metric-aggregation.html[Elasticsearch Documentation]

Metric aggregation reduce

Painless Documentation

{ref}/search-aggregations-metrics-scripted-metric-aggregation.html[Elasticsearch Documentation]

Bucket script aggregation

Painless Documentation

{ref}/search-aggregations-pipeline-bucket-script-aggregation.html[Elasticsearch Documentation]

Bucket selector aggregation

Painless Documentation

{ref}/search-aggregations-pipeline-bucket-selector-aggregation.html[Elasticsearch Documentation]

Watcher condition

Painless Documentation

{ref}/condition-script.html[Elasticsearch Documentation]

Watcher transform

Painless Documentation

{ref}/transform-script.html[Elasticsearch Documentation]

Context examples

To run the examples, index the sample seat data into Elasticsearch. The examples must be run sequentially to work correctly.

  1. Download the seat data. This data set contains booking information for a collection of plays. Each document represents a single seat for a play at a particular theater on a specific date and time.

    Each document contains the following fields:

    theatre ({ref}/keyword.html[keyword])

    The name of the theater the play is in.

    play ({ref}/text.html[text])

    The name of the play.

    actors ({ref}/text.html[text])

    A list of actors in the play.

    row ({ref}/number.html[integer])

    The row of the seat.

    number ({ref}/number.html[integer])

    The number of the seat within a row.

    cost ({ref}/number.html[double])

    The cost of the ticket for the seat.

    sold ({ref}/boolean.html[boolean])

    Whether or not the seat is sold.

    datetime ({ref}/date.html[date])

    The date and time of the play as a date object.

    date ({ref}/keyword.html[keyword])

    The date of the play as a keyword.

    time ({ref}/keyword.html[keyword])

    The time of the play as a keyword.

  2. {defguide}/running-elasticsearch.html[Start] Elasticsearch. Note these examples assume Elasticsearch and Kibana are running locally. To use the Console editor with a remote Kibana instance, click the settings icon and enter the Console URL. To submit a cURL request to a remote Elasticsearch instance, edit the request URL.

  3. Create {ref}/mapping.html[mappings] for the sample data:

    PUT /seats
    {
      "mappings": {
        "seat": {
          "properties": {
            "theatre":  { "type": "keyword" },
            "play":     { "type": "text"    },
            "actors":   { "type": "text"    },
            "row":      { "type": "integer" },
            "number":   { "type": "integer" },
            "cost":     { "type": "double"  },
            "sold":     { "type": "boolean" },
            "datetime": { "type": "date"    },
            "date":     { "type": "keyword" },
            "time":     { "type": "keyword" }
          }
        }
      }
    }
  4. Run the ingest processor context example. This sets up a script ingest processor used on each document as the seat data is indexed.

  5. Index the seat data:

    curl -XPOST localhost:9200/seats/seat/_bulk?pipeline=seats -H "Content-Type: application/x-ndjson" --data-binary "@/<local-file-path>/seats-init.json"

Ingest processor context

Use a Painless script in an {ref}/script-processor.html[ingest processor] to modify documents upon insertion.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

{ref}/mapping-index-field.html[ctx['_index']] (String)

The name of the index.

{ref}/mapping-type-field.html[ctx['_type']] (String)

The type of document within an index.

ctx (Map)

Contains extracted JSON in a Map and List structure for the fields that are part of the document.

Side Effects

{ref}/mapping-index-field.html[ctx['_index']]

Modify this to change the destination index for the current document.

{ref}/mapping-type-field.html[ctx['_type']]

Modify this to change the type for the current document.

ctx (Map)

Modify the values in the Map/List structure to add, modify, or delete the fields of a document.

Return

void

No expected return value.

API

The standard Painless API is available.

Example

To run this example, first follow the steps in context examples.

The seat data contains:

  • A date in the format YYYY-MM-DD where the second digit of both month and day is optional.

  • A time in the format HH:MM* where the second digit of both hours and minutes is optional. The star (*) represents either the String AM or PM.

The following ingest script processes the date and time Strings and stores the result in a datetime field.

String[] split(String s, char d) {                                   (1)
    int count = 0;

    for (char c : s.toCharArray()) {                                 (2)
        if (c == d) {
            ++count;
        }
    }

    if (count == 0) {
        return new String[] {s};                                     (3)
    }

    String[] r = new String[count + 1];                              (4)
    int i0 = 0, i1 = 0;
    count = 0;

    for (char c : s.toCharArray()) {                                 (5)
        if (c == d) {
            r[count++] = s.substring(i0, i1);
            i0 = i1 + 1;
        }

        ++i1;
    }

    r[count] = s.substring(i0, i1);                                  (6)

    return r;
}

String[] dateSplit = split(ctx.date, (char)"-");                     (7)
String year = dateSplit[0].trim();
String month = dateSplit[1].trim();

if (month.length() == 1) {                                           (8)
    month = "0" + month;
}

String day = dateSplit[2].trim();

if (day.length() == 1) {                                             (9)
    day = "0" + day;
}

boolean pm = ctx.time.substring(ctx.time.length() - 2).equals("PM"); (10)
String[] timeSplit = split(
        ctx.time.substring(0, ctx.time.length() - 2), (char)":");    (11)
int hours = Integer.parseInt(timeSplit[0].trim());
int minutes = Integer.parseInt(timeSplit[1].trim());

if (pm) {                                                            (12)
    hours += 12;
}

String dts = year + "-" + month + "-" + day + "T" +
        (hours < 10 ? "0" + hours : "" + hours) + ":" +
        (minutes < 10 ? "0" + minutes : "" + minutes) +
        ":00+08:00";                                                 (13)

ZonedDateTime dt = ZonedDateTime.parse(
         dts, DateTimeFormatter.ISO_OFFSET_DATE_TIME);               (14)
ctx.datetime = dt.getLong(ChronoField.INSTANT_SECONDS)*1000L;        (15)
  1. Creates a split function to split a String type value using a char type value as the delimiter. This is useful for handling the necessity of pulling out the individual pieces of the date and time Strings from the original seat data.

  2. The first pass through each char in the String collects how many new Strings the original is split into.

  3. Returns the original String if there are no instances of the delimiting char.

  4. Creates an array type value to collect the split Strings into based on the number of char delimiters found in the first pass.

  5. The second pass through each char in the String collects each split substring into an array type value of Strings.

  6. Collects the last substring into the array type value of Strings.

  7. Uses the split function to separate the date String from the seat data into year, month, and day Strings.

    Note
    • The use of a String type value to char type value cast as part of the second argument since character literals do not exist.

    • The use of the ctx ingest processor context variable to retrieve the data from the date field.

  8. Appends the string literal "0" value to a single digit month since the format of the seat data allows for this case.

  9. Appends the string literal "0" value to a single digit day since the format of the seat data allows for this case.

  10. Sets the boolean type variable to true if the time String is a time in the afternoon or evening.

    Note
    • The use of the ctx ingest processor context variable to retrieve the data from the time field.

  11. Uses the split function to separate the time String from the seat data into hours and minutes Strings.

    Note
    • The use of the substring method to remove the AM or PM portion of the time String.

    • The use of a String type value to char type value cast as part of the second argument since character literals do not exist.

    • The use of the ctx ingest processor context variable to retrieve the data from the date field.

  12. If the time String is an afternoon or evening value adds the integer literal 12 to the existing hours to move to a 24-hour based time.

  13. Builds a new time String that is parsable using existing API methods.

  14. Creates a ZonedDateTime reference type value by using the API method parse to parse the new time String.

  15. Sets the datetime field datetime to the number of milliseconds retrieved from the API method getLong.

    Note
    • The use of the ctx ingest processor context variable to set the field datetime. Manipulate each document’s fields with the ctx variable as each document is indexed.

Submit the following request:

PUT /_ingest/pipeline/seats
{
    "description": "update datetime for seats",
    "processors": [
      {
        "script": {
          "source": "String[] split(String s, char d) { int count = 0; for (char c : s.toCharArray()) { if (c == d) { ++count; } } if (count == 0) { return new String[] {s}; } String[] r = new String[count + 1]; int i0 = 0, i1 = 0; count = 0; for (char c : s.toCharArray()) { if (c == d) { r[count++] = s.substring(i0, i1); i0 = i1 + 1; } ++i1; } r[count] = s.substring(i0, i1); return r; } String[] dateSplit = split(ctx.date, (char)\"-\"); String year = dateSplit[0].trim(); String month = dateSplit[1].trim(); if (month.length() == 1) { month = \"0\" + month; } String day = dateSplit[2].trim(); if (day.length() == 1) { day = \"0\" + day; } boolean pm = ctx.time.substring(ctx.time.length() - 2).equals(\"PM\"); String[] timeSplit = split(ctx.time.substring(0, ctx.time.length() - 2), (char)\":\"); int hours = Integer.parseInt(timeSplit[0].trim()); int minutes = Integer.parseInt(timeSplit[1].trim()); if (pm) { hours += 12; } String dts = year + \"-\" + month + \"-\" + day + \"T\" + (hours < 10 ? \"0\" + hours : \"\" + hours) + \":\" + (minutes < 10 ? \"0\" + minutes : \"\" + minutes) + \":00+08:00\"; ZonedDateTime dt = ZonedDateTime.parse(dts, DateTimeFormatter.ISO_OFFSET_DATE_TIME); ctx.datetime = dt.getLong(ChronoField.INSTANT_SECONDS)*1000L;"
        }
      }
    ]
}

Update context

Use a Painless script in an {ref}/docs-update.html[update] operation to add, modify, or delete fields within a single document.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

ctx['op'] (String)

The name of the operation.

{ref}/mapping-routing-field.html[ctx['_routing']] (String, read-only)

The value used to select a shard for document storage.

{ref}/mapping-index-field.html[ctx['_index']] (String, read-only)

The name of the index.

{ref}/mapping-type-field.html[ctx['_type']] (String, read-only)

The type of document within an index.

{ref}/mapping-id-field.html[ctx['_id']] (int, read-only)

The unique document id.

ctx['_version'] (int, read-only)

The current version of the document.

ctx['_now'] (long, read-only)

The current timestamp in milliseconds.

{ref}/mapping-source-field.html[ctx['_source']] (Map)

Contains extracted JSON in a Map and List structure for the fields existing in a stored document.

Side Effects

ctx['op']

Use the default of index to update a document. Set to none to specify no operation or delete to delete the current document from the index.

{ref}/mapping-source-field.html[ctx['_source']]

Modify the values in the Map/List structure to add, modify, or delete the fields of a document.

Return

void

No expected return value.

API

The standard Painless API is available.

Update by query context

Use a Painless script in an {ref}/docs-update-by-query.html[update by query] operation to add, modify, or delete fields within each of a set of documents collected as the result of query.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

ctx['op'] (String)

The name of the operation.

{ref}/mapping-routing-field.html[ctx['_routing']] (String, read-only)

The value used to select a shard for document storage.

{ref}/mapping-index-field.html[ctx['_index']] (String, read-only)

The name of the index.

{ref}/mapping-type-field.html[ctx['_type']] (String, read-only)

The type of document within an index.

{ref}/mapping-id-field.html[ctx['_id']] (int, read-only)

The unique document id.

ctx['_version'] (int, read-only)

The current version of the document.

{ref}/mapping-source-field.html[ctx['_source']] (Map)

Contains extracted JSON in a Map and List structure for the fields existing in a stored document.

Side Effects

ctx['op']

Use the default of index to update a document. Set to none to specify no operation or delete to delete the current document from the index.

{ref}/mapping-source-field.html[ctx['_source']]

Modify the values in the Map/List structure to add, modify, or delete the fields of a document.

Return

void

No expected return value.

API

The standard Painless API is available.

Reindex context

Use a Painless script in a {ref}/docs-reindex.html[reindex] operation to add, modify, or delete fields within each document in an original index as its reindexed into a target index.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

ctx['op'] (String)

The name of the operation.

{ref}/mapping-routing-field.html[ctx['_routing']] (String)

The value used to select a shard for document storage.

{ref}/mapping-index-field.html[ctx['_index']] (String)

The name of the index.

{ref}/mapping-type-field.html[ctx['_type']] (String)

The type of document within an index.

{ref}/mapping-id-field.html[ctx['_id']] (int, read-only)

The unique document id.

ctx['_version'] (int)

The current version of the document.

{ref}/mapping-source-field.html[ctx['_source']] (Map)

Contains extracted JSON in a Map and List structure for the fields existing in a stored document.

Side Effects

ctx['op']

Use the default of index to update a document. Set to none to specify no operation or delete to delete the current document from the index.

{ref}/mapping-routing-field.html[ctx['_routing']]

Modify this to change the routing value for the current document.

{ref}/mapping-index-field.html[ctx['_index']]

Modify this to change the destination index for the current document.

{ref}/mapping-type-field.html[ctx['_type']]

Modify this to change the type for the current document.

{ref}/mapping-id-field.html[ctx['_id']]

Modify this to change the id for the current document.

ctx['_version'] (int)

Modify this to modify the version for the current document.

{ref}/mapping-source-field.html[ctx['_source']]

Modify the values in the Map/List structure to add, modify, or delete the fields of a document.

Return

void

No expected return value.

API

The standard Painless API is available.

Sort context

Use a Painless script to {ref}/search-request-sort.html[sort] the documents in a query.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

doc (Map, read-only)

Contains the fields of the current document. For single-valued fields, the value can be accessed via doc['fieldname'].value. For multi-valued fields, this returns the first value; other values can be accessed via doc['fieldname'].get(index)

_score (double read-only)

The similarity score of the current document.

Return

double

The score for the specified document.

API

The standard Painless API is available.

Example

To run this example, first follow the steps in context examples.

To sort results by the length of the theatre field, submit the following query:

GET /_search
{
    "query" : {
        "term" : { "sold" : "true" }
    },
    "sort" : {
        "_script" : {
            "type" : "number",
            "script" : {
                "lang": "painless",
                "source": "doc['theatre'].value.length() * params.factor",
                "params" : {
                    "factor" : 1.1
                }
            },
            "order" : "asc"
        }
    }
}

Similarity context

Use a Painless script to create a {ref}/index-modules-similarity.html[similarity] equation for scoring documents in a query.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

Variables

params (Map, read-only)

User-defined parameters passed in at query-time.

weight (float, read-only)

The weight as calculated by a weight script

query.boost (float, read-only)

The boost value if provided by the query. If this is not provided the value is 1.0f.

field.docCount (long, read-only)

The number of documents that have a value for the current field.

field.sumDocFreq (long, read-only)

The sum of all terms that exist for the current field. If this is not available the value is -1.

field.sumTotalTermFreq (long, read-only)

The sum of occurrences in the index for all the terms that exist in the current field. If this is not available the value is -1.

term.docFreq (long, read-only)

The number of documents that contain the current term in the index.

term.totalTermFreq (long, read-only)

The total occurrences of the current term in the index.

doc.length (long, read-only)

The number of tokens the current document has in the current field. This is decoded from the stored {ref}/norms.html[norms] and may be approximate for long fields

doc.freq (long, read-only)

The number of occurrences of the current term in the current document for the current field.

Note that the query, field, and term variables are also available to the weight context. They are more efficiently used there, as they are constant for all documents.

For queries that contain multiple terms, the script is called once for each term with that term’s calculated weight, and the results are summed. Note that some terms might have a doc.freq value of 0 on a document, for example if a query uses synonyms.

Return

double

The similarity score for the current document.

API

The standard Painless API is available.

Weight context

Use a Painless script to create a {ref}/index-modules-similarity.html[weight] for use in a similarity script. The weight makes up the part of the similarity calculation that is independent of the document being scored, and so can be built up front and cached.

Queries that contain multiple terms calculate a separate weight for each term.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

query.boost (float, read-only)

The boost value if provided by the query. If this is not provided the value is 1.0f.

field.docCount (long, read-only)

The number of documents that have a value for the current field.

field.sumDocFreq (long, read-only)

The sum of all terms that exist for the current field. If this is not available the value is -1.

field.sumTotalTermFreq (long, read-only)

The sum of occurrences in the index for all the terms that exist in the current field. If this is not available the value is -1.

term.docFreq (long, read-only)

The number of documents that contain the current term in the index.

term.totalTermFreq (long, read-only)

The total occurrences of the current term in the index.

Return

double

A scoring factor used across all documents.

API

The standard Painless API is available.

Score context

Use a Painless script in a {ref}/query-dsl-function-score-query.html[function score] to apply a new score to documents returned from a query.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

doc (Map, read-only)

Contains the fields of the current document. For single-valued fields, the value can be accessed via doc['fieldname'].value. For multi-valued fields, this returns the first value; other values can be accessed via doc['fieldname'].get(index)

_score (double read-only)

The similarity score of the current document.

Return

double

The score for the current document.

API

The standard Painless API is available.

Example

To run this example, first follow the steps in context examples.

The following query finds all unsold seats, with lower 'row' values scored higher.

GET /seats/_search
{
    "query": {
        "function_score": {
            "query": {
                "match": { "sold": "false" }
            },
            "script_score" : {
                "script" : {
                  "source": "1.0 / doc['row'].value"
                }
            }
        }
    }
}

Field context

Use a Painless script to create a {ref}/search-request-script-fields.html[script field] to return a customized value for each document in the results of a query.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

doc (Map, read-only)

Contains the fields of the specified document where each field is a List of values.

{ref}/mapping-source-field.html[ctx['_source']] (Map)

Contains extracted JSON in a Map and List structure for the fields existing in a stored document.

_score (double read-only)

The original score of the specified document.

Return

Object

The customized value for each document.

API

The standard Painless API is available.

Example

To run this example, first follow the steps in context examples.

You can then use these two example scripts to compute custom information for each search hit and output it to two new fields.

The first script gets the doc value for the datetime field and calls the getDayOfWeek function to determine the corresponding day of the week.

doc['datetime'].value.getDayOfWeek();

The second script calculates the number of actors. Actors' names are stored as a text array in the actors field.

params['_source']['actors'].length;                        (1)
  1. By default, doc values are not available for text fields. However, you can still calculate the number of actors by extracting actors from _source. Note that params['_source']['actors'] is a list.

Submit the following request:

GET seats/_search
{
    "query" : {
        "match_all": {}
    },
    "script_fields" : {
        "day-of-week" : {
            "script" : {
                "source": "doc['datetime'].value.getDayOfWeek()"
            }
        },
        "number-of-actors" : {
            "script" : {
                "source": "params['_source']['actors'].length"
            }
        }
    }
}

Filter context

Use a Painless script as a {ref}/query-dsl-script-query.html[filter] in a query to include and exclude documents.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

doc (Map, read-only)

Contains the fields of the current document where each field is a List of values.

Return

boolean

Return true if the current document should be returned as a result of the query, and false otherwise.

API

The standard Painless API is available.

Example

To run this example, first follow the steps in context examples.

This script finds all unsold documents that cost less than $18.

doc['sold'].value == false && doc['cost'].value < 18

Defining cost as a script parameter enables the cost to be configured in the script query request. For example, the following request finds all available theatre seats for evening performances that are under $18.

GET evening/_search
{
    "query": {
        "bool" : {
            "filter" : {
                "script" : {
                    "script" : {
                        "source" : "doc['sold'].value == false && doc['cost'].value < params.cost",
                        "params" : {
                            "cost" : 18
                        }
                    }
                }
            }
        }
    }
}

Minimum should match context

Use a Painless script to specify the {ref}/query-dsl-terms-set-query.html[minimum] number of terms that a specified field needs to match with for a document to be part of the query results.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

params['num_terms'] (int, read-only)

The number of terms specified to match with.

doc (Map, read-only)

Contains the fields of the current document where each field is a List of values.

Return

int

The minimum number of terms required to match the current document.

API

The standard Painless API is available.

Example

To run this example, first follow the steps in context examples.

Imagine that you want to find seats to performances by your favorite actors. You have a list of favorite actors in mind, and you want to find performances where the cast includes at least a certain number of them. terms_set query with minimum_should_match_script is a way to accomplish this. To make the query request more configurable, you can define min_actors_to_see as a script parameter.

To ensure that the parameter min_actors_to_see doesn’t exceed the number of favorite actors, you can use num_term`s to get the number of actors in the list and `Math.min to get the lesser of the two.

Math.min(params['num_terms'], params['min_actors_to_see'])

The following request finds seats to performances with at least two of the three specified actors.

GET seats/_search
{
    "query" : {
        "terms_set": {
            "actors" : {
                "terms" : ["smith", "earns", "black"],
                "minimum_should_match_script": {
                    "source": "Math.min(params['num_terms'], params['min_actors_to_see'])",
                    "params" : {
                        "min_actors_to_see" : 2
                    }
                }
            }
        }
    }
}

Metric aggregation initialization context

Use a Painless script to {ref}/search-aggregations-metrics-scripted-metric-aggregation.html[initialize] values for use in a scripted metric aggregation. An initialization script is run prior to document collection once per shard and is optional as part of the full metric aggregation.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

state (Map)

Empty Map used to add values for use in a map script.

Side Effects

state (Map)

Add values to this Map to for use in a map. Additional values must be of the type Map, List, String or primitive.

Return

void

No expected return value.

API

The standard Painless API is available.

Metric aggregation map context

Use a Painless script to {ref}/search-aggregations-metrics-scripted-metric-aggregation.html[map] values for use in a scripted metric aggregation. A map script is run once per collected document following an optional initialization script and is required as part of a full metric aggregation.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

state (Map)

Map used to add values for processing in a combine script or to be returned from the aggregation.

doc (Map, read-only)

Contains the fields of the current document where each field is a List of values.

_score (double read-only)

The similarity score of the current document.

Side Effects

state (Map)

Use this Map to add values for processing in a combine script. Additional values must be of the type Map, List, String or primitive. The same state Map is shared between all aggregated documents on a given shard. If an initialization script is provided as part of the aggregation then values added from the initialization script are available. If no combine script is specified, values must be directly stored in state in a usable form. If no combine script and no reduce script are specified, the state values are used as the result.

Return

void

No expected return value.

API

The standard Painless API is available.

Metric aggregation combine context

Use a Painless script to {ref}/search-aggregations-metrics-scripted-metric-aggregation.html[combine] values for use in a scripted metric aggregation. A combine script is run once per shard following a map script and is optional as part of a full metric aggregation.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

state (Map)

Map with values available from the prior map script.

Return

List, Map, String, or primitive

A value collected for use in a reduce script. If no reduce script is specified, the value is used as part of the result.

API

The standard Painless API is available.

Metric aggregation reduce context

Use a Painless script to {ref}/search-aggregations-metrics-scripted-metric-aggregation.html[reduce] values to produce the result of a scripted metric aggregation. A reduce script is run once on the coordinating node following a combine script (or a map script if no combine script is specified) and is optional as part of a full metric aggregation.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

states (Map)

Map with values available from the prior combine script (or a map script if no combine script is specified).

Return

List, Map, String, or primitive

A value used as the result.

API

The standard Painless API is available.

Bucket script aggregation context

Use a Painless script in an {ref}/search-aggregations-pipeline-bucket-script-aggregation.html[bucket_script pipeline aggregation] to calculate a value as a result in a bucket.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query. The parameters include values defined as part of the buckets_path.

Return

numeric

The calculated value as the result.

API

The standard Painless API is available.

Example

To run this example, first follow the steps in context examples.

The painless context in a bucket_script aggregation provides a params map. This map contains both user-specified custom values, as well as the values from other aggregations specified in the buckets_path property.

This example takes the values from a min and max aggregation, calculates the difference, and adds the user-specified base_cost to the result:

(params.max - params.min) + params.base_cost

Note that the values are extracted from the params map. In context, the aggregation looks like this:

GET /seats/_search
{
  "size": 0,
  "aggs": {
    "theatres": {
      "terms": {
        "field": "theatre",
        "size": 10
      },
      "aggs": {
        "min_cost": {
          "min": {
            "field": "cost"
          }
        },
        "max_cost": {
          "max": {
            "field": "cost"
          }
        },
        "spread_plus_base": {
          "bucket_script": {
            "buckets_path": { (1)
              "min": "min_cost",
              "max": "max_cost"
            },
            "script": {
              "params": {
                "base_cost": 5 (2)
              },
              "source": "(params.max - params.min) + params.base_cost"
            }
          }
        }
      }
    }
  }
}
  1. The buckets_path points to two aggregations (min_cost, max_cost) and adds min/max variables to the params map

  2. The user-specified base_cost is also added to the script’s params map

Bucket selector aggregation context

Use a Painless script in an {ref}/search-aggregations-pipeline-bucket-selector-aggregation.html[bucket_selector aggregation] to determine if a bucket should be retained or filtered out.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query. The parameters include values defined as part of the buckets_path.

Return

boolean

True if the the bucket should be retained, false if the bucket should be filtered out.

API

To run this example, first follow the steps in context examples.

The painless context in a bucket_selector aggregation provides a params map. This map contains both user-specified custom values, as well as the values from other aggregations specified in the buckets_path property.

Unlike some other aggregation contexts, the bucket_selector context must return a boolean true or false.

This example finds the max of each bucket, adds a user-specified base_cost, and retains all of the buckets that are greater than 10.

params.max + params.base_cost > 10

Note that the values are extracted from the params map. The script is in the form of an expression that returns true or false. In context, the aggregation looks like this:

GET /seats/_search
{
  "size": 0,
  "aggs": {
    "theatres": {
      "terms": {
        "field": "theatre",
        "size": 10
      },
      "aggs": {
        "max_cost": {
          "max": {
            "field": "cost"
          }
        },
        "filtering_agg": {
          "bucket_selector": {
            "buckets_path": { (1)
              "max": "max_cost"
            },
            "script": {
              "params": {
                "base_cost": 5 (2)
              },
              "source": "params.max + params.base_cost > 10"
            }
          }
        }
      }
    }
  }
}
  1. The buckets_path points to the max aggregations (max_cost) and adds max variables to the params map

  2. The user-specified base_cost is also added to the params map

Analysis Predicate Context

Use a painless script to determine whether or not the current token in an analysis chain matches a predicate.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

token.term (CharSequence, read-only)

The characters of the current token

token.position (int, read-only)

The position of the current token

token.positionIncrement (int, read-only)

The position increment of the current token

token.positionLength (int, read-only)

The position length of the current token

token.startOffset (int, read-only)

The start offset of the current token

token.endOffset (int, read-only)

The end offset of the current token

token.type (String, read-only)

The type of the current token

token.keyword ('boolean`, read-only)

Whether or not the current token is marked as a keyword

Return

boolean

Whether or not the current token matches the predicate

API

The standard Painless API is available.

Watcher condition context

Use a Painless script as a {ref}/condition-script.html[watch condition] that determines whether to execute a watch or a particular action within a watch. Condition scripts return a Boolean value to indicate the status of the condition.

The following variables are available in all watcher contexts.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

ctx['watch_id'] (String, read-only)

The id of the watch.

ctx['id'] (String, read-only)

The server generated unique identifer for the run watch.

ctx['metadata'] (Map, read-only)

Metadata can be added to the top level of the watch definition. This is user defined and is typically used to consolidate duplicate values in a watch.

ctx['execution_time'] (ZonedDateTime, read-only)

The time the watch began execution.

ctx['trigger']['scheduled_time'] (ZonedDateTime, read-only)

The scheduled trigger time for the watch. This is the time the watch should be executed.

ctx['trigger']['triggered_time'] (ZonedDateTime, read-only)

The actual trigger time for the watch. This is the time the watch was triggered for execution.

ctx['payload'] (Map, read-only)

The accessible watch data based upon the {ref}/input.html[watch input].

API

The standard Painless API is available.

To run this example, first follow the steps in context examples.

Return

boolean

Expects true if the condition is met, and false if it is not.

API

The standard Painless API is available.

Example

POST _watcher/watch/_execute
{
  "watch" : {
    "trigger" : { "schedule" : { "interval" : "24h" } },
    "input" : {
      "search" : {
        "request" : {
          "indices" : [ "seats" ],
          "body" : {
            "query" : {
              "term": { "sold": "true"}
            },
            "aggs" : {
              "theatres" : {
                "terms" : { "field" : "play" },
                "aggs" : {
                  "money" : {
                    "sum": { "field" : "cost" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "condition" : {
      "script" :
      """
        return ctx.payload.aggregations.theatres.buckets.stream()       (1)
          .filter(theatre -> theatre.money.value < 15000 ||
                             theatre.money.value > 50000)               (2)
          .count() > 0                                                  (3)
      """
    },
    "actions" : {
      "my_log" : {
        "logging" : {
          "text" : "The output of the search was : {{ctx.payload.aggregations.theatres.buckets}}"
        }
      }
    }
  }
}
  1. The Java Stream API is used in the condition. This API allows manipulation of the elements of the list in a pipeline.

  2. The stream filter removes items that do not meet the filter criteria.

  3. If there is at least one item in the list, the condition evaluates to true and the watch is executed.

The following action condition script controls execution of the my_log action based on the value of the seats sold for the plays in the data set. The script aggregates the total sold seats for each play and returns true if there is at least one play that has sold over $50,000.

POST _watcher/watch/_execute
{
  "watch" : {
    "trigger" : { "schedule" : { "interval" : "24h" } },
    "input" : {
      "search" : {
        "request" : {
          "indices" : [ "seats" ],
          "body" : {
            "query" : {
              "term": { "sold": "true"}
            },
            "aggs" : {
              "theatres" : {
                "terms" : { "field" : "play" },
                "aggs" : {
                  "money" : {
                    "sum": { "field" : "cost" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "actions" : {
      "my_log" : {
        "condition": {                                                (1)
          "script" :
          """
            return ctx.payload.aggregations.theatres.buckets.stream()
              .anyMatch(theatre -> theatre.money.value > 50000)       (2)
          """
        },
        "logging" : {
          "text" : "At least one play has grossed over $50,000: {{ctx.payload.aggregations.theatres.buckets}}"
        }
      }
    }
  }
}

This example uses a nearly identical condition as the previous example. The differences below are subtle and are worth calling out.

  1. The location of the condition is no longer at the top level, but is within an individual action.

  2. Instead of a filter, anyMatch is used to return a boolean value

The following example shows scripted watch and action conditions within the context of a complete watch. This watch also uses a scripted transform.

POST _watcher/watch/_execute
{
  "watch" : {
    "metadata" : { "high_threshold": 50000, "low_threshold": 15000 },
    "trigger" : { "schedule" : { "interval" : "24h" } },
    "input" : {
      "search" : {
        "request" : {
          "indices" : [ "seats" ],
          "body" : {
            "query" : {
              "term": { "sold": "true"}
            },
            "aggs" : {
              "theatres" : {
                "terms" : { "field" : "play" },
                "aggs" : {
                  "money" : {
                    "sum": { "field" : "cost" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "condition" : {
      "script" :
      """
        return ctx.payload.aggregations.theatres.buckets.stream()
          .anyMatch(theatre -> theatre.money.value < ctx.metadata.low_threshold ||
                               theatre.money.value > ctx.metadata.high_threshold)
      """
    },
    "transform" : {
      "script":
      """
        return [
          'money_makers': ctx.payload.aggregations.theatres.buckets.stream()
            .filter(t -> {
                return t.money.value > ctx.metadata.high_threshold
            })
            .map(t -> {
                return ['play': t.key, 'total_value': t.money.value ]
            }).collect(Collectors.toList()),
          'duds' : ctx.payload.aggregations.theatres.buckets.stream()
            .filter(t -> {
                return t.money.value < ctx.metadata.low_threshold
            })
            .map(t -> {
                return ['play': t.key, 'total_value': t.money.value ]
            }).collect(Collectors.toList())
          ]
      """
    },
    "actions" : {
      "log_money_makers" : {
        "condition": {
          "script" : "return ctx.payload.money_makers.size() > 0"
        },
        "transform": {
          "script" :
          """
          def formatter = NumberFormat.getCurrencyInstance();
          return [
            'plays_value': ctx.payload.money_makers.stream()
              .map(t-> formatter.format(t.total_value) + ' for the play ' + t.play)
              .collect(Collectors.joining(", "))
          ]
          """
        },
        "logging" : {
          "text" : "The following plays contain the highest grossing total income: {{ctx.payload.plays_value}}"
        }
      },
      "log_duds" : {
        "condition": {
          "script" : "return ctx.payload.duds.size() > 0"
        },
        "transform": {
          "script" :
          """
          def formatter = NumberFormat.getCurrencyInstance();
          return [
            'plays_value': ctx.payload.duds.stream()
              .map(t-> formatter.format(t.total_value) + ' for the play ' + t.play)
              .collect(Collectors.joining(", "))
          ]
          """
        },
        "logging" : {
          "text" : "The following plays need more advertising due to their low total income: {{ctx.payload.plays_value}}"
        }
      }
    }
  }
}

The following example shows the use of metadata and transforming dates into a readable format.

POST _watcher/watch/_execute
{
  "watch" : {
    "metadata" : { "min_hits": 10000 },
    "trigger" : { "schedule" : { "interval" : "24h" } },
    "input" : {
      "search" : {
        "request" : {
          "indices" : [ "seats" ],
          "body" : {
            "query" : {
              "term": { "sold": "true"}
            },
            "aggs" : {
              "theatres" : {
                "terms" : { "field" : "play" },
                "aggs" : {
                  "money" : {
                    "sum": { "field" : "cost" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "condition" : {
      "script" :
      """
        return ctx.payload.hits.total > ctx.metadata.min_hits
      """
    },
    "transform" : {
      "script" :
      """
        def theDate = ZonedDateTime.ofInstant(ctx.execution_time.toInstant(), ctx.execution_time.getZone());
        return ['human_date': DateTimeFormatter.RFC_1123_DATE_TIME.format(theDate),
                'aggregations': ctx.payload.aggregations]
      """
    },
    "actions" : {
      "my_log" : {
        "logging" : {
          "text" : "The watch was successfully executed on {{ctx.payload.human_date}} and contained {{ctx.payload.aggregations.theatres.buckets.size}} buckets"
        }
      }
    }
  }
}

Watcher transform context

Use a Painless script as a {ref}/transform-script.html[watch transform] to transform a payload into a new payload for further use in the watch. Transform scripts return an Object value of the new payload.

The following variables are available in all watcher contexts.

Variables

params (Map, read-only)

User-defined parameters passed in as part of the query.

ctx['watch_id'] (String, read-only)

The id of the watch.

ctx['id'] (String, read-only)

The server generated unique identifer for the run watch.

ctx['metadata'] (Map, read-only)

Metadata can be added to the top level of the watch definition. This is user defined and is typically used to consolidate duplicate values in a watch.

ctx['execution_time'] (ZonedDateTime, read-only)

The time the watch began execution.

ctx['trigger']['scheduled_time'] (ZonedDateTime, read-only)

The scheduled trigger time for the watch. This is the time the watch should be executed.

ctx['trigger']['triggered_time'] (ZonedDateTime, read-only)

The actual trigger time for the watch. This is the time the watch was triggered for execution.

ctx['payload'] (Map, read-only)

The accessible watch data based upon the {ref}/input.html[watch input].

API

The standard Painless API is available.

To run this example, first follow the steps in context examples.

Return

Object

The new payload.

API

The standard Painless API is available.

Example

POST _watcher/watch/_execute
{
  "watch" : {
    "trigger" : { "schedule" : { "interval" : "24h" } },
    "input" : {
      "search" : {
        "request" : {
          "indices" : [ "seats" ],
          "body" : {
            "query" : { "term": { "sold": "true"} },
            "aggs" : {
              "theatres" : {
                "terms" : { "field" : "play" },
                "aggs" : {
                  "money" : {
                    "sum": { "field" : "cost" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "transform" : {
      "script":
      """
        return [
          'money_makers': ctx.payload.aggregations.theatres.buckets.stream()  (1)
            .filter(t -> {                                                    (2)
                return t.money.value > 50000
            })
            .map(t -> {                                                       (3)
                return ['play': t.key, 'total_value': t.money.value ]
            }).collect(Collectors.toList()),                                  (4)
          'duds' : ctx.payload.aggregations.theatres.buckets.stream()         (5)
            .filter(t -> {
                return t.money.value < 15000
            })
            .map(t -> {
                return ['play': t.key, 'total_value': t.money.value ]
            }).collect(Collectors.toList())
          ]
      """
    },
    "actions" : {
      "my_log" : {
        "logging" : {
          "text" : "The output of the payload was transformed to {{ctx.payload}}"
        }
      }
    }
  }
}
  1. The Java Stream API is used in the transform. This API allows manipulation of the elements of the list in a pipeline.

  2. The stream filter removes items that do not meet the filter criteria.

  3. The stream map transforms each element into a new object.

  4. The collector reduces the stream to a java.util.List.

  5. This is done again for the second set of values in the transform.

The following action transform changes each value in the mod_log action into a String. This transform does not change the values in the unmod_log action.

POST _watcher/watch/_execute
{
  "watch" : {
    "trigger" : { "schedule" : { "interval" : "24h" } },
    "input" : {
      "search" : {
        "request" : {
          "indices" : [ "seats" ],
          "body" : {
            "query" : {
              "term": { "sold": "true"}
            },
            "aggs" : {
              "theatres" : {
                "terms" : { "field" : "play" },
                "aggs" : {
                  "money" : {
                    "sum": { "field" : "cost" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "actions" : {
      "mod_log" : {
        "transform": {                                                                (1)
          "script" :
          """
          def formatter = NumberFormat.getCurrencyInstance();
          return [
            'msg': ctx.payload.aggregations.theatres.buckets.stream()
              .map(t-> formatter.format(t.money.value) + ' for the play ' + t.key)
              .collect(Collectors.joining(", "))
          ]
          """
        },
        "logging" : {
          "text" : "The output of the payload was transformed to: {{ctx.payload.msg}}"
        }
      },
      "unmod_log" : {                                                                 (2)
        "logging" : {
          "text" : "The output of the payload was not transformed and this value should not exist: {{ctx.payload.msg}}"
        }
      }
    }
  }
}

This example uses the streaming API in a very similar manner. The differences below are subtle and worth calling out.

  1. The location of the transform is no longer at the top level, but is within an individual action.

  2. A second action that does not transform the payload is given for reference.

The following example shows scripted watch and action transforms within the context of a complete watch. This watch also uses a scripted condition.

POST _watcher/watch/_execute
{
  "watch" : {
    "metadata" : { "high_threshold": 50000, "low_threshold": 15000 },
    "trigger" : { "schedule" : { "interval" : "24h" } },
    "input" : {
      "search" : {
        "request" : {
          "indices" : [ "seats" ],
          "body" : {
            "query" : {
              "term": { "sold": "true"}
            },
            "aggs" : {
              "theatres" : {
                "terms" : { "field" : "play" },
                "aggs" : {
                  "money" : {
                    "sum": { "field" : "cost" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "condition" : {
      "script" :
      """
        return ctx.payload.aggregations.theatres.buckets.stream()
          .anyMatch(theatre -> theatre.money.value < ctx.metadata.low_threshold ||
                               theatre.money.value > ctx.metadata.high_threshold)
      """
    },
    "transform" : {
      "script":
      """
        return [
          'money_makers': ctx.payload.aggregations.theatres.buckets.stream()
            .filter(t -> {
                return t.money.value > ctx.metadata.high_threshold
            })
            .map(t -> {
                return ['play': t.key, 'total_value': t.money.value ]
            }).collect(Collectors.toList()),
          'duds' : ctx.payload.aggregations.theatres.buckets.stream()
            .filter(t -> {
                return t.money.value < ctx.metadata.low_threshold
            })
            .map(t -> {
                return ['play': t.key, 'total_value': t.money.value ]
            }).collect(Collectors.toList())
          ]
      """
    },
    "actions" : {
      "log_money_makers" : {
        "condition": {
          "script" : "return ctx.payload.money_makers.size() > 0"
        },
        "transform": {
          "script" :
          """
          def formatter = NumberFormat.getCurrencyInstance();
          return [
            'plays_value': ctx.payload.money_makers.stream()
              .map(t-> formatter.format(t.total_value) + ' for the play ' + t.play)
              .collect(Collectors.joining(", "))
          ]
          """
        },
        "logging" : {
          "text" : "The following plays contain the highest grossing total income: {{ctx.payload.plays_value}}"
        }
      },
      "log_duds" : {
        "condition": {
          "script" : "return ctx.payload.duds.size() > 0"
        },
        "transform": {
          "script" :
          """
          def formatter = NumberFormat.getCurrencyInstance();
          return [
            'plays_value': ctx.payload.duds.stream()
              .map(t-> formatter.format(t.total_value) + ' for the play ' + t.play)
              .collect(Collectors.joining(", "))
          ]
          """
        },
        "logging" : {
          "text" : "The following plays need more advertising due to their low total income: {{ctx.payload.plays_value}}"
        }
      }
    }
  }
}

The following example shows the use of metadata and transforming dates into a readable format.

POST _watcher/watch/_execute
{
  "watch" : {
    "metadata" : { "min_hits": 10000 },
    "trigger" : { "schedule" : { "interval" : "24h" } },
    "input" : {
      "search" : {
        "request" : {
          "indices" : [ "seats" ],
          "body" : {
            "query" : {
              "term": { "sold": "true"}
            },
            "aggs" : {
              "theatres" : {
                "terms" : { "field" : "play" },
                "aggs" : {
                  "money" : {
                    "sum": { "field" : "cost" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "condition" : {
      "script" :
      """
        return ctx.payload.hits.total > ctx.metadata.min_hits
      """
    },
    "transform" : {
      "script" :
      """
        def theDate = ZonedDateTime.ofInstant(ctx.execution_time.toInstant(), ctx.execution_time.getZone());
        return ['human_date': DateTimeFormatter.RFC_1123_DATE_TIME.format(theDate),
                'aggregations': ctx.payload.aggregations]
      """
    },
    "actions" : {
      "my_log" : {
        "logging" : {
          "text" : "The watch was successfully executed on {{ctx.payload.human_date}} and contained {{ctx.payload.aggregations.theatres.buckets.size}} buckets"
        }
      }
    }
  }
}