Ada
ORM Filters

Drizzle Filter

Convert data table filters into Drizzle ORM SQL conditions.

Ada includes a Drizzle ORM filter builder that converts filter state into Drizzle SQL expressions. It imports from drizzle-orm and drizzle-orm/pg-core.

Installation

npx shadcn@latest add https://ada-table.vercel.app/r/drizzle-filter.json

Usage

import { and, or } from "drizzle-orm";
import { buildFilterCondition } from "@/components/data-table/integrations/drizzle";
import { tasksTable } from "@/db/schema";

const conditions = search.filters
  .map((f) => buildFilterCondition(tasksTable, f))
  .filter(Boolean);

const where = search.joinOperator === "and"
  ? and(...conditions)
  : or(...conditions);

const tasks = await db.select().from(tasksTable).where(where);

API

buildFilterCondition(table, filter)

Converts a single filter model into a Drizzle SQL condition:

function buildFilterCondition<T extends PgTable>(
  table: T,
  filter: FilterModel
): SQL | undefined;
  • table — Your Drizzle table definition. The function looks up the column by filter.columnId.
  • Returns undefined if the column doesn't exist or the filter can't be mapped.

Full Example

routes/tasks.tsx
import { and, or, count, desc, asc } from "drizzle-orm";
import { buildFilterCondition } from "@/components/data-table/integrations/drizzle";
import { tasksTable } from "@/db/schema";
import { db } from "@/db";

export async function getTasks(search: DataTableSearchState) {
  const conditions = search.filters
    .map((f) => buildFilterCondition(tasksTable, f))
    .filter((c): c is SQL => c != null);

  const where = conditions.length > 0
    ? search.joinOperator === "and"
      ? and(...conditions)
      : or(...conditions)
    : undefined;

  const orderBy = search.sort.map((s) =>
    s.desc ? desc(tasksTable[s.id]) : asc(tasksTable[s.id])
  );

  const [data, countResult] = await Promise.all([
    db.select()
      .from(tasksTable)
      .where(where)
      .orderBy(...orderBy)
      .limit(search.perPage)
      .offset((search.page - 1) * search.perPage),
    db.select({ count: count() })
      .from(tasksTable)
      .where(where),
  ]);

  return {
    data,
    pageCount: Math.ceil(countResult[0].count / search.perPage),
  };
}

Filter Mapping Reference

Text Filters

OperatorDrizzle Expression
containsilike(col, '%value%')
does not containnotIlike(col, '%value%')
iseq(col, value)
is notne(col, value)
is emptyor(isNull(col), eq(col, ''))
is not emptyand(isNotNull(col), ne(col, ''))

Number Filters

OperatorDrizzle Expression
iseq(col, value)
is notne(col, value)
is greater thangt(col, value)
is greater than or equal togte(col, value)
is less thanlt(col, value)
is less than or equal tolte(col, value)
is betweenbetween(col, min, max)
is not betweennot(between(col, min, max))

Date Filters

OperatorDrizzle Expression
iseq(col, date)
is notne(col, date)
is beforelt(col, date)
is aftergt(col, date)
is betweenbetween(col, start, end)
is not betweennot(between(col, start, end))

Option Filters

OperatorDrizzle Expression
iseq(col, value)
is notne(col, value)
is any ofor(eq(col, v1), eq(col, v2), ...)
is none ofnot(or(eq(col, v1), ...))

Multi-Option Filters

OperatorDrizzle Expression
include / include any ofarrayOverlaps(col, values)
exclude / exclude if any ofnot(arrayOverlaps(col, values))
include all ofarrayContains(col, values)
exclude if allnot(arrayContains(col, values))

PostgreSQL Only

The Drizzle filter builder uses PgColumn and PgTable types from drizzle-orm/pg-core. It relies on PostgreSQL-specific functions like arrayOverlaps and arrayContains for multi-option columns. If you need MySQL or SQLite support, you can adapt the builder by replacing these with equivalent expressions.

On this page