Skip to content

[DOCS] Add watcher examples#36565

Merged
hub-cap merged 9 commits intoelastic:masterfrom
hub-cap:painless_watcher_condition
Jan 15, 2019
Merged

[DOCS] Add watcher examples#36565
hub-cap merged 9 commits intoelastic:masterfrom
hub-cap:painless_watcher_condition

Conversation

@hub-cap
Copy link
Copy Markdown
Contributor

@hub-cap hub-cap commented Dec 12, 2018

No description provided.

@hub-cap hub-cap added >docs General docs changes :Core/Infra/Scripting Scripting abstractions, Painless, and Mustache v7.0.0 v6.6.0 labels Dec 12, 2018
@hub-cap hub-cap requested a review from jdconrad December 12, 2018 17:52
@elasticmachine
Copy link
Copy Markdown
Collaborator

Pinging @elastic/es-core-infra

You can then use the example watch to retrieve and manipulate total spend
calculations for each play present in the example data set.

The watcher condition can be used in two places, and below are examples of each.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont really like this sentence, and would like advice on whether its needed.

@hub-cap
Copy link
Copy Markdown
Contributor Author

hub-cap commented Dec 12, 2018

I hope this is in the right direction. I would love some feedback from the docs team and @jdconrad on whether this is good or not. Next up is transform, and I will be doing something similar to this for transform. Feedback welcome!!!

@jdconrad jdconrad requested a review from debadair December 12, 2018 18:00
@jdconrad
Copy link
Copy Markdown
Contributor

Adding @debadair for docs review.

@jdconrad
Copy link
Copy Markdown
Contributor

@hub-cap Thanks for the work so far! Much appreciated.

@hub-cap
Copy link
Copy Markdown
Contributor Author

hub-cap commented Dec 12, 2018

while munching a sandwich at lunch I decided I did not like these docs. I think they should be structured differently. Give me some time to formulate, but lets not worry about review for now.

@hub-cap
Copy link
Copy Markdown
Contributor Author

hub-cap commented Dec 12, 2018

So my thought is to consolidate both of the watcher contexts into 1 document. Both of the contexts share the same variables accessible within the context. So I figured I can use a single example to show both the transform and the condition, as well as highlight all the values within the context. Its going to take me a little while to glean all the values that the context may have, so it could be another day until I get an update here. Lets wait on any review until then.

@honzakral
Copy link
Copy Markdown
Contributor

Thank you so much for these examples, they will be super useful to users trying to orient themselves.

For completeness I have a similar (in functionality) pattern that I find using over an over in watchers that collect potentially multiple events (which is vast majority in many use cases):

def events = [];
for (def host: ctx.payload.aggregations.hosts.buckets) {
  if (host.max_cpu.value > 90) {
    events.add([
      // add generic metadata about the event
      '_id': ctx.watch_id + '-' + host.key,
      'watch': ctx.watch_id,
      '@timestamp': ctx.trigger.triggered_time,
      // and all the values that contributed to it being an event
      'host': host.key,
      'cpu': host.max_cpu.value
    ]);
  }
}
ctx.payload.events = events;
return events.size() > 0;

I store the resulting list on the payload so that I don't have to repeat the same exact logic in a transform down the line when I want to act. I can pass this directly to an index action or use it in a template for logging or email actions, alternatively pass it to a webhook via {{#toJson}}ctx.payload.events{{/toJson}} keeping all my logic in a single location.

Because the stream API is not as well known among people not fluent in Java I purposefully avoid it and stick with the super basic forloop which a larger number of peoplecan read. (hopefully that will change if we document it here though :) )

Refactor out the variables, and reuse them in the 2 contexts, as well as
adding a complete example to each of the actual docs.
@hub-cap
Copy link
Copy Markdown
Contributor Author

hub-cap commented Dec 17, 2018

@honzakral im considering adding this for entry, i just need to find out where is a good place for it. Maybe I can add it in the "complete examples" section.

@debadair and @jdconrad, I have restructured it such that the variables section now has its own file and that is imported into both of the context examples. They also both have a "complete example" imported in at the end so the user can actually understand a full watch example with both conditions and transforms. I think this is probably a good place to put the "for loop" example from honza above, as well. As is, I think its ready for ripping my horrible use of the english language apart.

