Query Syntax
Select documents with filter, then sort, paginate, and search the results.
Overview
Filters are JSON objects that describe which documents to match. The same syntax works everywhere a filter is accepted.
Where filters work:
data.query,data.count,data.aggregate,data.update,data.upsert,data.archive,data.deletefiles.query(fields belong to file records)users.query(fields must start withdata., see Users)
Key features:
- Bare values mean equality
- Inline operators:
"price:$gte": 10 - Comparison, array, string, type, and logical operators
- Shortcuts for dates, missing values, and empty arrays or strings
- Page-based or offset-based pagination
Example:
json
{
"operation": "data.query",
"payload": {
"collection": "products",
"filter": {
"status": "active",
"price:$between": [10, 100],
"tags:$includes": "sale"
},
"sort": "price asc",
"per_page": 20
}
}Filter Syntax
Multiple conditions are joined with AND.
json
{
"status": "active",
"stats.views:$gte": 10,
"tags:$includes": "api"
}Write an operator inline as field:$operator, or as a nested object. These are equivalent:
json
{ "price:$gte": 10, "price:$lt": 100 }json
{ "price": { "$gte": 10, "$lt": 100 } }Field paths:
- Dot notation for nested fields:
profile.city []for array traversal:people[].name.[*]is not valid
Complete Operator Index
The following operators and shortcuts are accepted by lib_kokoa.py. Names are case-sensitive. Use field:$operator or a nested operator object, except logical operators, which belong at filter level.
| Group | Supported names |
|---|---|
| Equality | $eq, $ne |
| Ordered comparison | $gt, $gte, $lt, $lte, $between |
| Presence and type | $exists, $type |
| Membership | $in, $nin, $includes, $nincludes, $all, $any, $none |
| Arrays | $elemMatch, $size |
| Strings | $startsWith, $endsWith, $contains, $ilike, $istartsWith, $iendsWith, $icontains, $regex |
| Logical | $and, $or, $nor, $not |
| Date aliases | $before, $after, $since, $until |
| Missing and empty values | $orMissing, $nex, $empty, $blank |
| Range complement | $outside |
Nested arrays and missing values
json
{
"operation": "data.query",
"payload": {
"collection": "orders",
"filter": {
"items:$elemMatch": {"sku:$istartsWith": "bag-", "qty:$gte": 2},
"tags:$none": ["internal", "test"],
"discount:$orMissing": {"$lte": 10},
"summary:$blank": false,
"price:$outside": [20, 50]
}
}
}$blank checks the empty string, not whitespace. $outside uses strict comparisons beyond the two bounds. $orMissing also accepts a bare scalar, which means equality or absent/null. Use $elemMatch when several conditions must apply to the same array element; people[].name traverses an array field.
Comparison and Presence
| Operator | Matches | Example |
|---|---|---|
$eq | Equal value | "status:$eq": "active" |
$ne | Present and different | "status:$ne": "draft" |
$gt, $gte | Greater than / greater than or equal | "price:$gte": 10 |
$lt, $lte | Less than / less than or equal | "price:$lt": 100 |
$between | Inclusive range | "price:$between": [10, 100] |
$exists | Present (true) or absent/null (false) | "summary:$exists": true |
$type | Value type | "price:$type": "number" |
Rules:
- Ordered comparisons accept numbers or strings, not booleans.
$betweentakes exactly two bounds.$neonly matches documents where the field is present. Use$nexto include missing fields.$typeacceptsnumber,boolean,string,array,object,null,integer,real,text,true, andfalse.
Arrays and Membership
| Operator | Matches | Example |
|---|---|---|
$in | Value is in the list | "status:$in": ["active", "draft"] |
$nin | Value is not in the list | "status:$nin": ["deleted"] |
$includes | Array contains the value | "tags:$includes": "api" |
$nincludes | Array does not contain the value | "tags:$nincludes": "internal" |
$all | Array contains every listed value | "tags:$all": ["api", "docs"] |
$any | Array contains at least one listed value | "tags:$any": ["api", "docs"] |
$none | Array contains none of the listed values | "tags:$none": ["internal", "draft"] |
$size | Array length | "tags:$size": {"$gte": 2} |
$elemMatch | One array element matches a filter | "items:$elemMatch": {"sku": "A1", "qty:$gte": 2} |
Rules:
- List operands must not be empty.
$sizeaccepts an integer, or an object using$eq,$ne,$gt,$gte,$lt, and$lte.
Strings
| Operator | Matches | Example |
|---|---|---|
$startsWith | Prefix | "sku:$startsWith": "A-" |
$endsWith | Suffix | "email:$endsWith": "@example.com" |
$contains | Substring | "title:$contains": "API" |
$istartsWith, $iendsWith, $icontains | Case-insensitive versions | "title:$icontains": "api" |
$ilike | Case-insensitive LIKE pattern | "title:$ilike": "%api%" |
$regex | Regular expression | "slug:$regex": "^release-[0-9]+$" |
$ilike keeps % and _ as LIKE wildcards. Invalid regular expressions are rejected.
Logical Operators
Use $and, $or, $nor, and $not at the filter level.
json
{
"operation": "data.query",
"payload": {
"collection": "notes",
"filter": {
"status": "active",
"$or": [
{ "tags:$includes": "api" },
{ "stats.views:$gte": 100 }
],
"$not": { "title:$startsWith": "Internal" }
}
}
}| Operator | Takes | Matches when |
|---|---|---|
$and | Non-empty array of filters | Every filter matches |
$or | Non-empty array of filters | At least one filter matches |
$nor | Non-empty array of filters | No filter matches |
$not | One filter object | The filter does not match |
All top-level conditions still apply. Logical operators cannot be placed inside a field's operator object.
Shortcuts
Shortcuts expand into standard filters.
| Shortcut | Meaning | Example |
|---|---|---|
$before, $after | $lt, $gt | "_created_at:$before": "2026-01-01" |
$since, $until | $gte, $lte | "_created_at:$since": "2026-01-01" |
$nex | Not equal, or absent/null | "plan:$nex": "free" |
$orMissing | Matches the condition, or absent/null | "price:$orMissing": {"$lt": 100} |
$empty | true: array absent, null, or empty. false: present and non-empty | "tags:$empty": true |
$blank | true: string absent, null, or "". false: present and non-empty | "summary:$blank": true |
$outside | Below or above an inclusive range | "price:$outside": [10, 100] |
$orMissing cannot contain another shortcut that expands into a logical clause.
Example - Documents created this year that are not marked free:
json
{
"filter": {
"_created_at:$since": "2026-01-01",
"plan:$nex": "free"
}
}Sorting
Pass sort to data.query as "<field> asc" or "<field> desc". The default is _created_at desc.
json
{
"operation": "data.query",
"payload": {
"collection": "products",
"sort": "price asc"
}
}Pagination
Use page-based or offset-based pagination. Pick one style per request.
Page-based:
json
{
"operation": "data.query",
"payload": {
"collection": "notes",
"page": 2,
"per_page": 20
}
}Offset-based:
json
{
"operation": "data.query",
"payload": {
"collection": "notes",
"limit": 20,
"offset": 20
}
}Pages start at 1. data.query defaults to page: 1 and per_page: 20.
Pagination metadata:
json
{
"meta": {
"pagination": {
"total_items": 84,
"count": 20,
"per_page": 20,
"page": 2,
"total_pages": 5,
"next_page": 3,
"prev_page": 1
}
}
}Loop through documents with the JavaScript SDK:
The SDK returns data directly, so this offset loop stops when a batch is shorter than the requested limit. For a stable traversal, avoid concurrent inserts/deletes and choose a deterministic sort for your data. See JavaScript SDK.
javascript
const all = [];
const limit = 100;
for (let offset = 0; ; offset += limit) {
const documents = await sbc.data.query({
collection: "notes", limit, offset, sort: "_id asc",
});
all.push(...documents);
if (documents.length < limit) break;
}Other operations:
| Operation | Pagination |
|---|---|
files.query | page/per_page (defaults 1 and 20) or limit/offset |
users.query | page/per_page or limit/offset, no API defaults |
chat.list | limit/offset only |
Search
Use search with data.query for full-text search. Combine it with filter to narrow the matches.
json
{
"operation": "data.query",
"payload": {
"collection": "articles",
"search": "API documentation",
"filter": { "status": "published" },
"per_page": 10
}
}Full-text search depends on the collection's configuration. To match text in one field, use a string operator such as $icontains.