Conditionals
Conditionals allow you to control data flow and processing based on the content of your records. They evaluate JSON records against specified criteria and return true or false, determining whether data passes through routing edges or whether transforms are applied.
Where Conditionals Are Used
Conditionals appear in two key contexts within Monad:
Edge Data Routing
When configuring edges between pipeline components, conditionals determine which records flow through to the destination. Records that match the conditions pass through; those that don't are excluded from that edge. See Data Routing for details on configuring edges.
Transform Conditionals
Transforms can include an optional conditional block that determines whether the transform operations are applied to each record. When a transform has conditionals configured:
- If the condition evaluates to true: The transform operations are applied to the record
- If the condition evaluates to false: The record is handled according to the
elseaction:bypass(default): The record passes through unchangeddrop: The record is excluded from the output
Structure
Conditional configurations use a two-level hierarchy:
- Logical Conditions: Define how conditions are combined using an operator (
and,or, etc.) - Leaf Conditions: The individual checks that evaluate record data (
equals,contains, etc.)
Important: The root of any conditional configuration must always be a logical condition. You cannot use a leaf condition directly at the top level—it must be wrapped in a logical condition with an operator.
Logical Condition Structure
A logical condition has two fields:
operator: How to combine the child conditions (always,never,and,or,nor,xor)conditions: An array of leaf conditions or nested logical conditions
{
"operator": "and",
"conditions": [
{
"type_id": "equals",
"config": {
"key": "status",
"value": "active"
}
},
{
"type_id": "key_exists",
"config": {
"key": "user_id"
}
}
]
}
Leaf Condition Structure
A leaf condition specifies the actual check to perform:
type_id: The type of condition (equals,contains,key_exists, etc.)config: The parameters for that condition type
{
"type_id": "equals",
"config": {
"key": "status",
"value": "active"
}
}
Invalid: Leaf Condition at Root
This is invalid because a leaf condition cannot be used directly—it must be inside a logical condition:
{
"type_id": "equals",
"config": {
"key": "status",
"value": "active"
}
}
Even for a single condition, wrap it in a logical condition:
{
"operator": "and",
"conditions": [
{
"type_id": "equals",
"config": {
"key": "status",
"value": "active"
}
}
]
}
Key Path Syntax
Leaf conditions use GJSON key path notation to specify which field in a record to evaluate. This syntax uses dot notation to traverse nested JSON structures.
Basic Dot Notation
Use dots to access nested fields:
| Path | Description |
|---|---|
status | Top-level field named "status" |
user.email | The "email" field inside the "user" object |
event.metadata.source | Deeply nested field |
Example record:
{
"user": {
"name": "Alice",
"email": "alice@example.com"
},
"status": "active"
}
| Path | Result |
|---|---|
status | "active" |
user.name | "Alice" |
user.email | "alice@example.com" |
Array Index Access
Use numeric indices to access array elements (zero-based):
| Path | Description |
|---|---|
items.0 | First element of the "items" array |
items.1 | Second element of the "items" array |
data.2 | Third element of the "data" array |
Combining Objects and Arrays
Dot notation works seamlessly with mixed structures:
Example record:
{
"users": [
{ "name": "Alice", "role": "admin" },
{ "name": "Bob", "role": "user" }
],
"metadata": {
"tags": ["important", "reviewed"]
}
}
| Path | Result |
|---|---|
users.0 | { "name": "Alice", "role": "admin" } |
users.0.name | "Alice" |
users.1.role | "user" |
metadata.tags.0 | "important" |
Wildcard Key
Use * as the key to check all top-level fields in the record. See Wildcard Key Matching for details.
Logical Operators
Logical operators define how conditions within a logical condition are evaluated together.
always
Routes all records without evaluating any conditions.
- Conditions: Any conditions provided will be ignored
- Use case: Default routing, testing, or when all data should flow through
{
"operator": "always",
"conditions": []
}
never
Blocks all records without evaluating any conditions.
- Conditions: Any conditions provided will be ignored
- Use case: Temporarily disabling a route without removing configuration
{
"operator": "never",
"conditions": []
}
and
All conditions must evaluate to true for the overall result to be true.
- Conditions required: At least 1
- Use case: Strict filtering where records must meet all criteria
{
"operator": "and",
"conditions": [
{ "type_id": "equals", "config": { "key": "type", "value": "alert" } },
{ "type_id": "greater_than", "config": { "key": "severity", "value": 3 } }
]
}
or
At least one condition must evaluate to true for the overall result to be true.
- Conditions required: At least 1
- Use case: Flexible routing where records matching any criterion should pass
{
"operator": "or",
"conditions": [
{ "type_id": "equals", "config": { "key": "priority", "value": "high" } },
{ "type_id": "equals", "config": { "key": "priority", "value": "critical" } }
]
}
nor
None of the conditions should evaluate to true for the overall result to be true (inverse of OR).
- Conditions required: At least 1
- Use case: Excluding records that match any unwanted criteria
{
"operator": "nor",
"conditions": [
{ "type_id": "equals", "config": { "key": "environment", "value": "test" } },
{ "type_id": "contains", "config": { "key": "source", "value": "debug" } }
]
}
xor
Exactly one condition must evaluate to true for the overall result to be true.
- Conditions required: At least 2
- Use case: Mutually exclusive routing scenarios
{
"operator": "xor",
"conditions": [
{ "type_id": "equals", "config": { "key": "region", "value": "eu" } },
{ "type_id": "equals", "config": { "key": "region", "value": "us" } }
]
}
Leaf Condition Types
Leaf conditions are the individual checks that evaluate specific aspects of a record. They are used inside the conditions array of a logical condition. All leaf conditions support the not parameter to negate their result.
Click on a condition type for detailed documentation, examples, and best practices.
| Condition | Description |
|---|---|
| contains | Check if a string contains a substring or array contains an element |
| ends_with | Check if a string ends with a suffix |
| equals | Check if a value exactly matches (supports strings, numbers, booleans, arrays, objects) |
| equals_any | Check if a value matches any item in a list |
| greater_than | Check if a number is greater than a threshold |
| key_exists | Check if a field exists in the record |
| less_than | Check if a number is less than a threshold |
| matches_regex | Check if a string matches a regular expression |
| sample | Sample a percentage of records randomly or by key |
| starts_with | Check if a string starts with a prefix |
The not Parameter
Every condition type supports the not parameter, which inverts the condition's result. This is useful for expressing negative conditions without creating complex nested structures.
Without not:
{
"type_id": "equals",
"config": {
"key": "status",
"value": "active"
}
}
This matches records where status equals "active".
With not:
{
"type_id": "equals",
"config": {
"key": "status",
"value": "active",
"not": true
}
}
This matches records where status does NOT equal "active".
Behavior When Key Is Missing
When evaluating a condition on a key that doesn't exist in the record:
- Without
not: The condition returnsfalse - With
not: The condition returnstrue
This allows conditions like "key does not exist" to be expressed using key_exists with not: true.
Wildcard Key Matching
Several condition types support using * as the key value to check all keys in the record. When * is used:
- The condition evaluates each top-level key in the record
- Returns
trueif ANY key's value matches the condition - Supported by:
equals,equals_any,contains,starts_with,ends_with,matches_regex,greater_than,less_than
{
"type_id": "contains",
"config": {
"key": "*",
"value": "sensitive"
}
}
This matches any record that has "sensitive" in any of its top-level values.
Nesting Logical Conditions
Logical conditions can be nested within other logical conditions to create complex expressions. Within the conditions array of a logical condition, you can include either:
- Leaf conditions: Objects with
type_idandconfig - Nested logical conditions: Objects with
operatorandconditions
This recursive structure allows you to build arbitrarily complex logic, subject to the maximum depth limit.
Maximum nesting depth: 3 levels of logical conditions
Example: Nested Logical Condition (AND within OR)
Match records that are either:
- High severity alerts, OR
- Both from production AND containing errors
{
"operator": "or",
"conditions": [
{
"type_id": "equals",
"config": {
"key": "severity",
"value": "high"
}
},
{
"operator": "and",
"conditions": [
{
"type_id": "equals",
"config": {
"key": "environment",
"value": "production"
}
},
{
"type_id": "contains",
"config": {
"key": "message",
"value": "error"
}
}
]
}
]
}
Transform Conditional Example
When using conditionals with transforms, you specify the conditions and an else action:
{
"operations": [
{
"operation": "add",
"arguments": {
"key": "processed",
"value": true
}
}
],
"conditional": {
"conditions": {
"operator": "and",
"conditions": [
{
"type_id": "key_exists",
"config": {
"key": "user_id"
}
},
{
"type_id": "equals",
"config": {
"key": "status",
"value": "active"
}
}
]
},
"else": "bypass"
}
}
In this example:
- If both conditions are met, the transform adds
"processed": trueto the record - If conditions are not met, the record passes through unchanged (
bypass)
Complete Examples
Example 1: Security Alert Routing
Route high-priority security events to an urgent alerts destination:
{
"operator": "and",
"conditions": [
{
"type_id": "equals",
"config": {
"key": "event_type",
"value": "security"
}
},
{
"type_id": "equals_any",
"config": {
"key": "severity",
"values": ["critical", "high"]
}
},
{
"type_id": "equals",
"config": {
"key": "status",
"value": "active",
"not": true
}
}
]
}
Matches security events with critical or high severity that are not already resolved.
Example 2: Filtering Test Data
Exclude test and debug records from production pipelines:
{
"operator": "nor",
"conditions": [
{
"type_id": "equals",
"config": {
"key": "environment",
"value": "test"
}
},
{
"type_id": "contains",
"config": {
"key": "source",
"value": "debug"
}
},
{
"type_id": "starts_with",
"config": {
"key": "user_id",
"value": "test_"
}
}
]
}
Passes through records that don't match any of the test/debug patterns.
Example 3: Complex Nested Conditions
Route records that meet complex business rules:
{
"operator": "or",
"conditions": [
{
"operator": "and",
"conditions": [
{
"type_id": "equals",
"config": {
"key": "region",
"value": "eu"
}
},
{
"type_id": "greater_than",
"config": {
"key": "data_size_bytes",
"value": 1000000
}
}
]
},
{
"operator": "and",
"conditions": [
{
"type_id": "equals",
"config": {
"key": "compliance_required",
"value": "true"
}
},
{
"type_id": "key_exists",
"config": {
"key": "gdpr_consent"
}
}
]
}
]
}
Matches records that are either:
- From EU region with data size over 1MB, OR
- Require compliance AND have GDPR consent recorded
Validation Rules
alwaysandneveroperators accept any number of conditions, but they will be ignoredand,or, andnoroperators require at least 1 conditionxoroperator requires at least 2 conditions- Maximum nesting depth is 3 levels
- All required configuration fields must be provided for each condition type