Filtering

When you use a field as an input to a list action, Keel generates a structured query input for that field. Instead of passing a plain value, you pass an object with one or more operators that describe how to filter.

schema.keel
model Product {
  fields {
    name Text
    price Decimal
    releaseDate Date
  }
  actions {
    list listProducts(name?, price?, releaseDate?)
  }
}
// POST /api/json/listProducts
{
  "where": {
    "name": { "contains": "Widget" },
    "price": { "greaterThan": 10.00 },
    "releaseDate": { "after": "2024-01-01" }
  }
}

Each field type has its own set of available operators. All operators are optional, and when multiple are provided they are AND'd together. The full set of operators for each type is documented below.

Text

Text fields (including Markdown) support exact matching, partial matching, and set membership.

OperatorTypeDescription
equalsstring | nullExact match. Pass null to find records where the field is null.
notEqualsstring | nullExcludes exact match. Pass null to find records where the field is not null.
startsWithstringMatches values that start with the given string.
endsWithstringMatches values that end with the given string.
containsstringMatches values that contain the given string.
oneOfstring[]Matches any of the provided values.
// Find products with "Pro" in the name, excluding a specific one
{
  "name": {
    "contains": "Pro",
    "notEquals": "Pro Max Legacy"
  }
}

Number

Number fields support exact matching, comparison, and set membership.

OperatorTypeDescription
equalsnumber | nullExact match.
notEqualsnumber | nullExcludes exact match.
lessThannumberMatches values strictly less than.
lessThanOrEqualsnumberMatches values less than or equal to.
greaterThannumberMatches values strictly greater than.
greaterThanOrEqualsnumberMatches values greater than or equal to.
oneOfnumber[]Matches any of the provided values.
// Find products with stock between 10 and 100
{
  "stock": {
    "greaterThanOrEquals": 10,
    "lessThanOrEquals": 100
  }
}

Decimal

Decimal fields support the same operators as Number.

OperatorTypeDescription
equalsnumber | nullExact match.
notEqualsnumber | nullExcludes exact match.
lessThannumberMatches values strictly less than.
lessThanOrEqualsnumberMatches values less than or equal to.
greaterThannumberMatches values strictly greater than.
greaterThanOrEqualsnumberMatches values greater than or equal to.
oneOfnumber[]Matches any of the provided values.

Boolean

Boolean fields support equality checks only.

OperatorTypeDescription
equalsboolean | nullExact match.
notEqualsboolean | nullExcludes exact match.
// Find all active products
{
  "isActive": {
    "equals": true
  }
}

Date

Date fields support exact matching, range comparisons, and relative date expressions. Values are in ISO 8601 format (YYYY-MM-DD).

OperatorTypeDescription
equalsstring | nullExact date match.
notEqualsstring | nullExcludes exact date match.
beforestringMatches dates strictly before.
onOrBeforestringMatches dates on or before.
afterstringMatches dates strictly after.
onOrAfterstringMatches dates on or after.
beforeRelativestringMatches dates before a relative period.
afterRelativestringMatches dates after a relative period.
equalsRelativestringMatches dates within a relative period.
// Find orders placed in Q1 2024
{
  "orderDate": {
    "onOrAfter": "2024-01-01",
    "before": "2024-04-01"
  }
}

Relative date expressions

The beforeRelative, afterRelative, and equalsRelative operators accept a string expression in the following format:

    attribute   value  complete          period
       v          v       v                 v
{this/next/last} {x}? {complete}? {minute/hour/day/week/month/year}
ComponentUsage
Attributethis, next, or last
ValueA positive integer. Must be omitted when using this. Defaults to 1 when omitted with next or last.
Completecomplete — when included, the period is a calendar-aligned boundary. For example, last month is a rolling 30-day window, while last complete month is the previous calendar month.
Periodminute, hour, day, week, month, or year. Plural forms are also accepted.

There are also a few shorthands:

