Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Operators Overview

datalogic-rs provides 64 built-in operators organized into logical categories. In the Rust crate, 33 baseline operators are always available in the default build (default = []); a further 29 canonical operators are enabled by opt-in Cargo features, and two flagd-compatible operators (fractional, sem_ver) sit behind the flagd feature. Every language binding (WASM, Node, Python, Go, JVM, .NET, PHP) ships with all operator features enabled, so the full set is available out of the box outside Rust. Counts are by canonical operator: var and ?: are accepted as input aliases of val and if, and match is an alias of switch, so the aliases are not counted separately. This section documents each operator with syntax, examples, and notes on behavior.

Operator Categories

CategoryOperatorsDescription
Variable Accessval (alias var), existsAccess and check data
Comparison==, ===, !=, !==, >, >=, <, <=Compare values
Logical!, !!, and, orBoolean logic
Arithmetic+, -, *, /, %, max, min, abs, ceil, floorMath operations
Control Flowif (alias ?:), ??, switch (alias match), typeConditional branching
Stringcat, substr, in, length, starts_with, ends_with, upper, lower, trim, splitString manipulation
Arraymerge, filter, map, reduce, all, some, none, sort, slice, group_by, distinctArray operations
Objectkeys, values, entriesObject take-apart
DateTimedatetime, timestamp, parse_date, format_date, date_diff, nowDate and time
Missing Valuesmissing, missing_someCheck for missing data
Error Handlingtry, throwException handling
flagd-Compatfractional, sem_verFeature-flag targeting (OpenFeature flagd spec); requires features = ["flagd"]

Which operators need which Cargo feature

This split only affects the Rust crate: only the baseline set is built in the default build (default = []). A rule that uses any other operator against an engine compiled without its feature still compiles (Engine::compile succeeds, because an unknown key is treated like an unregistered custom operator), but evaluating it fails with an InvalidOperator error naming the operator. In templating mode an unknown key is echoed as data instead of erroring (see the API reference). Every language binding enables all operator features, so the full set is always available there.

Cargo featureOperators
baseline (always on)val/var, comparison (==<=), and, or, !, !!, if/?:, + - * / %, min, max, cat, substr, in, map, filter, reduce, merge, all, some, none, missing, missing_some
ext-stringlength, starts_with, ends_with, upper, lower, trim, split
ext-arraysort, slice, group_by, distinct
ext-objectkeys, values, entries
ext-mathabs, ceil, floor
ext-controlexists, ??, switch/match, type
error-handlingtry, throw
datetimedatetime, timestamp, parse_date, format_date, date_diff, now
flagdfractional, sem_ver

The table above is maintained by hand; the machine-readable source of truth for a given build is Engine::builtin_operator_names(), which is derived from the compiler’s own lookup table and reflects the compiled feature set (aliases included). Use it instead of a hand-copied list when tooling needs to know what the running engine evaluates.

Operator Syntax

All operators follow the JSONLogic format:

{ "operator": [arg1, arg2, ...] }

Some operators accept a single argument without an array:

{ "var": "name" }
// Equivalent to:
{ "var": ["name"] }

Lazy Evaluation

Several operators use lazy (short-circuit) evaluation:

  • and: Stops at first falsy value
  • or: Stops at first truthy value
  • if: Only evaluates the matching branch
  • ?:: Only evaluates the matching branch
  • ??: Only evaluates fallback if first value is null

This matters when a later operand could raise an error, or is expensive:

{
  "and": [
    { "var": "denominator" },
    { "/": [100, { "var": "denominator" }] }
  ]
}

If denominator is missing or 0, and returns that falsy value and the division (which would throw a NaN error for an integer zero divisor) is never evaluated; with { "denominator": 4 } the result is 25. Note that var itself never errors on a missing path, it returns null, so plain property access such as { "var": "user.profile.name" } needs no guard.

Type Coercion

Operators handle types differently:

Loose vs Strict

  • == and != perform type coercion
  • === and !== require exact type match
{ "==": [1, "1"] }   // true (loose)
{ "===": [1, "1"] }  // false (strict)

Numeric Coercion

Arithmetic operators attempt to convert values to numbers:

{ "+": ["5", 3] }  // 8 (string "5" becomes number 5)

Truthiness

Boolean operators use configurable truthiness rules. By default (JavaScript-style):

  • Falsy: false, 0, "", null, [], {}
  • Truthy: Everything else

Custom Operators

You can add your own operators. See Custom Operators for details.

In v5 operator registration is builder-only:

let engine = Engine::builder()
    .add_operator("myop", MyOperator)
    .build();

Custom operators follow the same syntax in rules:

{ "myop": [arg1, arg2] }

Note: v5 removed the preserve operator. Wrap literals in templating mode (Engine::builder().with_templating(true).build(), requires feature = "templating") if you need to emit a JSON object verbatim from a rule. Literal scalars and arrays already work inline.

Emitting a key that is an operator name. Because a single-key object is an operator invocation, {"type": {"var": "x"}} runs the type operator rather than emitting a type field, and the same applies to every name on this page plus any custom operator you register. In templating mode you can opt into an escape prefix, Engine::builder().with_template_key_escape('$'), and write {"$type": ...} to emit type ({"$$type": ...} emits a literal $type). See Structured Objects.