Skip to Content
Documentation
Starter kits
Buy now
Conditions
Getting started

Operators

Built-in operators, value modes, and how to define your own.

Value modes

Built-in operator operands are derived from the field schema and value mode:

ModeOperand
singleOne field value
multipleAn array of field values
rangeA two-value tuple
noneNo value

For example, status in accepts ('lead' | 'customer' | 'churned')[], while arr between accepts [number, number].

Built-in operators

defaultOperators ships with the package. Restrict each field to the operators that make sense for it.

OperatorLabelTypesMode
equalsisenum, string, number, boolean, date, datetimesingle
notis notenum, string, number, boolean, date, datetimesingle
gtgreater thannumber, date, datetimesingle
gtegreater than or equalnumber, date, datetimesingle
ltless thannumber, date, datetimesingle
lteless than or equalnumber, date, datetimesingle
betweenis betweennumber, date, datetimerange
containscontainsstringsingle
startsWithstarts withstringsingle
endsWithends withstringsingle
inis any ofenum, string, numbermultiple
notInis none ofenum, string, numbermultiple
somehas some ofenum (array subjects)multiple
everyhas all ofenum (array subjects)multiple
isNullis emptyenum, string, number, boolean, date, datetimenone
isNotNullis not emptyenum, string, number, boolean, date, datetimenone

String operators (contains, startsWith, endsWith) compare case-insensitively. The Drizzle and Zero adapters preserve those semantics in SQL / ZQL.

some and every compare against array subjects. They have no generic SQL or ZQL form — map them yourself through the adapter operators option.

Custom operators

Custom operators use Standard Schema for both the subject and operand. The comparator parameters are inferred without generic annotations.

import {
  defaultOperators,
  defineConditions,
  defineOperator,
} from '@saas-js/conditions'
import { z } from 'zod'

const matches = defineOperator({
  id: 'matches',
  label: 'matches',
  types: ['string'],
  valueMode: 'single',
  subjectSchema: z.string(),
  valueSchema: z.object({
    pattern: z.string().min(1),
    flags: z.string().default('i'),
  }),
  serialize(value) {
    return `${value.flags}:${value.pattern}`
  },
  deserialize(value) {
    const [flags, pattern] = String(value).split(':')
    return { flags, pattern }
  },
  comparator(actual, expected) {
    return new RegExp(expected.pattern, expected.flags).test(actual)
  },
})

const searchableContacts = defineConditions({
  operators: [...defaultOperators, matches],
  fields: {
    name: {
      type: 'string',
      schema: z.string(),
      operators: ['equals', 'matches'],
    },
  },
})

Operator ids, supported field types, operand shapes, and comparator arguments are checked statically. The same schemas validate query values and custom operator subjects at runtime.

Adapters that emit SQL or ZQL need an explicit mapping for custom operators — see Drizzle and Zero.

Previous

Validate, evaluate, serialize

Next

Overview