@hub-cap hub-cap changed the title [DOCS] Add watcher condition examples [DOCS] Add watcher examples Dec 17, 2018
Copy link
Copy Markdown
Contributor

@jdconrad jdconrad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me from the tech side. Left a few comments on wording and such. Thanks so much for doing this! I really appreciate you going through the variables and adding hefty docs for them as well. This should be really helpful for our users.

Any metadata associated with the watch.
<1> The Java Stream API is used in the condition. This API allows manipulation of
the elements of the list in a pipeline.
<2> A Stream Filter is used to remove items that do not meet the criteria within the
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should stream filter be capitalized?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope :-)

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

the elements of the list in a pipeline.
<2> A Stream Filter is used to remove items that do not meet the criteria within the
filter.
<3> The collector reduces the stream back into a java.util.List
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add back ticks to java.util.List

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And then add a period. I'd also say "reduces the stream to a" rather than "back into a".

`ctx['trigger']['scheduled_time']` (`ZonedDateTime`, read-only)::
The scheduled trigger time for the watch.
The first example is a condition for the watch itself. This determines if any
of the actions in the watch are executed. The example aggregates the total sold
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may need to be re-worded as it sounds like you can select individual actions to execute for a watch based on a single condition.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other places, we use an "Example" label before the example intro (like Return and API).

Rather than "first" and "second", I'd just use "the following" to introduce the examples.

Example

The following watch condition script controls overall execution of the watch 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 under $15,000 or over $50,000.

*Return*
The next example is a condition on an action. This determines if a distinct action
in the watch are executed. The example aggregates the total sold seats for each play
in the data set. The action condition validates that there is at least one play that
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you be more descriptive here about the aggregation being executed? Aggregates the total seats how?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit redundant. I'd used the pattern from the watch condition intro:

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.

@@ -0,0 +1,112 @@
==== Complete Example

While these examples are contrived, a more complete watch shown below contains
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think you need to call out the example as contrived as that makes it sound like it's not really useful. Short examples often don't represent real world scenarios.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we generally avoid "meta info" like that as much as possible.


==== Time values in a watch

There are 3 time values that are available as part of the context. The
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

three instead of 3?

@hub-cap
Copy link
Copy Markdown
Contributor Author

hub-cap commented Dec 18, 2018

@spinscale if you would like to take a look as well, id be delighted :)

Copy link
Copy Markdown
Contributor

@spinscale spinscale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a few comments for possible improvements, nothing major though

"script" :
"""
return ctx.payload.aggregations.theatres.buckets.stream() <1>
.filter(t -> t.money.value < 15000 || t.money.value > 50000) <2>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe replace the t with threatre or bucket?

return ctx.payload.aggregations.theatres.buckets.stream() <1>
.filter(t -> t.money.value < 15000 || t.money.value > 50000) <2>
.collect(Collectors.toList()) <3>
.size() > 0 <4>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to create a list first and then use size, just use .count()?

"""
return ctx.payload.aggregations.theatres.buckets.stream()
.filter(t -> t.money.value > 50000) <2>
.collect(Collectors.toList())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, you don't even need .count(). You can use return ctx.payload.aggregations.theatres.buckets.stream().anyMatch(b -> b.money.value > 50000)`

.filter(t -> t.money.value < ctx.metadata.low_threshold ||
t.money.value > ctx.metadata.high_threshold)
.collect(Collectors.toList())
.size() > 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, use Stream.anyMatch()

'msg': ctx.payload.money_makers.stream()
.map(t-> formatter.format(t.total_value) + ' for the play ' + t.play)
.collect(Collectors.toList())
.join(", ")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just do Collectors.joining(' FOO ') instead of collect and join?

==== Time values in a watch

There are 3 time values that are available as part of the context. The
execution_time is the actual time the watch is executed. The other two
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

backticks around execution time?

