# Overview # 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](../routing/) for details on configuring edges. ### Transform Conditionals [Transforms](../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 ```json { "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 ```json { "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: ```json { "type_id": "equals", "config": { "key": "status", "value": "active" } } ``` Even for a single condition, wrap it in a logical condition: ```json { "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:** ```json { "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:** ```json { "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](#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 ```json { "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 ```json { "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 ```json { "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 ```json { "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 ```json { "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 ```json { "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](./contains) | Check if a string contains a substring or array contains an element | | [ends_with](./ends-with) | Check if a string ends with a suffix | | [equals](./equals) | Check if a value exactly matches (supports strings, numbers, booleans, arrays, objects) | | [equals_any](./equals-any) | Check if a value matches any item in a list | | [greater_than](./greater-than) | Check if a number is greater than a threshold | | [key_exists](./key-exists) | Check if a field exists in the record | | [less_than](./less-than) | Check if a number is less than a threshold | | [matches_regex](./matches-regex) | Check if a string matches a regular expression | | [sample](./sample) | Sample a percentage of records randomly or by key | | [starts_with](./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`:** ```json { "type_id": "equals", "config": { "key": "status", "value": "active" } } ``` This matches records where `status` equals "active". **With `not`:** ```json { "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` ```json { "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 ```json { "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: ```json { "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: ```json { "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: ```json { "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: ```json { "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