kitcn

Operators

Query `where` operators and mutation filter helpers (Drizzle-style)

Query where (Object Filters)

Object filters are the primary query mechanism for ORM relational queries. You can combine equality checks, comparison operators, and logical combinators in a single where object.

Combine equality checks, comparison operators, and logical combinators in a single where object:

await db.query.users.findMany({
  where: {
    OR: [{ role: 'admin' }, { role: 'member' }],
    age: { gt: 18 },
  },
});

For the full list of supported operators, see API Reference below.

String Operators (post-fetch)

The ORM supports Drizzle-style like/ilike operators and evaluates them post-fetch.

Search for posts containing "javascript" (case-insensitive):

await db.query.posts.withIndex('by_title').findMany({
  where: { title: { ilike: '%javascript%' } },
});

You can also use convenience operators for common patterns:

await db.query.users.withIndex('by_email').findMany({
  where: { email: { endsWith: '@example.com' } },
});

The supported string operators are:

  • like, ilike, notLike, notIlike
  • startsWith, endsWith, contains

Array Operators (post-fetch)

If a field is an array (for example, embeddings or other array-like values), you can use:

  • arrayContains
  • arrayContained
  • arrayOverlaps

Mutation where (Filter Expressions)

Mutation builders (insert, update, delete) use operator helpers that return a FilterExpression. These helpers accept column builders (like users.role) and use builder typing to infer the right value type.

Update all users with a specific role who are over 18:

import { and, eq, gt } from 'kitcn/orm';

// Schema: index('by_role').on(t.role) on users table
await ctx.orm.update(users).set({ role: 'admin' }).where(
  and(
    eq(users.role, 'member'),
    gt(users.age, 18),
  )
);

For the full list of mutation/RLS helpers, see API Reference below.

Unsupported SQL-only Features

These SQL-specific features are not available in the ORM:

  • RAW filters and SQL placeholders (sql.placeholder(...))
  • Prepared statements
  • SQL subquery operators exists / notExists (Convex has no SQL subquery runtime)

For existence checks, use relation filters instead:

// Users with posts
await db.query.users.findMany({
  where: { posts: true },
});

// Users without posts
await db.query.users.findMany({
  where: { NOT: { posts: true } },
});

API Reference

Supported Operators

Full list of operators available inside a where object:

where: {
  OR: [],
  AND: [],
  NOT: {},

  // relations (filter by existence or nested filters)
  posts: true,

  // columns
  age: 15,
  age: { eq: 15 },
  age: { ne: 15 },
  age: { gt: 15 },
  age: { gte: 15 },
  age: { lt: 15 },
  age: { lte: 15 },
  age: { between: [18, 65] },
  age: { notBetween: [18, 65] },
  role: { in: ['admin', 'member'] },
  role: { notIn: ['banned'] },
  email: { isNull: true },
  email: { isNotNull: true },
}

Note: between is inclusive (>= min and <= max). notBetween is strict outside-range (< min or > max).

Common Helpers

All available mutation/RLS operator helpers:

CategoryHelpers
Comparisoneq, ne, gt, gte, lt, lte
Rangebetween, notBetween
SetinArray, notInArray
Logicaland, or, not
NullisNull, isNotNull

Next Steps

On this page