"transform" : {
"script":
"""
return [ 'elements': ctx.payload.aggregations.theatres.buckets.size() ]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this transform is also not needed, one could call {{ctx.payload.aggregations.theatres.buckets.size}} to get the same result.

}
},
"actions" : {
"mod_log" : {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe use the long form for the action names here?

"text" : "The output of the payload was transformed to: {{ctx.payload.msg}}"
}
},
"unmod_log" : { <2>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

long name as well?

'msg': ctx.payload.aggregations.theatres.buckets.stream()
.map(t-> formatter.format(t.money.value) + ' for the play ' + t.key)
.collect(Collectors.toList())
.join(", ")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collectors.joining(', ')?

@jasontedor jasontedor added v6.7.0 and removed v6.6.0 labels Dec 19, 2018
will return a `boolean` value to determine if the condition was met. A
condition can be used to short circuit an entire watch or within an action
to determine execution of said action.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, we prefer present tense and active voice. I'd also consistently use either "watch condition" rather than flip-flopping between it and "Watcher condition". The last sentence and the first are largely redundant. I'd probably go with:

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.

I'd bump this up to follow the Watch condition context heading, before the context variables.

User-defined parameters passed in as part of the query.
`boolean`::
Expects `true` if the condition is met, and `false` otherwise.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say "Returns true if the condition is met, false if it is not."

@@ -0,0 +1,112 @@
==== Complete Example

While these examples are contrived, a more complete watch shown below contains
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we generally avoid "meta info" like that as much as possible.

`ctx['trigger']['scheduled_time']` (`ZonedDateTime`, read-only)::
The scheduled trigger time for the watch.
The first example is a condition for the watch itself. This determines if any
of the actions in the watch are executed. The example aggregates the total sold
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other places, we use an "Example" label before the example intro (like Return and API).

Rather than "first" and "second", I'd just use "the following" to introduce the examples.

Example

The following watch condition script controls overall execution of the watch 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 under $15,000 or over $50,000.

Any metadata associated with the watch.
<1> The Java Stream API is used in the condition. This API allows manipulation of
the elements of the list in a pipeline.
<2> A Stream Filter is used to remove items that do not meet the criteria within the
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope :-)

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

*Return*
The next example is a condition on an action. This determines if a distinct action
in the watch are executed. The example aggregates the total sold seats for each play
in the data set. The action condition validates that there is at least one play that
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit redundant. I'd used the pattern from the watch condition intro:

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.

@@ -0,0 +1,112 @@
==== Complete Example
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd remove the heading & just have this as part of the Example section.

both <<painless-watcher-condition-context, conditions>> and
<<painless-watcher-transform-context, transforms>>, and is the basis of the
above examples. It is shown for completeness.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe: 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>>.

I wouldn't link back to the top of this topic.

"condition" : {
"script" :
"""
return ctx.metadata.high_threshold == 50000 <1>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This and the time values examples seem overly-heavyweight to simply show how to access these values. I'd incorporate the info/example snippet into the context variable descriptions and maybe mention in the intro that condition scripts can access watch metadata and timestamps.

@@ -1,39 +1,162 @@
[[painless-watcher-transform-context]]
=== Watcher transform context

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty much the same set of comments applies to the transform context. :-)

@hub-cap
Copy link
Copy Markdown
Contributor Author

hub-cap commented Dec 20, 2018

Thanks to everyone for the reviews! Im going to get on these ASAP tomorrow!

@hub-cap
Copy link
Copy Markdown
Contributor Author

hub-cap commented Jan 2, 2019

I have updated the docs based on the feedback. thanks to @jdconrad @spinscale and @debadair.

the biggest "change" that i did that was not just following update comments was to move all of the common examples (metadata/time values/payload) into their own example. I just stuck it at the end, after the other example. Id like to still show some date formatting in the example, but its not strictly necessary if it does not fit the rest of the flow. Id be happy to remove it if it does not make sense there.

Copy link
Copy Markdown
Contributor

@debadair debadair left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@hub-cap hub-cap merged commit c0368a2 into elastic:master Jan 15, 2019
jasontedor added a commit to jasontedor/elasticsearch that referenced this pull request Jan 15, 2019
* master:
  Reformat some classes in the index universe
  [DOCS] Add watcher context examples (elastic#36565)
hub-cap added a commit that referenced this pull request Jan 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Core/Infra/Scripting Scripting abstractions, Painless, and Mustache >docs General docs changes v6.7.0 v7.0.0-beta1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants