Browse all documentation

Filters

Keep only the documents that match: compare values, search text, match list items, combine conditions, and filter inside groups, arrays, and blocks.

A filter keeps only the documents that match it. Pass it as Where in Go and where in TypeScript and REST. Examples use the example schema.

Published posts:

Go
page, err := local.List(ctx, "posts", ridu.ListOptions{
  Where: query.Equal("status", "published"),
  Actor: user,
})
TypeScript
const page = await ridu.list('posts', {
  where: { status: { equals: 'published' } }
});
REST
curl -G "$RIDU/api/collections/posts" \
  -H "Authorization: Session $TOKEN" \
  --data-urlencode 'where={"status":{"equals":"published"}}'

The rest of this page shows only the filter. In TypeScript, filters are typed PostsWhere, which ridu generate writes from the collection, so an unknown field or operator fails to compile. In REST, the filter is the where parameter.

Match a value

Posts that are published, and posts that aren’t:

Go
published := query.Equal("status", "published")
notPublished := query.NotEqual("status", "published")
TypeScript
const published: PostsWhere = { status: { equals: 'published' } };
const notPublished: PostsWhere = {
  status: { notEquals: 'published' }
};
REST
where={"status":{"equals":"published"}}
where={"status":{"notEquals":"published"}}

equals: null matches a field with no value.

Match one of several values

Posts that are drafts or published:

Go
draftOrPublished := query.In("status", "draft", "published")
TypeScript
const draftOrPublished: PostsWhere = {
  status: { in: ['draft', 'published'] }
};
REST
where={"status":{"in":["draft","published"]}}

To exclude several values, wrap in in not.

Compare numbers and dates

Posts with at least 600 views:

Go
popular := query.GreaterThanEqual("views", 600)
TypeScript
const popular: PostsWhere = { views: { greaterThanEqual: 600 } };
REST
where={"views":{"greaterThanEqual":600}}

Posts published since 15 September 2026:

Go
recent := query.GreaterThanEqual("publishedAt", "2026-09-15")
TypeScript
const recent: PostsWhere = {
  publishedAt: { greaterThanEqual: '2026-09-15' }
};
REST
where={"publishedAt":{"greaterThanEqual":"2026-09-15"}}

Use greaterThan, greaterThanEqual, lessThan and lessThanEqual on numbers, dates and text. Write a date in the field’s own format.

Search text

contains matches part of a value, ignoring case. like matches values that contain every word you give, in any order.

Titles containing “ridu”, and titles with both “sdk” and “typescript”:

Go
aboutRidu := query.Contains("title", "ridu")
sdkGuides := query.Like("title", "sdk typescript")
TypeScript
const aboutRidu: PostsWhere = { title: { contains: 'ridu' } };
const sdkGuides: PostsWhere = { title: { like: 'sdk typescript' } };
REST
where={"title":{"contains":"ridu"}}
where={"title":{"like":"sdk typescript"}}

The first matches “Hello, Ridu”; the second matches “TypeScript SDK tour”. Neither ranks results by relevance.

Check that a field is set

Posts that haven’t been given a publish date:

Go
undated := query.Exists("publishedAt", false)
TypeScript
const undated: PostsWhere = { publishedAt: { exists: false } };
REST
where={"publishedAt":{"exists":false}}

exists: true matches documents where the field has a value. An empty list counts as a value.

Combine conditions

Published posts that are tagged “typescript” or have more than 1,000 views:

Go
featured := query.And(
  query.Equal("status", "published"),
  query.Or(
    query.In("tags", "typescript"),
    query.GreaterThan("views", 1000),
  ),
)
TypeScript
const featured: PostsWhere = {
  and: [
    { status: { equals: 'published' } },
    {
      or: [
        { tags: { in: ['typescript'] } },
        { views: { greaterThan: 1000 } }
      ]
    }
  ]
};
REST
{
  "and": [
    { "status": { "equals": "published" } },
    {
      "or": [
        { "tags": { "in": ["typescript"] } },
        { "views": { "greaterThan": 1000 } }
      ]
    }
  ]
}

Posts not tagged “go”:

