Nexus

Filter Operators Reference

The definitive UQL filter operator catalog — every operator, its capability tag and pushdown eligibility, boolean composition with $and/$or/$not, the trusted-author $expr surface, and which subset each connector runs natively versus in-engine.

UQL filter operators appear in the where clause of any read (and in the where scope of an update or delete). This page is the definitive catalog: every operator Nexus recognizes, how it composes, and which connector runs which subset natively. There is one source of truth for the operator set — the same catalog validates ad-hoc query_data, saved reports, and pipeline reads — so an operator behaves identically wherever you write it. For how "runs natively versus in-engine" is decided, see Pushdown & capability semantics.

Operator syntax

Each field condition is either a scalar (equality shorthand) or a dict of operators:

{
  "where": {
    "status":  "shipped",
    "amount":  { "$gte": 1000, "$lt": 50000 },
    "region":  { "$in": ["APAC", "EMEA"] }
  }
}

"status": "shipped" is shorthand for { "$eq": "shipped" }. Multiple operators inside one field's dict are ANDed. Multiple fields at the top level are ANDed. Values are always sent as typed data — strings, numbers, arrays — and bind as parameters (%s placeholders) for SQL bridges or query variables for API bridges; nothing is concatenated into a query string.

The operator catalog

Operators are grouped by capability tag. A capability is either allowed or not for a given surface (see Capability gating below). The Pushdown-eligible column indicates whether the operator can be executed at the source at all; whether it is pushed for a given query also depends on the connector's declared profile.

Comparison & null — capability BASIC

Operator Meaning Pushdown-eligible Example value
$eq Equal to (or IS NULL when the value is null) Yes "shipped"
$ne Not equal to (or IS NOT NULL when the value is null) Yes "closed"
$gt Greater than Yes 1000
$gte Greater than or equal to Yes 1000
$lt Less than Yes 500
$lte Less than or equal to Yes 500
$between Inclusive range [low, high] Yes ["2026-01-01", "2026-12-31"]
$empty Field is null or empty (pass true) Yes true
$not_empty Field is not null and not empty (pass true) Yes true

Set — capability SET

Operator Meaning Pushdown-eligible Example value
$in Value is in the list Yes ["APAC", "EMEA", "AMER"]
$not_in Value is not in the list Yes ["draft", "void"]

Pattern — capability PATTERN

Operator Meaning Pushdown-eligible Example value
$contains Substring present (wildcards in the value are escaped) Yes "invoice"
$not_contains Substring absent Yes "draft"
$starts_with Prefix match Yes "INV-"
$not_starts_with Negated prefix Yes "TMP-"
$ends_with Suffix match Yes "-2026"
$not_ends_with Negated suffix Yes "-void"
$like Raw LIKE pattern — %/* for any run, _/? for one char; caller supplies wildcards, nothing escaped Yes "ACME%"
$not_like Negated LIKE pattern Yes "%-test"
$regex Regular-expression match No by default — pushed only on MySQL/PostgreSQL; evaluated in-engine elsewhere "^INV-\\d{6}$"

Multi-range DSL — capability FILTER_DSL

Operator Meaning Pushdown-eligible Example value
$filter Comma-separated terms, each a range, wildcard, or exact value Yes "1000-4000, 4*, 40?0"

$filter is a compact way to express a mix of ranges and patterns on one field. "1000-4000, 4*, 40?0" matches the numeric range 1000–4000, anything starting with 4, or the pattern 40?0. A bare * or empty string means match-all (the condition is dropped).

Boolean composition — capability LOGIC

$and, $or, and $not are structural keys, not per-field operators. Use them to compose sub-clauses:

{
  "where": {
    "$or": [
      { "status": { "$eq": "open" } },
      { "$and": [
        { "status": { "$eq": "invoiced" } },
        { "amount": { "$gt": 10000 } }
      ] }
    ],
    "$not": { "region": { "$eq": "internal" } }
  }
}

$and and $or take a list of sub-clauses; $not takes a single sub-clause. Top-level field conditions are still ANDed with the boolean keys.

Raw expression — capability EXPRESSION

$expr passes an expression string straight to the source's SQL WHERE — for field-to-field comparisons or database functions no structured operator covers:

{ "where": { "$expr": "net_amount > gross_amount * 0.9" } }

Because $expr bypasses parameterization, it is a trusted-author surface: it is available in saved reports and pipelines (whose definitions are themselves RBAC-controlled), and it is not accepted from ad-hoc query_data. It is meaningful only on SQL-backed surfaces (any SQL bridge, the workspace, NetSuite's SuiteQL surface); an API-backed record surface has no SQL WHERE to inject into. Never build $expr from end-user input.

Capability gating

Every surface declares which capability tags it permits, and an operator outside that set is rejected before it reaches the source — it cannot "slip through":

Surface BASIC SET PATTERN LOGIC FILTER_DSL EXPRESSION ($expr)
Ad-hoc query_data (MCP) Yes Yes Yes Yes Yes No
Saved reports Yes Yes Yes Yes Yes Yes
Pipeline reads Yes Yes Yes Yes Yes Yes

This is why $expr is safe to offer at all: the one surface an untrusted agent reaches directly (query_data) cannot use it, while report and pipeline definitions — which an admin or author controls — can.

Per-connector support

The operator vocabulary is universal — you write the same operators against every bridge. What differs per connector is where the work runs: a connector executes the subset of operators its backend can express natively and pushes those to the source; anything it cannot express is re-applied by Nexus in-engine over the returned rows (a clean, correct fallback bounded by a row budget — not a silent wrong answer). See the pushdown page for the full mechanics.

Connector WHERE pushdown Notes on operator coverage
PostgreSQL / MySQL / SQL Server / JDBC Full Every operator pushes; $regex pushes on MySQL/PostgreSQL, in-engine on SQL Server/JDBC without native regex.
Internal workspace (Postgres) Full Same as PostgreSQL.
NetSuite — SuiteQL surface Full SuiteQL is SQL; the full operator set pushes, including $expr.
NetSuite — REST record surface None Filters are evaluated in-engine over the record rows; results are correct but the whole page is fetched first.
QuickBooks Online Filter + order only Comparison/equality push via the QBO query grammar; aggregation runs in-engine.
Monday.com Native subset (below) GraphQL ItemsQuery expresses a subset; the rest falls back in-engine.
Stripe (preview, pre-1.0) None Filtering and ordering run in-engine over fetched rows.

Monday.com's native operator subset

Monday's GraphQL ItemsQuery natively expresses: $eq, $ne, $gt, $gte, $lt, $lte, $between, $contains, $not_contains, $starts_with, $ends_with, $empty, $not_empty. It does not natively express $in/$not_in, $like/$not_like, $not_starts_with/$not_ends_with, $regex, $filter, or $expr — those operators are still accepted and produce correct results, but are evaluated in-engine after Monday returns the board items rather than pushed into the query. Aggregations on Monday are always in-engine.

Discover before you filter

Field names in where must match the exact IDs returned by schema discovery. A typo raises a query error on a SQL bridge and, on some APIs, is silently ignored (returning unfiltered rows). Run discover_schema against the target bridge before writing filters to confirm field names and types.

See also