Documentation
Rule operators
All eighteen rule operators, what each one tests, and the exact reason string it returns when it matches and when it does not.
A condition is three things: a field path into the event payload, an operator, and zero or more values to compare against. The operator decides what comparison happens and what the evaluation says afterwards.
There are eighteen. Every one of them returns a reason whether it matches or not — that is the point of the engine, and the reason strings below are the product's own, character for character.
The operators
| Operator | Values | What it tests |
|---|---|---|
EQUALS |
one | The field equals the value |
NOT_EQUALS |
one | The field does not equal the value |
IN |
many | The field is one of the values |
NOT_IN |
many | The field is none of the values |
CONTAINS |
one | The field contains the value as a substring |
NOT_CONTAINS |
one | The field does not contain the value |
STARTS_WITH |
one | The field begins with the value |
ENDS_WITH |
one | The field ends with the value |
GT |
one | The field is numerically greater than the value |
GTE |
one | The field is greater than or equal to the value |
LT |
one | The field is numerically less than the value |
LTE |
one | The field is less than or equal to the value |
EXISTS |
none | The field is present |
NOT_EXISTS |
none | The field is absent |
IS_TRUE |
none | The field is truthy |
IS_FALSE |
none | The field is not truthy |
MATCHES_REGEX |
one | The field matches an RE2 regular expression |
TRIGGER_MATCH |
none | Another trigger fires for this same event |
Operators taking "many" read the whole values list. Operators taking "one" read the first value and ignore the rest.
Reason strings
This is what the evaluation tells you. %q renders a quoted string and %v
renders a list as [a b c], both following Go's formatting.
| Operator | Matched | Did not match |
|---|---|---|
EQUALS |
value "x" equals "x" |
value "x" does not equal "y" |
NOT_EQUALS |
not_equals: value "x" does not equal "y" |
not_equals: value "x" equals "x" |
IN |
value "x" found in [x y] |
value "x" not found in [y z] |
NOT_IN |
not_in: value "x" not found in [y z] |
not_in: value "x" found in [x y] |
CONTAINS |
value "abc" contains "b" |
value "abc" does not contain "z" |
NOT_CONTAINS |
not_contains: value "abc" does not contain "z" |
not_contains: value "abc" contains "b" |
STARTS_WITH |
value "abc" starts with "a" |
value "abc" does not start with "z" |
ENDS_WITH |
value "abc" ends with "c" |
value "abc" does not end with "z" |
GT |
5 GT 3 |
5 not GT 9 |
GTE |
5 GTE 5 |
5 not GTE 9 |
LT |
3 LT 5 |
9 not LT 5 |
LTE |
5 LTE 5 |
9 not LTE 5 |
EXISTS |
field exists |
field does not exist |
NOT_EXISTS |
field does not exist |
field exists |
IS_TRUE |
value true is_true=true |
value false is_true=false |
IS_FALSE |
value false is_false=true |
value true is_false=false |
MATCHES_REGEX |
value "a1" matches regex "^a\d$" |
value "bb" does not match regex "^a\d$" |
TRIGGER_MATCH |
the referenced trigger's own reason | the referenced trigger's own reason |
Two of these repay a second look.
Negated operators keep the positive form's reason and prefix it. A NOT_IN
that matches still reads not found in, because what it is reporting is the
comparison it ran, not the verdict it reached. Once you expect it, it is the more
informative of the two.
EXISTS and NOT_EXISTS describe the field, not the outcome. Both return
field exists or field does not exist depending on what is actually there, so
NOT_EXISTS matching reads field does not exist.
Arrays
When a field path crosses a JSON array of objects, the values from every element
are collected and the operator is applied with any-match semantics: the
condition matches if any element matches. EXISTS and NOT_EXISTS are the
exceptions and test the field itself.
The reason names which behaviour ran, and counts what it looked at:
array element matched: value "terminated" equals "terminated"
no array element matched (checked 3 elements)Composing triggers
TRIGGER_MATCH is the one operator that does not compare a field. It references
another trigger and matches when that trigger fires for the same event, which is
how a condition reuses logic you have already written rather than restating it.
It never reaches the comparison code — the evaluator intercepts it, evaluates the referenced trigger, and memoises the result for the rest of the message. A condition satisfied from that cache reports:
result from memoization cacheA reference that loops back to a trigger already being evaluated, including a trigger that references itself, doesn't match:
ref trigger 42 is already being evaluated: TRIGGER_MATCH cycle, treated as not matchedPast 32 nested references, the next one isn't evaluated:
TRIGGER_MATCH nesting exceeds 32; ref trigger 42 not evaluatedWhen a condition cannot be evaluated
These come back as not matched, with a reason saying why rather than failing the run:
| Reason | Cause |
|---|---|
field is nil |
The field path resolved to nothing |
no comparison value configured |
The operator needs a value and none was set |
no regex pattern configured |
MATCHES_REGEX with no pattern |
cannot parse field value abc as number: ... |
A numeric operator on a non-numeric field |
cannot parse comparison value "abc" as number: ... |
A numeric operator with a non-numeric value |
invalid regex "(" : ... |
MATCHES_REGEX with a pattern RE2 rejects |
There is one more you should never see, and it is worth knowing on sight:
provider scope 4 does not match current provider 7; skippedA condition scoped to a provider that did not send this event is skipped, and counts as matched, so that a rule written for one provider does not veto events from another. Triggers and rules explains why.
How values are compared
Comparisons are made on the string form of the field value. The numeric
operators — GT, GTE, LT, LTE — parse both sides as numbers at the point
of comparison and report a parse failure as the reason if either side will not
convert.
IS_TRUE and IS_FALSE treat the following as true: the boolean true, the
strings true, 1 and yes in any case, and any non-zero number. Everything
else, including the empty string, 0 and false, is not truthy.
A condition also carries a value_type of STRING, NUMBER or BOOLEAN. It
records what you meant the field to be, and the rule builder uses it to pick a
sensible input — but the comparison itself does not branch on it. The operator
you choose is what determines how the values are compared.