ops
The ops object is a special set of helper methods which can tie back to lower-level Kysely queries, enabling you to pass more complex statements without leaving an ideal statement flow:
await User.where({
email: ops.in(['burpcollaborator.net', 'howyadoin@gmail.biz']),
}).destroy()
await User.where({ email: ops.like('%burpcollaborator.net%') }).destroy()
await User.where({ email: ops.ilike('%burpcollaborator.net%') }).destroy()
await User.where({ id: ops.expression('<=', 23) }).destroy()
Comparison operators
ops.lessThan / ops.lessThanOrEqualTo and ops.greaterThan / ops.greaterThanOrEqualTo express bounded comparisons directly. They're especially useful for two-column interval overlap checks, where each boundary needs to be visible at the call site — choose < vs <= (and > vs >=) based on whether touching interval boundaries count as overlap in your domain:
const overlaps = await Booking.where({
place,
startsOn: ops.lessThanOrEqualTo(requestedEndsOn),
endsOn: ops.greaterThan(requestedStartsOn),
}).exists()
When a single column has a natural lower and/or upper bound instead, prefer the range helper from @rvoh/dream/utils — see the range guide.
Escaping user input in LIKE / ILIKE patterns
% and _ are SQL wildcards inside LIKE / ILIKE patterns. When the pattern is built from user input, wrap the variable in ops.like.escape so those characters (and \) match literally instead of acting as wildcards. It is the one escape helper for all four LIKE-family ops (like, ilike, not.like, not.ilike):
import { ops } from '@rvoh/dream'
// User-controlled search term — escape it before interpolating into the pattern
const term = this.castParam('search', 'string')
query = query.where({ name: ops.ilike(`%${ops.like.escape(term)}%`) })
Escape only the user-controlled portion. The wildcard markers (% and _) you add yourself are not passed through the helper.
Array column containment and equality
A bare array in where() (where({ status: [...] })) means IN against a scalar column and doesn't compile against an array column. Use ops to say which array comparison you want:
| What you want | How you write it | SQL |
|---|---|---|
| The column contains this element | where({ tags: ops.any('quiet') }) | "tags" @> ARRAY[$1]::text[] |
| The column is exactly this array | where({ tags: ops.equal(['quiet', 'walkable']) }) | "tags" = $1 |
ops.any throws AnyRequiresArrayColumn if the target column isn't an array type. ops.equal is order-sensitive — ops.equal(['quiet', 'walkable']) does not match a tags value of ['walkable', 'quiet'] — and its element types are constrained by the column, so passing a value outside an enum array column's allowed members is a compile-time error. ops.equal doesn't support json[] columns (Postgres defines no equality operator for json, only jsonb); use jsonb[] if you need array equality on JSON data.
Similarity operators
ops.similarity, ops.wordSimilarity, and ops.strictWordSimilarity provide fuzzy text matching backed by PostgreSQL's pg_trgm extension. See the similarity guide for setup and usage.