Skip to main content

Overview

Filters are the core of Criteria. They define conditions that records must match. Each filter consists of a field, an operator, and (usually) a value.
const criteria = Criteria.create<User>()
  .where("age", "greaterThan", 18)
  //      ↑         ↑           ↑
  //    field    operator     value

Filter Methods

Generic where()

The most flexible method - accepts any field, operator, and value:
criteria.where("status", "equals", "active");
criteria.where("age", "greaterThan", 18);
criteria.where("email", "contains", "@company.com");
criteria.where("role", "in", ["admin", "moderator"]);

Shorthand Methods

Convenience methods for common operations:
// Equality
criteria.whereEquals("status", "active");
// Equivalent to: .where("status", "equals", "active")

// String contains
criteria.whereContains("name", "john");
// Equivalent to: .where("name", "contains", "john")

// Array inclusion
criteria.whereIn("role", ["admin", "moderator"]);
// Equivalent to: .where("role", "in", ["admin", "moderator"])

// Range
criteria.whereBetween("age", 18, 65);
// Equivalent to: .where("age", "between", [18, 65])

// Null checks
criteria.whereNull("deletedAt");
criteria.whereNotNull("email");

Operators by Type

String Operators

OperatorDescriptionExample
equalsExact matchwhere("name", "equals", "John")
notEqualsNot equalwhere("status", "notEquals", "deleted")
containsContains substring (case-insensitive)where("bio", "contains", "developer")
startsWithStarts with prefixwhere("email", "startsWith", "admin")
endsWithEnds with suffixwhere("email", "endsWith", "@gmail.com")
inIn array of valueswhere("status", "in", ["active", "pending"])
notInNot in arraywhere("role", "notIn", ["banned", "suspended"])
isNullIs null/undefinedwhere("nickname", "isNull")
isNotNullIs not nullwhere("email", "isNotNull")
const criteria = Criteria.create<User>()
  .whereEquals("status", "active")
  .whereContains("name", "john")
  .where("email", "endsWith", "@company.com")
  .whereNotNull("verifiedAt");

Number Operators

OperatorDescriptionExample
equalsExact matchwhere("age", "equals", 25)
notEqualsNot equalwhere("score", "notEquals", 0)
greaterThanGreater thanwhere("age", "greaterThan", 18)
greaterThanOrEqualGreater than or equalwhere("price", "greaterThanOrEqual", 100)
lessThanLess thanwhere("quantity", "lessThan", 10)
lessThanOrEqualLess than or equalwhere("discount", "lessThanOrEqual", 50)
betweenBetween two values (inclusive)where("age", "between", [18, 65])
inIn array of valueswhere("status", "in", [1, 2, 3])
notInNot in arraywhere("priority", "notIn", [0, -1])
isNullIs nullwhere("score", "isNull")
isNotNullIs not nullwhere("rating", "isNotNull")
const criteria = Criteria.create<Product>()
  .where("price", "greaterThanOrEqual", 10)
  .where("price", "lessThan", 100)
  .whereBetween("stock", 1, 1000)
  .where("rating", "greaterThan", 4);

Date Operators

OperatorDescriptionExample
equalsExact matchwhere("date", "equals", specificDate)
notEqualsNot equalwhere("updatedAt", "notEquals", oldDate)
greaterThanAfter datewhere("createdAt", "greaterThan", lastWeek)
greaterThanOrEqualOn or afterwhere("startDate", "greaterThanOrEqual", today)
lessThanBefore datewhere("expiresAt", "lessThan", now)
lessThanOrEqualOn or beforewhere("deadline", "lessThanOrEqual", endOfMonth)
betweenWithin date rangewhere("createdAt", "between", [startDate, endDate])
isNullIs nullwhere("deletedAt", "isNull")
isNotNullIs not nullwhere("publishedAt", "isNotNull")
const now = new Date();
const lastWeek = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
const lastMonth = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);

const criteria = Criteria.create<Article>()
  .where("publishedAt", "greaterThan", lastWeek)
  .whereBetween("createdAt", lastMonth, now)
  .whereNull("deletedAt");

Boolean Operators

OperatorDescriptionExample
equalsIs true/falsewhere("isActive", "equals", true)
notEqualsOpposite valuewhere("isDeleted", "notEquals", true)
isNullIs nullwhere("isVerified", "isNull")
isNotNullIs not nullwhere("isAdmin", "isNotNull")
const criteria = Criteria.create<User>()
  .whereEquals("isActive", true)
  .whereEquals("isVerified", true)
  .where("isBanned", "notEquals", true);

Array Operators

For array fields in your entity:
OperatorDescriptionExample
inArray contains any of valueswhere("tags", "in", ["featured", "popular"])
notInArray doesn’t contain anywhere("categories", "notIn", ["archived"])
isNullIs null/emptywhere("permissions", "isNull")
isNotNullIs not null/emptywhere("roles", "isNotNull")
const criteria = Criteria.create<Article>()
  .where("tags", "in", ["javascript", "typescript"])
  .where("categories", "notIn", ["draft", "archived"]);