Go
notAboutGo := query.Not(query.In("tags", "go"))
TypeScript
const notAboutGo: PostsWhere = { not: { tags: { in: ['go'] } } };
REST
where={"not":{"tags":{"in":["go"]}}}

and and or take two or more conditions; not takes one.

Lists and has-many fields

Some fields hold several items: text and number lists, multiple selects, and relationships or uploads that allow many documents. Filter them with in, which matches a document when any of its items equals any value you give.

Posts tagged “guide” or “launch”:

Go
guidesOrLaunches := query.In("tags", "guide", "launch")
TypeScript
const guidesOrLaunches: PostsWhere = {
  tags: { in: ['guide', 'launch'] }
};
REST
where={"tags":{"in":["guide","launch"]}}

Matching is exact, so “guide” doesn’t match “guidebook”. To require two tags, join two in filters with and; to exclude a tag, wrap in in not. These fields also accept exists and equals: null. Any other operator is refused, rather than matching nothing.

A has-many relationship takes document IDs: { authors: { in: [adaID] } } finds posts that list Ada among their authors. A polymorphic relationship can point into several collections, so each value names one: { subjects: { in: [{ relationTo: 'posts', id: postID }] } }, or query.Reference("posts", postID) in Go.

Fields in groups, arrays and blocks

Reach a nested field with a dotted path of field names.

Posts whose SEO title is “Hello Ridu”:

Go
seoTitled := query.Equal("seo.title", "Hello Ridu")
TypeScript
const seoTitled: PostsWhere = {
  'seo.title': { equals: 'Hello Ridu' }
};
REST
where={"seo.title":{"equals":"Hello Ridu"}}

A path through an array uses the array’s name. A path through a blocks field adds the block’s slug, so a note block’s text inside a card block inside a layout field is layout.card.children.note.text. Row positions never appear in a path.

A path through arrays or blocks reaches the field in every row, so a document can match on any row:

Comparison Matches when
equals, in, contains, like, ranges, exists: true at least one row’s value matches
notEquals, exists: false, not no row’s value matches the opposite comparison
equals: null a row’s value is null, or no row has the field

Each condition in an and is matched on its own, so two conditions can be met by two different rows. Paths inside a rich-text or other plugin field aren’t filterable; store values you need to search in ordinary fields.

JSON fields

A JSON field can hold any value, so a comparison applies only when the stored value is a scalar of the same type. contains and like match a stored string, ignoring case: { metadata: { contains: 'plan' } } matches "Launch PLAN". An array or object never contains text and never equals a scalar; it only counts as present for exists. Use a text list when items should match one by one.

A path into a JSON value, such as metadata.owner, follows object keys and stops at an array. PostgreSQL, SQLite and the in-memory store support these paths; MongoDB refuses them.

Operators

TypeScript and REST Go Matches
equals, notEquals query.Equal, query.NotEqual an exact value, or any other value
in query.In one of several values; for lists, any item equals one
greaterThan, greaterThanEqual query.GreaterThan, query.GreaterThanEqual a larger number, date or text
lessThan, lessThanEqual query.LessThan, query.LessThanEqual a smaller number, date or text
contains query.Contains text that contains the value, ignoring case
like query.Like text that contains every word of the value, ignoring case
exists query.Exists a field that has a value, or doesn’t
and, or, not query.And, query.Or, query.Not combinations of other filters

New to building filters in Go? Filters and paths explains query.Expression and how the same filters work in access rules.

When a filter is refused

Ridu checks a filter before it reads anything. An unknown path, an operator the field doesn’t support, or a field with a read rule fails with bad_query (400) in Go, over REST and in the SDK. The issue code says why: unsupported_operator or unsupported_path.

REST response
{
  "error": {
    "code": "bad_query",
    "status": 400,
    "issues": [
      {
        "code": "unsupported_operator",
        "path": "tags",
        "message": "text list \"tags\" does not support \"equal\" …"
      }
    ]
  }
}

Over HTTP, a where holds at most 100 comparisons, nested at most 16 deep, with at most 100 values in one in. A path has at most 64 segments and 4,096 bytes.