Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

4. Values and types

4.1 The value model

Documents have no schema. Every field holds one of six kinds:

Kindtype()Description
null"null"An explicit null
boolean"bool"true or false
number"number"A signed integer (i64), an unsigned integer (u64) or a finite float (f64). The physical kind is kept in storage
string"string"UTF-8 text
array"array"Ordered values of any kinds, mixed kinds allowed
object"object"A nested document

A field can also be missing: absent from the document. Missing is not null (4.4).

4.2 _id

Every stored document has an _id, a UUID v7 such as 01900000-0000-7000-8000-000000000003.

  • insert and load generate it when absent.
  • load accepts an explicit _id (32 hex digits, with or without dashes): idempotent imports and upserts.
  • where _id == "<uuid>" runs as a primary-key lookup instead of a scan.
  • select always keeps _id.
  • Rows made by group carry a deterministic synthetic _id.

Other reserved fields: _place and _app_instance (the scope of a document, written by the Core), _distance (written by near), and the history fields _doc_id, _version, _op, _generation, _at, _by, _by_device (chapter 8).

4.3 Comparison rules

  • Integers and floats compare numerically: age == 31.0 matches age: 31.
  • Strings compare lexicographically (by code point): name >= "B" and name < "D".
  • No cross-kind coercion. age == "31" is an error (operation "==" is incompatible with values …), not false.
    • Exception: == and != accept null on either side (nick? == null).
    • Exception: x in [...] never fails on kinds: an item of another kind just does not match.
  • Fields compare with fields: where age >= profile.score, where o.user == u.name.

Order between kinds (used by sort, group keys, distinct, window partitions): missing and null first, then booleans, numbers, strings, arrays, objects. A number and a numeric string are never compared by value.

4.4 Missing fields

A field read by a predicate or an expression must exist on every row that reaches it:

expression evaluation failed: field path "nickname" is missing

This is deliberate: a typo surfaces as an error instead of an empty result. Rows already streamed before the failing one have been sent, and the response ends in error.

The tools for optional fields:

ToolMeaningExample
field?Reads null when missingwhere nick? == "Zed", profile?.country
exists(x)true or false, never failswhere exists(nick) and nick != ""
coalesce(a, b, …)First present, non-null argumentcoalesce(nick, name)
optional(x)Same as x?optional(nick)
lazy and, or, if, caseUnevaluated branches are not readif(exists(x), x, 0)

Stages that tolerate missing fields: projections (select field, drop, rename), near (drops rows without the vector), unwind (passes rows without the array), group and pivot keys after a filter was pushed past them (read as optional, null).

Pattern. A collection fed by files of several shapes, where some rows lack date: on data | where date? != null | … keeps the dated rows and never fails.

4.5 Numbers

OperationResult
+ - * % on integersAn integer while it fits in i64 (qty * 2 + 1 → 7), else a float
Anything with a floatA float
/Always a float (7 / 2 → 3.5)
Division by zeroAn error: division by zero; use div(a, b, fallback) for a safe division
Unary -Integer negation when possible
sum (stage measure or function)An integer while every term is one and the total fits, else a float

On the wire, integers above 2³² travel as floats so that JavaScript clients never lose precision silently; integers beyond 2⁵³ are refused on the wire.

4.6 Text

Text is UTF-8. Positions count characters, not bytes; a negative start counts from the end. There is no + on strings: use concat.

4.7 Dates

There is no date type. A date is either:

  • an ISO-8601 string: "2026-03-09", "2026-03", "2026-03-09T10:00:00Z", "2026-03-09 10:00:00+02:00";
  • a number of milliseconds since the Unix epoch.

Every computation is in UTC. ISO strings of the same shape sort and compare correctly as text (month >= "2026-02"); mixing shapes or kinds does not. The date functions accept both and return epoch milliseconds, except date_format (text) and date_part (a number).