ShorthandEquivalent
nowThe current moment
todaythis day
tomorrownext complete day
yesterdaylast complete day

Relative dates are timezone dependent and default to UTC. Use the Time-Zone header to specify the user's timezone. The value must be a IANA time zone (opens in a new tab) string. If using the generated client, it uses the user's timezone automatically.

Timestamp

Timestamp fields support range comparisons and relative expressions. Values are in ISO 8601 format with time (YYYY-MM-DDTHH:mm:ss.sssZ).

OperatorTypeDescription
beforestringMatches timestamps strictly before.
afterstringMatches timestamps strictly after.
beforeRelativestringMatches timestamps before a relative period.
afterRelativestringMatches timestamps after a relative period.
equalsRelativestringMatches timestamps within a relative period.
// Find records created in the last 7 days
{
  "createdAt": {
    "afterRelative": "last 7 days"
  }
}

Unlike Date, Timestamp does not support equals, notEquals, onOrBefore, or onOrAfter. Use before and after to define ranges.

Duration

Duration fields support exact matching and comparisons. Values are in ISO 8601 duration format (opens in a new tab) (e.g. P1D for one day, PT2H30M for two hours and thirty minutes).

OperatorTypeDescription
equalsstring | nullExact match.
notEqualsstring | nullExcludes exact match.
lessThanstringMatches durations strictly less than.
lessThanOrEqualsstringMatches durations less than or equal to.
greaterThanstringMatches durations strictly greater than.
greaterThanOrEqualsstringMatches durations greater than or equal to.
// Find tasks with estimated time under 2 hours
{
  "estimatedTime": {
    "lessThan": "PT2H"
  }
}

ID

ID fields support exact matching and set membership.

OperatorTypeDescription
equalsstring | nullExact match.
notEqualsstring | nullExcludes exact match.
oneOfstring[]Matches any of the provided IDs.

Enum

Enum fields support exact matching and set membership. Values are the enum member names as strings.

OperatorTypeDescription
equalsstring | nullExact match.
notEqualsstring | nullExcludes exact match.
oneOfstring[]Matches any of the provided values.
schema.keel
enum Status {
  Draft
  Published
  Archived
}
 
model Post {
  fields {
    status Status
  }
  actions {
    list listPosts(status?)
  }
}
// Find posts that are either Draft or Published
{
  "where": {
    "status": {
      "oneOf": ["Draft", "Published"]
    }
  }
}

Null checks

For fields that are optional in your schema, the equals and notEquals operators accept null. This lets you filter for records where a field has or has not been set.

schema.keel
model Task {
  fields {
    completedAt Timestamp?
  }
  actions {
    list listTasks(completedAt?)
  }
}
{
  "where": {
    "completedAt": {
      "equals": null
    }
  }
}

Array fields

When a field is an array type (e.g. Text[], Number[]), the query input has a different structure. At the top level you can check if the entire array exactly matches or doesn't match a set of values. You can also use any and all to filter based on individual elements.

OperatorTypeDescription
equalsT[] | nullThe array exactly equals the provided values.
notEqualsT[] | nullThe array does not exactly equal the provided values.
anyobjectAt least one element in the array matches the nested condition.
allobjectEvery element in the array matches the nested condition.

The operators available inside any and all are the same as those for the corresponding scalar type, except that equals and notEquals inside any/all are not nullable, and oneOf is not available.

schema.keel
model Product {
  fields {
    tags Text[]
    scores Number[]
  }
  actions {
    list listProducts(tags?, scores?)
  }
}
// Find products where any tag contains "sale"
// and all scores are greater than 80
{
  "where": {
    "tags": {
      "any": {
        "equals": "sale"
      }
    },
    "scores": {
      "all": {
        "greaterThan": 80
      }
    }
  }
}

Non-filterable types

The following field types cannot be used as inputs to list actions and do not have query inputs:

  • Secret — encrypted fields
  • Password — hashed fields
  • File — binary file fields
  • Vector — embedding vectors