> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mangrovesystems.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Rule Expressions

> Reference for the rule expression language: variables, operators, comparisons, and aggregates.

Rule expressions are written in a small expression language with familiar comparison and logical operators. Each expression is a **condition** — when it evaluates to true on a record, that record is flagged with an alert.

```
{{carbon_content}} not between 0.50 and 0.90
```

This page is the reference for the full set of operators and patterns. For a walkthrough of writing your first rule, see [Create a Data Rule](/data-rules/create-a-rule).

## Variables

Variables reference the data Mangrove evaluates the expression against. They are wrapped in double curly braces.

```
{{slug}}
```

The values the rule editor offers depend on the rule's target:

* **Event Data** rules — variables are datapoint slugs from your project's data point types. Selecting a specific event type scopes the rule to that event type and narrows autocomplete to its datapoints — we recommend doing this so the rule's intent is clear. If no event type is selected, the rule evaluates against any event in the project whose datapoints satisfy the expression.
* **Batch Calculations** rules — variables are nodes in the selected model. When multiple nodes share the same slug, qualify the reference with the datapoint type using the form `node."<NodeName>".<slug>` (for example, `node."Mass".biochar_mass`).

Type `{{` in the rule editor to open autocomplete. Each suggestion shows the variable name and its description, value type, or unit when available.

## Comparison operators

| Operator | Meaning                  |
| -------- | ------------------------ |
| `=`      | Equal to                 |
| `!=`     | Not equal to             |
| `<`      | Less than                |
| `<=`     | Less than or equal to    |
| `>`      | Greater than             |
| `>=`     | Greater than or equal to |

```
{{ash_content}} > 0.05
{{batch_status}} != 'verified'
```

## Logical operators

Combine conditions with `AND`, `OR`, and `NOT`. Use parentheses to group.

```
{{ash_content}} > 0.05 AND {{moisture}} < 0.10
NOT ({{feedstock_type}} = 'manure' OR {{feedstock_type}} = 'crop_residue')
```

## Range and set operators

| Operator                  | Example                             | Triggers when...                                 |
| ------------------------- | ----------------------------------- | ------------------------------------------------ |
| `BETWEEN ... AND ...`     | `{{ph}} between 6.0 and 8.0`        | The value is within the range (inclusive).       |
| `NOT BETWEEN ... AND ...` | `{{ph}} not between 6.0 and 8.0`    | The value is outside the range.                  |
| `IN (...)`                | `{{country}} in ('US', 'CA', 'MX')` | The value matches any item in the list.          |
| `NOT IN (...)`            | `{{country}} not in ('US', 'CA')`   | The value matches none of the items in the list. |

## Text operators

| Operator      | Example                                  | Triggers when...                         |
| ------------- | ---------------------------------------- | ---------------------------------------- |
| `CONTAINS`    | `{{notes}} contains 'override'`          | The text contains the substring.         |
| `STARTS WITH` | `{{tracking_id}} starts with 'TMP-'`     | The text starts with the prefix.         |
| `ENDS WITH`   | `{{filename}} ends with '.csv'`          | The text ends with the suffix.           |
| `MATCHES`     | `{{lot_code}} matches '[A-Z]{2}-[0-9]+'` | The text matches the regular expression. |

String literals use **single quotes**.

## Presence operators

Use these to check whether a value was recorded at all.

| Operator     | Triggers when...            |
| ------------ | --------------------------- |
| `IS MISSING` | The variable has no value.  |
| `IS PRESENT` | The variable has any value. |

The variable must be one of your project's datapoint slugs (or a model node, for Batch Calculations rules):

```
{{lab_sample_id}} is missing
{{adjustment_note}} is present AND {{adjustment_note}} = 'manual'
```

## Aggregates

Aggregate functions roll a set of values into a single number. They are most useful in **Batch Calculations** rules, where a model node may hold many values across a batch.

| Function | Returns           |
| -------- | ----------------- |
| `COUNT`  | Number of values  |
| `SUM`    | Sum of values     |
| `AVG`    | Average of values |
| `MIN`    | Smallest value    |
| `MAX`    | Largest value     |

```
SUM({{batch_input_mass}}) > 1000
AVG({{moisture}}) not between 0.05 and 0.15
COUNT({{lab_sample}}) < 3
```

## Booleans and numbers

* Booleans are written `true` and `false` (case-insensitive).
* Numbers are written without quotes — both integers and decimals work: `0`, `42`, `3.14`, `0.005`.

## Common patterns

These examples assume your project has datapoints with the slugs shown — substitute slugs that exist in your project.

```
# Out-of-range alert
{{carbon_content}} not between 0.50 and 0.90

# Missing required value
{{lab_sample_id}} is missing

# Tracking ID format check
{{tracking_id}} not matches '[A-Z]{2}-[0-9]{6}'

# Batch CI sanity check
{{ci_score}} > 30 OR {{ci_score}} < -50

# Insufficient lab samples in a batch
COUNT({{lab_sample_id}}) < 3
```

## Tips for writing good rules

* **Phrase the rule as the failure case.** A rule with the expression `{{ph}} not between 6.0 and 8.0` triggers when pH is out of range — that's the alert you want.
* **Start specific.** Scope to an event type when possible. Broad rules across all event types are harder to interpret when they fire.
* **Validate before saving.** The validate step previews matches against recent data — use it to catch over-eager rules before they generate noise.
* **Prefer presence checks over equality with empty string.** Use `IS MISSING` rather than `= ''` to detect missing values.
* **Use comments in the rule name.** Since expressions are terse, lean on the rule name and description to explain *why* the rule exists.
