Praxsuite

Operators

Vincent Depassier · August 29, 2026

PraxQL — Operators

Operators appear in where conditions, and in having for aggregation queries. Every condition has the form:

{ "field": "ColumnName", "op": "operator", "value": someValue }

Operator names are matched case-insensitively: isNull, isnull and ISNULL are the same operator.


Comparison

Operator

SQL

Description

eq

=

Equals

neq

!=

Not equals

gt

>

Greater than

gte

>=

Greater than or equal

lt

<

Less than

lte

<=

Less than or equal

{ "field": "Total", "op": "gte", "value": 100 }
{ "field": "Status", "op": "neq", "value": "Deleted" }
{ "field": "CreatedDate", "op": "gt", "value": "2026-01-01" }

Pattern matching

Operator

SQL

Description

like

LIKE

Case-sensitive pattern. % is the wildcard.

ilike

ILIKE

Case-insensitive pattern. % is the wildcard.

{ "field": "Name", "op": "like", "value": "%Corp%" }
{ "field": "Email", "op": "ilike", "value": "%@gmail.com" }

A pattern longer than 200 characters is rejected.


Set membership

Operator

SQL

Description

in

IN (...)

The value is one of the array

notIn

NOT IN (...)

The value is none of them. nin is accepted as an alias.

{ "field": "Status", "op": "in", "value": ["Active", "Pending", "Trial"] }
{ "field": "Status", "op": "notIn", "value": ["Deleted", "Archived"] }

value must be an array.


Null checks

Operator

SQL

Description

isNull

IS NULL

The column has no value

isNotNull

IS NOT NULL

The column has a value

is

IS NULL / IS NOT NULL

The same test, decided by whether you send a value

{ "field": "DeletedAt", "op": "isNull" }
{ "field": "DeletedAt", "op": "isNotNull" }

Prefer isNull and isNotNull. They say outright what is expresses through the presence or absence of a value, which is the part nobody guesses: with is, sending "value": null tests IS NULL, and omitting the key entirely tests IS NOT NULL.

To test a boolean column, compare it instead:

{ "field": "IsVerified", "op": "eq", "value": true }

Send a real JSON true, never the string "true". A string matches zero rows and reports no error, which makes it one of the slowest bugs to notice.


Range

Operator

SQL

Description

between

BETWEEN x AND y

Inclusive range

{ "field": "Date", "op": "between", "value": ["2026-01-01", "2026-06-30"] }
{ "field": "Score", "op": "between", "value": [70, 100] }

value must be a two-element array, [min, max]. Both bounds are inclusive.


Array and text

Operator

Description

contains

An array column contains all the given values

textsearch

Full-text search on a text column

{ "field": "Tags", "op": "contains", "value": ["urgent", "billing"] }
{ "field": "Description", "op": "textsearch", "value": "warehouse logistics" }

textsearch suits long-text columns. It returns results unranked — there is no relevance score, so you cannot sort by "best match".


Logical grouping

AND, the default

Conditions in the same array are AND'd automatically:

"where": [
  { "field": "IsActive", "op": "eq", "value": true },
  { "field": "Region",   "op": "eq", "value": "EU" }
]

→ WHERE IsActive = true AND Region = 'EU'

OR

"where": [
  {
    "or": [
      { "field": "Region", "op": "eq", "value": "EU" },
      { "field": "Region", "op": "eq", "value": "US" }
    ]
  }
]

→ WHERE (Region = 'EU' OR Region = 'US')

Nesting

"where": [
  { "field": "IsActive", "op": "eq", "value": true },
  {
    "or": [
      { "field": "Region", "op": "eq", "value": "EU" },
      {
        "and": [
          { "field": "Region", "op": "eq", "value": "US" },
          { "field": "Tier",   "op": "eq", "value": "Enterprise" }
        ]
      }
    ]
  }
]

→ WHERE IsActive = true AND (Region = 'EU' OR (Region = 'US' AND Tier = 'Enterprise'))


Guardrails

Limit

Value

Where conditions per query

50

Condition nesting depth

5

like / ilike pattern length

200 characters

Exceeding any of them returns 400 INVALID_QUERY rather than a truncated result. If you hit the nesting cap, the query is usually asking two questions at once — split it.