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
- UQL Syntax — the full request shape.
- Pushdown & Capability Semantics — how native-versus-in-engine is decided.
- UQL Examples — worked queries using these operators.
- Connectors Overview — per-system capabilities.