Nested Field Paths

Filter on nested object properties:
interface User {
  id: string;
  name: string;
  profile: {
    bio: string;
    location: {
      city: string;
      country: string;
    };
  };
  settings: {
    notifications: boolean;
    theme: string;
  };
}

const criteria = Criteria.create<User>()
  .whereContains("profile.bio", "developer")
  .whereEquals("profile.location.country", "Brazil")
  .whereEquals("settings.notifications", true);

Array Item Field Paths

Filter on properties of items in arrays:
interface User {
  id: string;
  posts: {
    title: string;
    published: boolean;
    views: number;
  }[];
  comments: {
    text: string;
    likes: number;
  }[];
}

const criteria = Criteria.create<User>()
  .whereContains("posts.title", "tutorial")
  .whereEquals("posts.published", true)
  .where("comments.likes", "greaterThan", 10);
When filtering on array item properties, the filter matches if any item in the array matches the condition. For more control, see Quantifiers.

Combining Multiple Filters

All filters are combined with AND logic:
const criteria = Criteria.create<User>()
  .whereEquals("status", "active")         // AND
  .where("age", "greaterThanOrEqual", 18)  // AND
  .whereNotNull("email")                   // AND
  .whereContains("name", "john");

// Matches users where:
// status = "active"
// AND age >= 18
// AND email IS NOT NULL
// AND name contains "john"

Filter Validation

The library validates filters at runtime:
// Invalid operator for type
const criteria = Criteria.create<User>()
  .where("age", "contains", "18");
// Throws: InvalidCriteriaError: Operator "contains" is not valid for type "number"

// This helps catch configuration errors early

Quantifiers

For array fields, you can specify how the filter should match:

whereSome - Any Item Matches

// Match if ANY post has > 100 views
criteria.whereSome("posts.views", "greaterThan", 100);

whereEvery - All Items Match

// Match if ALL posts are published
criteria.whereEvery("posts.published", "equals", true);

whereNone - No Item Matches

// Match if NO comment has negative likes
criteria.whereNone("comments.likes", "lessThan", 0);

With Query Params

// URL: /users?posts.views:greaterThan@some=100
const criteria = Criteria.fromQueryParams<User>({
  "posts.views:greaterThan@some": "100",
});
Quantifier support depends on your ORM/database. See the integration guides for specific implementations.

Real-World Examples

function searchUsers(query: string, filters: UserFilters) {
  let criteria = Criteria.create<User>()
    .whereEquals("status", "active")
    .whereNull("deletedAt");

  if (filters.role) {
    criteria = criteria.whereEquals("role", filters.role);
  }

  if (filters.minAge) {
    criteria = criteria.where("age", "greaterThanOrEqual", filters.minAge);
  }

  if (filters.verifiedOnly) {
    criteria = criteria.whereNotNull("verifiedAt");
  }

  if (query) {
    criteria = criteria.search(["name", "email", "bio"], query);
  }

  return criteria.orderByDesc("createdAt").paginate(1, 20);
}

Product Catalog

function getProducts(filters: ProductFilters) {
  let criteria = Criteria.create<Product>()
    .whereEquals("published", true)
    .whereNotNull("stock");

  if (filters.category) {
    criteria = criteria.whereEquals("categoryId", filters.category);
  }

  if (filters.priceRange) {
    criteria = criteria.whereBetween(
      "price",
      filters.priceRange.min,
      filters.priceRange.max
    );
  }

  if (filters.inStock) {
    criteria = criteria.where("stock", "greaterThan", 0);
  }

  if (filters.rating) {
    criteria = criteria.where("rating", "greaterThanOrEqual", filters.rating);
  }

  if (filters.tags?.length) {
    criteria = criteria.whereIn("tags", filters.tags);
  }

  return criteria;
}

Date Range Queries

function getOrdersByPeriod(period: "today" | "week" | "month" | "year") {
  const now = new Date();
  let startDate: Date;

  switch (period) {
    case "today":
      startDate = new Date(now.setHours(0, 0, 0, 0));
      break;
    case "week":
      startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
      break;
    case "month":
      startDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
      break;
    case "year":
      startDate = new Date(now.getTime() - 365 * 24 * 60 * 60 * 1000);
      break;
  }

  return Criteria.create<Order>()
    .where("createdAt", "greaterThanOrEqual", startDate)
    .whereNotNull("completedAt")
    .orderByDesc("createdAt");
}

Filter Interface

For reference, here’s the Filter type structure:
interface Filter<TField = string, TValue = unknown> {
  field: TField;
  operator: FilterOperator;
  value: TValue;
  options?: {
    quantifier?: "some" | "every" | "none";
  };
}

type FilterOperator =
  | "equals"
  | "notEquals"
  | "greaterThan"
  | "greaterThanOrEqual"
  | "lessThan"
  | "lessThanOrEqual"
  | "contains"
  | "startsWith"
  | "endsWith"
  | "in"
  | "notIn"
  | "between"
  | "isNull"
  | "isNotNull";