﻿---
title: Watcher transform context
description: Use a Painless script as a watch transform to transform a payload into a new payload for further use in the watch. Transform scripts return an Object...
url: https://www.elastic.co/elastic/docs-builder/docs/2437/reference/scripting-languages/painless/painless-watcher-transform-context
products:
  - Painless
---

# Watcher transform context
Use a Painless script as a [watch transform](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/2437/explore-analyze/alerts-cases/watcher/transform-script) 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**
<definitions>
  <definition term="params (Map, read-only)">
    User-defined parameters passed in as part of the query.
  </definition>
  <definition term="ctx['watch_id'] (String, read-only)">
    The id of the watch.
  </definition>
  <definition term="ctx['id'] (String, read-only)">
    The server generated unique identifier for the run watch.
  </definition>
  <definition term="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.
  </definition>
  <definition term="ctx['execution_time'] (ZonedDateTime, read-only)">
    The time the watch began execution.
  </definition>
  <definition term="ctx['trigger']['scheduled_time'] (ZonedDateTime, read-only)">
    The scheduled trigger time for the watch. This is the time the watch should be executed.
  </definition>
  <definition term="ctx['trigger']['triggered_time'] (ZonedDateTime, read-only)">
    The actual trigger time for the watch. This is the time the watch was triggered for execution.
  </definition>
  <definition term="ctx['payload'] (Map, read-only)">
    The accessible watch data based upon the [watch input](https://docs-v3-preview.elastic.dev/elastic/docs-builder/docs/2437/explore-analyze/alerts-cases/watcher/input).
  </definition>
</definitions>

**Return**
<definitions>
  <definition term="Object">
    The new payload.
  </definition>
</definitions>

**API**
The standard [Painless API](https://www.elastic.co/guide/en/elasticsearch/painless/current/painless-api-reference-shared.html) is available.
**Example**
To run the examples, first follow the steps in [context examples](https://www.elastic.co/elastic/docs-builder/docs/2437/reference/scripting-languages/painless/painless-context-examples).
```json

{
  "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}}"
        }
      }
    }
  }
}
```

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.
```json

{
  "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}}"
        }
      }
    }
  }
}
```

The following example shows scripted watch and action transforms within the context of a complete watch. This watch also uses a scripted [condition](https://www.elastic.co/elastic/docs-builder/docs/2437/reference/scripting-languages/painless/painless-watcher-condition-context).
```json

{
  "watch" : {
    "metadata" : { "high_threshold": 4000, "low_threshold": 1000 },
    "trigger" : { "schedule" : { "interval" : "24h" } },
    "input" : {
      "search" : {
        "request" : {
          "indices" : [ "seats" ],
          "body" : {
            "query" : {
              "term": { "sold": "true"}
            },
            "aggs" : {
              "theatres" : {
                "terms" : { "field" : "play" },
                "aggs" : {
                  "money" : {
                    "sum": {
                      "field" : "cost",
                      "script": {
                       "source": "doc.cost.value * doc.number.value"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "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.
```json

{
  "watch" : {
    "metadata" : { "min_hits": 10 },
    "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"
        }
      }
    }
  }
}
```