Skip to main content

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 else action:
    • bypass (default): The record passes through unchanged
    • drop: The record is excluded from the output

Structure

Conditional configurations use a two-level hierarchy:

  1. Logical Conditions: Define how conditions are combined using an operator (and, or, etc.)
  2. 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:

PathDescription
statusTop-level field named "status"
user.emailThe "email" field inside the "user" object
event.metadata.sourceDeeply nested field

Example record:

{
"user": {
"name": "Alice",
"email": "alice@example.com"
},
"status": "active"
}
PathResult
status"active"
user.name"Alice"
user.email"alice@example.com"

Array Index Access

Use numeric indices to access array elements (zero-based):

PathDescription
items.0First element of the "items" array
items.1Second element of the "items" array
data.2Third 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"]
}
}
PathResult
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.

ConditionDescription
containsCheck if a string contains a substring or array contains an element
ends_withCheck if a string ends with a suffix
equalsCheck if a value exactly matches (supports strings, numbers, booleans, arrays, objects)
equals_anyCheck if a value matches any item in a list
greater_thanCheck if a number is greater than a threshold
key_existsCheck if a field exists in the record
less_thanCheck if a number is less than a threshold
matches_regexCheck if a string matches a regular expression
sampleSample a percentage of records randomly or by key
starts_withCheck 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 returns false
  • With not: The condition returns true

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 true if 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_id and config
  • Nested logical conditions: Objects with operator and conditions

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": true to 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

  • always and never operators accept any number of conditions, but they will be ignored
  • and, or, and nor operators require at least 1 condition
  • xor operator requires at least 2 conditions
  • Maximum nesting depth is 3 levels
  • All required configuration fields must be provided for each condition type