Filters
A filter is a plain JavaScript/TypeScript object that describes which documents to match. TalaDB evaluates filters against each document's fields.
TypeScript type
type Filter<T extends Document = Document> = {
[K in keyof T]?: T[K] | FieldOps<T[K]>
} & {
$and?: Filter<T>[]
$or?: Filter<T>[]
$not?: Filter<T>
}Field shorthand (equality)
Providing a bare value for a field is equivalent to { $eq: value }.
// These are identical:
await users.find({ name: 'Alice' })
await users.find({ name: { $eq: 'Alice' } })Comparison operators
| Operator | Meaning | Example |
|---|---|---|
$eq | Equal to | { age: { $eq: 30 } } |
$ne | Not equal to | { status: { $ne: 'deleted' } } |
$gt | Greater than | { score: { $gt: 90 } } |
$gte | Greater than or equal | { age: { $gte: 18 } } |
$lt | Less than | { price: { $lt: 100 } } |
$lte | Less than or equal | { age: { $lte: 65 } } |
// Age between 25 and 40 (inclusive)
await users.find({ age: { $gte: 25, $lte: 40 } })Multiple operators on the same field are implicitly ANDed.
Membership operators
| Operator | Meaning | Example |
|---|---|---|
$in | Value is in the array | { role: { $in: ['admin', 'editor'] } } |
$nin | Value is not in the array | { tag: { $nin: ['spam', 'archive'] } } |
const staff = await users.find({ role: { $in: ['admin', 'moderator', 'editor'] } })
const clean = await posts.find({ tags: { $nin: ['spam', 'hidden'] } })Logical operators
$and
All conditions must match. Equivalent to a regular object with multiple fields.
// These are identical:
await users.find({ age: { $gte: 18 }, active: true })
await users.find({ $and: [{ age: { $gte: 18 } }, { active: true }] })Use the explicit $and form when you need two conditions on the same field or when mixing field conditions with logical operators.
$or
At least one condition must match.
await users.find({
$or: [{ role: 'admin' }, { role: 'superuser' }],
})Index acceleration for $or
When every branch of an $or uses an equality or range filter on indexed fields, TalaDB uses an IndexOr plan: it runs one index range scan per branch and merges the results in memory. Otherwise it falls back to a full scan.
$not
The condition must not match.
await users.find({ $not: { active: false } })Existence operator
// Documents that have an `avatar` field (value may be null)
await users.find({ avatar: { $exists: true } })
// Documents without a `deletedAt` field
await users.find({ deletedAt: { $exists: false } })Pattern matching ($regex)
Match string fields against a regular expression. The regex is compiled at query time and evaluated per document — there is no index acceleration.
await users.find({ email: { $regex: '@example\\.com$' } })| Behaviour | Notes |
|---|---|
| Non-string fields | Never match — $regex against a number or boolean always returns no results |
| Invalid pattern | Returns no results — the engine does not throw on a malformed regex |
| Character classes | Full PCRE-like syntax: \d, \w, \s, anchors ^ / $, groups, quantifiers |
| Case-insensitive | Use the inline flag: (?i)pattern |
// Domain suffix
await accounts.find({ website: { $regex: '\\.(io|com|dev)$' } })
// Starts with a digit
await codes.find({ ref: { $regex: '^\\d' } })
// Case-insensitive match (inline flag)
await tags.find({ label: { $regex: '(?i)^todo' } })
// Combined with other operators
await users.find({
$and: [
{ role: 'admin' },
{ email: { $regex: '@company\\.com$' } },
],
})Performance
$regex always performs a full collection scan — even when an index exists on the same field. For large collections, consider adding additional equality or range filters in an $and to narrow the candidate set first.
Nested field queries (dot-notation)
Use dot-notation to filter on fields inside nested objects. Any comparison or existence operator works on a nested path.
// Equality on a nested field
await users.find({ 'address.city': 'London' })
// Range on a nested field
await users.find({ 'location.altitude': { $gt: 1000 } })
// Three levels deep
await config.find({ 'server.tls.enabled': true })
// Existence check on a nested field
await profiles.find({ 'meta.avatarUrl': { $exists: true } })Nested paths resolve into Value::Object entries recursively. If any segment along the path is absent or is not an object, the document is treated as not matching (no error is thrown).
// Documents where address.city == 'London' are matched;
// documents where `address` is absent, not an object, or has no `city` are not matched.
await users.find({ 'address.city': 'London' })With logical operators
Dot-notation paths work inside $and, $or, and $not:
await users.find({
$or: [
{ 'address.city': 'London' },
{ 'address.city': 'Edinburgh' },
],
})
await users.find({
$and: [
{ 'address.country': 'UK' },
{ 'address.postcode': { $regex: '^SW' } },
],
})Index acceleration with nested fields
Standard secondary indexes (created with createIndex) work on top-level fields only. Filtering on a nested field always uses a full scan. If you frequently query a nested field, consider flattening it to a top-level field or using a compound index on the top-level fields you need.
Full-text search ($contains)
Available on fields indexed with createFtsIndex:
await posts.createFtsIndex('body')
// Matches documents whose `body` contains all three terms (after normalisation)
const results = await posts.find({ body: { $contains: 'rust embedded database' } })Search terms are normalised (lowercased, punctuation stripped). Terms shorter than 3 characters are ignored. The result set is post-filtered to ensure all terms appear.
Match all documents
Pass an empty filter or no filter at all:
const all = await users.find()
const all = await users.find({})Index acceleration summary
The query planner selects an index when the filter matches one of these patterns:
| Pattern | Plan |
|---|---|
{ field: value } or { field: { $eq: value } } on an indexed field | IndexEq — point lookup |
{ field: { $gt/$gte/$lt/$lte } } on an indexed field | IndexRange — B-tree range scan |
{ field: { $in: [...] } } on an indexed field | IndexIn — one point lookup per value |
$or where every branch is an indexed equality/range | IndexOr — one range scan per branch, merged |
$and with equality on all fields of a compound index | CompoundIndexEq — single B-tree range scan |
{ field: { $contains: '...' } } on an FTS-indexed field | FtsSearch — inverted token index |
| All other filters | FullScan — every document evaluated in memory |
Filter examples
// Exact match
await users.find({ email: 'alice@example.com' })
// Range
await products.find({ price: { $gte: 10, $lte: 50 } })
// Compound
await orders.find({
$and: [
{ status: 'shipped' },
{ createdAt: { $gte: Date.now() - 86_400_000 } },
],
})
// OR with multiple values
await notifications.find({
$or: [
{ type: 'mention' },
{ type: 'reply' },
{ type: 'reaction' },
],
})
// NOT
await sessions.find({ $not: { expired: true } })
// Existence
await profiles.find({ bio: { $exists: true } })
// Full-text
await articles.find({ content: { $contains: 'open source rust' } })
// Regex — domain match
await accounts.find({ email: { $regex: '@acme\\.com$' } })
// Nested field
await orders.find({ 'shipping.address.city': 'Berlin' })
// Compound index — equality on both indexed fields in one B-tree scan
await people.find({ $and: [{ lastName: 'Smith' }, { firstName: 'Alice' }] })