bijection/schema.ts:
bijection/schema.ts
active_customers has one row for each customer that isn’t archived, with the
fields name and region, plus the system fields _id and _creationTime of
the customer it came from.
Read on to learn how keys and expressions work.
The defineView constructor
defineView takes an object with these fields:
Register the view in
defineSchema under the name you’ll read it by. The name
shares its namespace with your tables.
A view has no validator of its own. Bijection infers the view’s document type
from its expression and the validators of the tables it reads, so
Doc<"active_customers"> in your generated types is:
Keys
Every view row is an object with a stable identity: the_id of one document in
the key table. The key is written as { from: "<table>", field: "_id" }, and
_id is the only supported key field.
The key table must be the table the expression starts from. Bijection follows the
expression’s main chain, through .as(), .filter(), .select() and
joins with cardinality: "one", back to the
first q.table(...). If that table is itself a view, the key table is that
view’s key table. For a view over customers, the key is always
{ from: "customers", field: "_id" }.
Because rows are keyed this way:
- A view’s
_idhas the key table’s ID type, soDoc<"active_customers">["_id"]isId<"customers">. - A view has at most one row per key. A customer either is or isn’t a member of
active_customers, and a change to its fields can’t make it a different object. - Bijection refuses a view whose main chain doesn’t preserve one row per key:
a
groupByor acardinality: "many"join directly in the main chain can’t keep a single source_idfor each row. Use grouped results on the right side of a join instead, as shown in Joins and aggregates.
Grouped or compound view keys, where a row is identified by a tuple of field
values instead of a document ID, aren’t supported.
Building expressions with q
Import q from bijection/server. An expression starts with q.table and
chains relational operators. Each operator returns a new relation, so you can
build expressions in variables and reuse them.
Referring to fields
Useq.field(path) to refer to a field of the current row. Nested fields use a
dot-separated path such as q.field("address.city").
After .as("customer"), every field of the row lives under customer, so you
refer to it as q.field("customer.name"). Aliases are how you tell fields apart
once you join two tables:
Filtering
.filter takes a boolean expression. The scalar operators are the same ones
you use in query filters:
Projecting
.select maps output field names to expressions. The output row contains only
the fields you select, plus _id and _creationTime from the key table:
_. A selected field that can be missing
in the input is optional in the output type.
Literal values keep their exact type. A bigint literal is sent as a 64-bit
integer, not a floating-point number.
Views over views
q.table can read another view by name. The outer view is keyed by the same
table as the inner one:
bijection/schema.ts
Work limits
A view’slimits bound the work of one evaluation: max_rows counts rows read,
and max_bytes counts the bytes of those rows. The default is 1,000 rows and
1 MiB:
bijection/schema.ts
ViewWorkLimit
error instead of returning a partial result. See
Reading views.
What Bijection checks
1
While you type
defineSchema checks that every q.table(...) in a view names a table or
view declared in the same schema. A typo fails to type-check with the
message view input table is not in this schema: <name>.2
When you deploy
bijection dev and
bijection deploy check the complete definition:
the key, field references and types, join indexes, grouping indexes,
limits, and that views don’t form a cycle. An invalid view fails the push
with a message naming the problem.3
When you read
Reads enforce the work limits and the one-row-per-key rule for joins.
Changing a view
You can change a view’s expression, limits and indexes and deploy again. Queries reading the view see the new definition from that deploy on.What views don’t support
A view expression is a declarative plan, not a function. These aren’t available inside an expression:- Arbitrary TypeScript callbacks or
ctx.dbreads. - Sorting, limits and top-N selection. Order results when you read the view, with an index.
DISTINCT, unions, ranking and window functions.- Joins on anything other than field equality, and right or full outer joins.
- Aggregates other than those listed under Grouping.
- Search, vector and staged indexes on the view itself.