Data types
Every type a field may be declared as, two kinds of absence, and the one order they all sort in.
A field with no declaration holds anything. DEFINE FIELD … TYPE narrows it to
one of these, and this is the whole set:
| written | holds |
|---|---|
any | any value at all — the same as not declaring a type |
none | the field is not present — what a read of a field a record does not have answers, and what a function answers when there is nothing to answer with |
null | the field is present and holds nothing — written deliberately, and a different fact from none; object::has tells the two apart |
bool | true or false |
number | any of the three numeric forms |
int | a signed integer |
float | a binary floating-point number |
decimal | an exact decimal — money, and anything else that must not drift |
string | text |
bytes | opaque bytes |
duration | a span of time, which may be negative |
datetime | a point in time |
uuid | a universally unique identifier, as its sixteen bytes |
table | a reference to a table |
record | a reference to one record |
array | an ordered sequence |
object | a map from field name to value |
range | a span between two values |
set | a collection with no duplicates and no significant order |
geometry | a shape on the sphere — see Geometry |
regex | a pattern, held rather than executed |
'draft' | 'published' | one of a fixed set of strings, and nothing else |
vector<768> | an array of exactly that many numbers |
DEFINE NAMESPACE prod;
USE NAMESPACE prod;
DEFINE DATABASE orders;
USE DATABASE orders;
DEFINE COLLECTION users;
DEFINE FIELD email ON users TYPE string;
DEFINE FIELD balance ON users TYPE decimal;
DEFINE FIELD seen_at ON users TYPE datetime;
DEFINE FIELD owner ON users TYPE record;
DEFINE FIELD tags ON users TYPE set;
DEFINE FIELD status ON users TYPE 'draft' | 'published';
A type name may be a reserved word, because after TYPE nothing else can
appear — the grammar knows what it is reading.
Every one of them can also be written as a column, in the parentheses of a
DEFINE TABLE — see Definitions.
The last two rows are the ones that are not words. Every other type is a name the grammar recognises on its own; those two carry a value — a set of permitted strings, and a width — and so they are written out rather than looked up.
A union of strings
The first of them is the one kind that is not a type of the value system but a subset of one:
DEFINE COLLECTION orders;
DEFINE FIELD state ON orders TYPE 'new' | 'paid' | 'shipped';
CREATE orders = { state: 'paid' };
TYPE string is true of a status column and says nothing; an ASSERT … IN [ … ]
says it, but says it where a reader of the schema does not look. The members are
held sorted and deduplicated, because a declared type is a set, and a set that
remembered the order somebody typed it in would be two values for one fact.
vector<n> — an array that declares how wide it is
DEFINE COLLECTION documents;
DEFINE FIELD embedding ON documents TYPE vector<3>;
DEFINE TABLE pages (title_at vector<2>, body_at vector<4>);
Both spellings take it, because it is a type rather than a spelling.
The width is what the declaration is for. TYPE array is true of a 768-wide
embedding and says nothing, so without a width a 512-wide row sits legally beside
a 768-wide one and nothing refuses either. The mistake then surfaces only where
the distance functions meet them — per read, long after the write, at the point
furthest from the cause — and not as an error, because a vector of the wrong
shape is infinitely far from everything. That is a plausible ordering rather
than a complaint, so the wrong answer looks exactly like a right one.
With a width, the refusal happens at the write and names both shapes:
CREATE documents:1 = { embedding: [0.1, 0.2, 0.3] };
CREATE documents:2 = { embedding: [0.1, 0.2] };
Refused at the second statement, and the message names both shapes:
error: table documents declares embedding as vector<3>, but record 2 holds
vector<2> there
It is declared on the field, not on the table, because that is where a vector
is: an array of numbers in an ordinary field, with the index built over it. The
pages table above holds two of them, each held to its own width — a width
declared for the table could only govern one and would leave the other exactly as
unchecked as it was.
Three boundaries follow from that:
- There is no width-less
vector. An array whose length nobody declared is thearraythis language already has, and a word that looked checked and was not would be worse than no word at all. - There is no
vector<0>. The only value such a field could hold is the empty array, which no distance can measure and no index will keep. - The width is a literal, not a parameter. A schema whose shape depended on what was bound when the declaration ran would leave the catalog with nothing single to store.
A declared width does not make the field mandatory — that is REQUIRED, and
it is a separate constraint, the same rule every other kind follows.
vector stays a name a caller may use, for a reason a database of embeddings
makes obvious: that is exactly where a field called vector turns up.
When the vectors are the data rather than a column of it, the store is declared in one statement instead — see Vectors.
Three numbers, not one
int, float and decimal are separate types and stay separate. They share a
sort rank, so they compare semantically — 1 and 1.0 and dec 1.00 order
together rather than by which type they happen to be — but a decimal is never
quietly turned into a float, because that is how money loses a hundredth.
Write a decimal with dec:
DEFINE COLLECTION ledgers;
CREATE ledgers = { balance: dec 10.05 };
How deep a value nests
A value nests at most 64 containers deep — arrays, sets, objects and ranges inside one another. Deeper is refused by name wherever it arrives, a statement or a frame on the wire, because every level costs a stack frame in the parser and the decoder, and a crafted frame used to be able to end the node before anybody had signed in. A statement also nests at most 64 expressions, so the deepest literal you can write is 63 arrays inside one another:
the statement nests more than 64 expressions deep (at 71..72)
A store written by 0.9 that holds a value deeper than 64 containers cannot be
read by 0.10.0-beta or anything later; flatten the value and ingest it again.
Two kinds of nothing
| means | |
|---|---|
NONE | the field is not present |
NULL | the field is present and holds nothing |
They are different values and the language keeps them apart everywhere — in conditions, in key-value reads, and in what a fold counts. Never written and written empty are different facts, and a store that merged them could not tell you which had happened.
Neither is a small number. They do not appear in ordered comparisons at all, which is what stops "youngest customer" from quietly becoming "the record nobody filled in".
One order, across types
Every value sorts against every other, by a declared order across types rather
than by an error. That is why SELECT * FROM people WHERE age > 18 answers even
when one record's age is the string 'nineteen' — a string orders after every
number, so the comparison has an answer instead of failing the whole read.
regex holds a pattern; it does not run one
There is no regular-expression engine in this store. A regex value round-trips
as a pattern — distinguishable from a string that happens to spell one — and
matching is a later capability rather than an implied one. Saying so is the
difference between a type that is honest about its scope and a type that looks
like a feature.
For patterns you can use today, see LIKE in
Conditions; for whole words, see
Full-text search.
geometry stores a shape
The type stores and returns a shape. Answering intersects, within or a
distance over one is an engine, and a separate thing — a stored geometry
with no index is already useful, and conflating the two is how the work stops
being schedulable. Both exist; see Geometry.
What a declaration adds beyond a type
| clause | effect |
|---|---|
REQUIRED | the field must hold a value: present, and not NULL |
DEFAULT <value> | what a write supplying no value uses instead |
ASSERT <constraint> | what the value must satisfy beyond its type |
ANALYZER <name> | the analyzer this field's text becomes terms by |
All four are covered in Definitions, including
the three shapes an ASSERT may not take.