[DOCS] Add watcher context examples (#36565)

This commit is contained in:
Michael Basnight 2019-01-14 20:44:21 -06:00 committed by GitHub
parent 3bc0711b90
commit c0368a2086
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 466 additions and 55 deletions

View File

@ -1,38 +1,136 @@
[[painless-watcher-condition-context]]
=== Watcher condition context
Use a Painless script as a {xpack-ref}/condition-script.html[watcher condition]
to test if a response is necessary.
Use a Painless script as a {xpack-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.
*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['execution_time']` (`ZonedDateTime`, read-only)::
The start time for the watch.
`ctx['trigger']['scheduled_time']` (`ZonedDateTime`, read-only)::
The scheduled trigger time for the watch.
`ctx['trigger']['triggered_time']` (`ZonedDateTime`, read-only)::
The actual trigger time for the watch.
`ctx['metadata']` (`Map`, read-only)::
Any metadata associated with the watch.
`ctx['payload']` (`Map`, read-only)::
The accessible watch data based upon the
{xpack-ref}/input.html[watch input].
include::painless-watcher-context-variables.asciidoc[]
*Return*
`boolean`::
Expects `true` if the condition is met, and `false` otherwise.
Expects `true` if the condition is met, and `false` if it is not.
*API*
The standard <<painless-api-reference, Painless API>> is available.
The standard <<painless-api-reference, Painless API>> is available.
*Example*
[source,Painless]
----
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.
[source,Painless]
----
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
<<painless-watcher-transform-context, transform>>.
include::painless-watcher-context-example.asciidoc[]

View File

@ -0,0 +1,157 @@
[source,Painless]
----
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.
[source,Painless]
----
POST _xpack/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"
}
}
}
}
}
----

View File

@ -0,0 +1,40 @@
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
{xpack-ref}/input.html[watch input].
*API*
The standard <<painless-api-reference, Painless API>> is available.
To run this example, first follow the steps in
<<painless-context-examples, context examples>>.

View File

@ -1,33 +1,11 @@
[[painless-watcher-transform-context]]
=== Watcher transform context
Use a Painless script to {xpack-ref}/transform-script.html[transform] watch
data into a new payload for use in a response to a condition.
*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['execution_time']` (`ZonedDateTime`, read-only)::
The start time for the watch.
`ctx['trigger']['scheduled_time']` (`ZonedDateTime`, read-only)::
The scheduled trigger time for the watch.
`ctx['trigger']['triggered_time']` (`ZonedDateTime`, read-only)::
The actual trigger time for the watch.
`ctx['metadata']` (`Map`, read-only)::
Any metadata associated with the watch.
`ctx['payload']` (`Map`, read-only)::
The accessible watch data based upon the
{xpack-ref}/input.html[watch input].
Use a Painless script as a {xpack-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.
include::painless-watcher-context-variables.asciidoc[]
*Return*
@ -36,4 +14,142 @@ data into a new payload for use in a response to a condition.
*API*
The standard <<painless-api-reference, Painless API>> is available.
The standard <<painless-api-reference, Painless API>> is available.
*Example*
[source,Painless]
----
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.
[source,Painless]
----
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
<<painless-watcher-condition-context, condition>>.
include::painless-watcher-context-example.asciidoc[]

View File

@ -42,4 +42,4 @@ Queries that contain multiple terms calculate a separate weight for each term.
*API*
The standard <<painless-api-reference, Painless API>> is available.
The standard <<painless-api-reference, Painless API>> is available.