OMGDB DOCS
// Querying

Query Operators

Reference for the OMGDB find query language — comparison, element, logical, evaluation, and array operators, dotted paths, array semantics, and projection.


A filter is a JSON object matched against each document in a collection. Bare fields test equality; field values wrapping $-prefixed operators express richer predicates. Multiple fields in one filter are implicitly ANDed, and the empty filter {} matches every document.

You run a filter with find:

omgdb find app.omgdb users '{"age":{"$gte":30}}'

The filter is compiled and validated into an internal AST before matching, so structurally invalid filters and unknown operators are rejected up front with precise, LLM-friendly errors (see Errors and self-repair). For how filters choose an index or fall back to a scan, see indexes; to inspect the plan or debug a no-results query, see introspection.

The find command

omgdb find <store> <collection> [<filter>] [--limit N] [--project '<spec>']
Argument / flagMeaning
<store>Path to the store directory (e.g. app.omgdb).
<collection>Collection (namespace) name.
<filter>MongoDB-style filter as a JSON string. Defaults to {} (match all).
--limit NPrint at most N matching documents.
--project '<spec>'Projection JSON; see Projection.

Each match prints as one line of canonical JSON.

Comparison operators

OperatorDescriptionExample
$eqField equals the operand. This is the implicit operator for a bare value: {"name":"ana"} compiles to $eq.{"name":{"$eq":"ana"}}
$neField does not equal the operand. Also matches a missing field.{"age":{"$ne":25}}
$gtField is greater than the operand under the total value order.{"age":{"$gt":20}}
$gteField is greater than or equal to the operand.{"age":{"$gte":30}}
$ltField is less than the operand.{"age":{"$lt":30}}
$lteField is less than or equal to the operand.{"age":{"$lte":65}}
$inField equals any value in the operand array.{"role":{"$in":["admin","root"]}}
$ninField equals none of the values in the operand array. Also matches a missing field.{"role":{"$nin":["user"]}}

$in and $nin require an array operand; anything else is a compile error. Combining bounds on one field intersects them — {"age":{"$gt":20,"$lt":40}} matches ages strictly between 20 and 40.

{"name":"ana"}
{"age":{"$gte":30}}
{"age":{"$gt":20,"$lt":40}}
{"age":{"$ne":25}}
{"role":{"$in":["admin","root"]}}
{"role":{"$nin":["user"]}}

Note: $eq, $ne, $in, and $nin use value equality, where NaN never equals itself. The ordering operators ($gt/$gte/$lt/$lte) use the total order, which places NaN consistently. Equality and ordering can therefore disagree on NaN.

Element operators

OperatorDescriptionExample
$existsMatches on field presence (true) or absence (false).{"age":{"$exists":true}}
$typeField’s type name equals the operand string.{"age":{"$type":"long"}}

$exists requires a boolean operand and only tests whether the (dotted) path resolves — it does not inspect the value’s type. $type requires a single type-name string.

{"age":{"$exists":true}}
{"missing":{"$exists":false}}
{"age":{"$type":"long"}}

Type names come from the value’s BSON-style type. Note that integers report as long and floats as double:

Value$type name
nullnull
booleanbool
integer (i64)long
float (f64)double
stringstring
binary databinData
arrayarray
object / sub-documentobject
ObjectIdobjectId
date / timestampdate

Limitation: $type accepts only a single type-name string. The array-of-types form ({"$type":["long","double"]}) and numeric BSON type codes are not supported, and int/number are not recognised — only long matches an integer field.

Logical operators

OperatorDescriptionExample
$andAll listed sub-filters must match.{"$and":[{"a":5},{"b":"x"}]}
$orAt least one sub-filter must match.{"$or":[{"a":1},{"b":"x"}]}
$norNone of the listed sub-filters may match.{"$nor":[{"a":1},{"b":"y"}]}
$notNegates a field-level operator expression.{"a":{"$not":{"$gt":10}}}

$and, $or, and $nor each require an array of filter objects. Because every filter is already an implicit AND of its fields, $and is only needed when you want multiple conditions on the same field path that cannot be expressed in one object.

{"$and":[{"a":5},{"b":"x"}]}
{"$or":[{"a":1},{"b":"x"}]}
{"$nor":[{"a":1},{"b":"y"}]}
{"a":{"$not":{"$gt":10}}}

$not is a field-level operator: it wraps an operator-expression object (every key starting with $) and matches when that inner expression does not. It cannot wrap a bare scalar — {"a":{"$not":5}} is rejected as malformed.

Note: Index acceleration only inspects the root implicit AND of single-field predicates. A top-level $or, $nor, or $not, or any equality/range nested inside a logical operator, forces a full collection scan. See indexes.

Evaluation operators

OperatorDescriptionExample
$regexString field matches the regular expression.{"name":{"$regex":"^Ada"}}
$modInteger (or truncated float) field modulo divisor equals remainder.{"n":{"$mod":[4,2]}}

$regex uses Rust’s regex crate and is unanchored, so it is a substring match unless you anchor the pattern with ^ / $. An invalid pattern is a compile error. Flags are supplied with a sibling $options string in the same field object:

FlagEffect
iCase-insensitive
mMulti-line (^/$ match at line boundaries)
sDot matches newline (dotall)
xIgnore whitespace (extended / verbose mode)
{"name":{"$regex":"^Ada"}}
{"name":{"$regex":"^ada","$options":"i"}}
{"tags":{"$regex":"rust","$options":"i"}}

$mod requires a two-element [divisor, remainder] integer array with a non-zero divisor ([0,1] is rejected). It applies to integer fields directly and to float fields by truncating toward zero; non-numeric fields simply do not match.

{"n":{"$mod":[4,2]}}

Note: $options is only a modifier for $regex, read from the same field object. A standalone $options (with no $regex) compiles to a no-op that always matches; a non-string $options value is ignored.

Array operators

OperatorDescriptionExample
$sizeField is an array of exactly the given length.{"tags":{"$size":3}}
$allField is an array containing every listed value.{"tags":{"$all":["a","c"]}}
$elemMatchField is an array with at least one element satisfying the criteria.{"items":{"$elemMatch":{"qty":{"$gt":5}}}}

$size requires a non-negative integer and matches only when the field is an array. $all requires an array operand and tests plain element membership, again only on array fields.

$elemMatch requires an object operand and has two forms, chosen by the operand’s shape:

  • Document form — the operand is a sub-filter (with plain field keys), matched against each object element: {"items":{"$elemMatch":{"qty":{"$gt":5}}}}.
  • Scalar form — the operand is an operator expression (every key starts with $), matched against each scalar element: {"tags":{"$elemMatch":{"$eq":"b"}}}.
{"tags":{"$size":3}}
{"tags":{"$all":["a","c"]}}
{"items":{"$elemMatch":{"qty":{"$gt":5}}}}
{"tags":{"$elemMatch":{"$eq":"b"}}}

Limitation: $all does not support the nested $elemMatch-inside-$all form — it only checks plain element membership. An $elemMatch operand mixing $-keys and plain keys is treated as the document sub-filter form.

Dotted-path traversal

A field key is split on . into path segments. Each segment descends into an embedded document by key, or into an array by numeric index:

{"addr.city":"athens"}
{"items.0.sku":"x"}

addr.city reads the city field of the addr sub-document; items.0.sku reads the sku field of the first array element. Descending through a scalar or a missing key resolves to nothing, so the predicate fails to match — unless the operator tolerates absence ($exists:false, $ne, $nin, $not).

Note: Dotted-path predicates are never index-accelerated (an index is keyed on a single top-level field), so a filter like {"addr.city":"athens"} always scans.

Array-contains semantics

A scalar predicate on an array field matches if any element satisfies it (MongoDB’s multikey semantics). This applies to equality, the comparison operators, $in, and $regex:

{"tags":"rag"}
{"tags":{"$in":["db"]}}
{"tags":{"$regex":"rust","$options":"i"}}

{"tags":"rag"} matches a document whose tags array contains "rag". {"age":{"$gt":20}} matches if any element of an array-valued age field exceeds 20. $size, $all, and $elemMatch, by contrast, operate on the array as a whole and require the field itself to be an array.

Implicit AND

Multiple top-level fields in a filter are ANDed together — all conditions must hold:

{"role":"admin","age":{"$gte":18}}

This matches documents where role equals "admin" and age is at least 18. The empty filter {} is an AND of zero conditions and therefore matches every document.

Value ordering

The comparison operators (and range queries and sorting) use a single total order across all value types. Numbers compare numerically across i64/f64; values of different types compare by a fixed type rank:

null < number < string < object < array < bytes < bool < date < objectId

Arrays compare element-wise then by length; objects compare by (key, value) pairs in order then by length. NaN is ordered consistently (via total_cmp), which is why the ordering operators stay total even though $eq treats NaN as unequal to itself.

Projection

Pass --project with a JSON spec to control which top-level fields each result includes. A value of 1 (or true) includes a field; 0 (or false) excludes it.

omgdb find app.omgdb users '{"role":"admin"}' --project '{"name":1}'
{"name":1}            // include name (and _id by default)
{"name":1,"_id":0}    // include name, suppress _id
{"age":0}             // exclude age, keep everything else
{"name":1,"age":0}    // ERROR: cannot mix inclusion and exclusion

Rules:

  • _id is kept by default and does not set inclusion/exclusion mode; suppress it explicitly with "_id":0.
  • Inclusion and exclusion modes cannot be mixed (aside from _id). Any value other than an integer or boolean is rejected; in practice use 1/true for inclusion and 0/false for exclusion (any non-zero integer is treated as inclusion).

Limitation: Projection is top-level only — it cannot include or exclude nested dotted fields, and array projection operators ($slice, positional $, $elemMatch) are not implemented.

Errors and self-repair

Filters are validated at compile time. Two error kinds are surfaced:

  • Malformed query — a structurally invalid operand, e.g. $in without an array, $exists without a boolean, $mod with a zero divisor, or an invalid $regex pattern. The message names the exact problem.
  • Unknown query operator — an unrecognised $-prefixed token. The message echoes the exact bad token and, when a close match exists, adds a “did you mean” suggestion (nearest known operator within edit distance 2).
{"age":{"$gtee":1}}
// error: unknown query operator: $gtee (did you mean `$gte`?)

The recognised operators are: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $type, $not, $and, $or, $nor, $size, $all, $mod, $elemMatch, $regex.

To debug a query that returns nothing, omgdb diagnose reports the per-predicate selectivity (how many documents each top-level field condition matches alone, plus the observed value range for a condition that matches nothing), and omgdb explain describes whether the query will use an index or a full scan. See introspection and indexes. For multi-stage transformations beyond filtering, see aggregation.

Edit this page on GitHub →