Reference

WHERE Operators

Full reference for filter operators available in Jazz query builders, with examples for each column kind.

Operator reference by column type

Operator support by column type (TypeScript):

Column kindOperators / shape
ideq, ne, in
s.string()eq, ne, contains
s.boolean()plain boolean equality (done: true)
s.int() / s.float()eq, ne, gt, gte, lt, lte
s.timestamp()eq, ne, gt, gte, lt, lte
s.bytes()eq, ne
s.ref("...") (required)eq, ne
s.ref("...").optional()eq, ne, isNull
s.enum(...)eq, ne, in
s.array(...)eq, contains
s.json() / s.json(schema)eq, ne, in

Examples

Equality and inequality

// Exact match (shorthand — no operator object needed)
const incompleteTodos = await db.all(app.todos.where({ done: false }));

// Not equal
const nonDraftTodos = await db.all(app.todos.where({ title: { ne: "Draft" } }));

// One of a set
const selectedTodos = await db.all(app.todos.where({ id: { in: [todoIdA, todoIdB] } }));
// Exact match
let query = QueryBuilder::new("todos")
    .filter_eq("done", Value::Boolean(false))
    .build();
let incomplete_todos = client.query(query, None).await?;

// Not equal
let query = QueryBuilder::new("todos")
    .filter_ne("title", Value::Text("Draft".into()))
    .build();
let non_draft_todos = client.query(query, None).await?;

Numeric comparisons

const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;

const recentTodos = await db.all(app.todos.where({ created_at: { gt: oneWeekAgo } }));
const highPriority = await db.all(app.todos.where({ priority: { gte: 3 } }));
const lowPriority = await db.all(app.todos.where({ priority: { lt: 10 } }));
let now_ms = std::time::SystemTime::now()
    .duration_since(std::time::UNIX_EPOCH)
    .unwrap()
    .as_millis() as u64;
let one_week_ago = Value::Timestamp(now_ms - 7 * 24 * 60 * 60 * 1000);

let query = QueryBuilder::new("todos")
    .filter_gt("created_at", one_week_ago)
    .build();
let recent_todos = client.query(query, None).await?;

let query = QueryBuilder::new("todos")
    .filter_ge("priority", Value::Integer(3))
    .build();
let high_priority = client.query(query, None).await?;

let query = QueryBuilder::new("todos")
    .filter_lt("priority", Value::Integer(10))
    .build();
let low_priority = client.query(query, None).await?;

String contains

// Substring match (case-sensitive)
const matches = await db.all(app.todos.where({ title: { contains: searchTerm } }));
// Substring match (case-sensitive)
let query = QueryBuilder::new("todos")
    .filter_contains("title", Value::Text(search_term.into()))
    .build();
let matches = client.query(query, None).await?;

Null checks on optional references

// Rows where the optional ref is not set
const unlinkedTodos = await db.all(app.todos.where({ parentId: { isNull: true } }));

// Rows where it is set
const linkedTodos = await db.all(app.todos.where({ parentId: { isNull: false } }));
// Rows where the optional ref is not set
let query = QueryBuilder::new("todos").filter_is_null("parent").build();
let unlinked_todos = client.query(query, None).await?;

// Rows where it is set
let query = QueryBuilder::new("todos")
    .filter_is_not_null("parent")
    .build();
let linked_todos = client.query(query, None).await?;

Multiple conditions (AND)

All predicates passed to where(...) / chained filter_* calls are AND-combined:

// done AND assigned to a project
const doneWithProject = await db.all(
  app.todos.where({
    done: true,
    projectId: { isNull: false },
  }),
);
// Multiple filter calls are AND-combined
let query = QueryBuilder::new("todos")
    .filter_eq("done", Value::Boolean(true))
    .filter_is_not_null("project")
    .build();
let done_with_project = client.query(query, None).await?;

Combining with ordering and limits

const recentIncomplete = await db.all(
  app.todos.where({ done: false }).orderBy("created_at", "asc").limit(50),
);
let query = QueryBuilder::new("todos")
    .filter_eq("done", Value::Boolean(false))
    .order_by("created_at")
    .limit(50)
    .build();
let recent_incomplete = client.query(query, None).await?;

Live subscriptions with WHERE

useAll and query subscriptions accept the same query builders as db.all. The subscription stays active and updates whenever any row enters or exits the filter:

export function subscribeOpenTodos(db: Db, onChange: (todos: unknown[]) => void) {
  return db.subscribeAll(app.todos.where({ done: false }), ({ all }) => onChange(all));
}
let query = QueryBuilder::new("todos")
    .filter_eq("done", Value::Boolean(false))
    .build();
let pending = client.subscribe(query).await?;

For reactive framework bindings (useAll in React/Vue, QuerySubscription in Svelte), see Framework Patterns.

On this page