Define a relation
A payment reads the open invoices in its block (the invoices with the same customer and currency). One relation shows the 10 newest as text, and a second counts all of them from the last 180 days and adds their amounts:
Each judgment has up to 4 relations, and each relation has
fields, aggregate or both. A relation reads the answers or outcomes of at most 4 judgments, and a document and every document related to it are in the same namespace.
The four joins
- The documents that point at the judged one:
{"theirs": "attributes.<name>", "mine": "id"}. A document belongs to the judged document whoseidis in itstheirsattribute, and the relation takeslast_n,windowor both. - The document the judged one points at:
{"theirs": "id", "mine": "attributes.<name>"}. See read the document the judged one points at. - The documents that share a key:
{"theirs": "attributes.<key>", "mine": "attributes.<key>"}, the same attribute on both sides. This blocking relation reads the candidates for matching, and it needswindowunless it setsrelative. - The documents a matching judgment linked: one side
id, the otheranswers.<j>.valueof a judgment that chooses among a blocking relation’s candidates, withwhennaming one of its thresholds. See read the matches from the invoice.
invalid_request.
Which documents a relation reads
- Newest created first, unless
orderchanges it. Brussle orders bycreated_at, solast_n: 8is the 8 most recently created. It applieswindowfirst, then the order, thenlast_n. - At most the newest 1,000. A relation reads at most the newest 1,000 matching documents, whatever its
last_n,windowandorder. Brussle applies amatchon answers or outcomes to each of those 1,000 beforelast_nkeeps the newest that qualify.
How window counts
The window counts back from the later of two times (when Brussle received the judged document’s newest write, and the newest creation among its related documents) and never from the clock, so an edit to an old related document never moves it. You can set a document’screated_at but not the time of a write, so after an import a window counts back from the import. Use last_n to read imported history. On a blocking relation, the window counts back from the newest document in the block. A relation that reads the document the judged one points at takes no window.
Count, sum and show the latest
aggregate computes numbers over the documents the relation selected, in the shape {"count": true, "count_where": {...}, "sum": [paths], "avg": [paths], "min": [paths], "max": [paths], "latest": [paths]}, with at most 8 paths in all.
countis the number of documents selected.labelled, on a relation that reads an outcome, counts those that have one.count_wherecounts the documents whose answers or newest outcome meet a condition. See count the matches of a matcher.sum,avg,minandmaxskip values that are not numbers. With nothing to work on,sumis 0, andavg,minandmaxare null.latestis the value in the most recently created document that has one, whatever the order. It can name an image (images.photo, shown aslatest(images.photo)), and an engine that reads images counts it like any other image.latest,minandmaxcan namecreated_atorupdated_at, which gives the time of the newest, oldest or most recent document as RFC 3339 text. The featureage(<relation>.latest(created_at))turns that time into days, measured from the judged document’s own write."render": falsekeeps the aggregates (all butlatest) out of what the engine reads. Features still read them, so Brussle records a change that moves only them with no engine call.
match. Brussle calculates aggregates before it cuts anything, and never cuts them.
What the engine sees
Each relation becomes onerelated.<name> entry in the compiled context, after the fields entries, in the order of the relations’ names:
recordsholds the selected documents in the relation’s order, keyed by path.- One key per aggregate, such as
countandsum(state.amount). capped: trueappears when the relation may be missing a matching document, and the evaluation then showsrelations_capped: true. A relation withoutlast_nis capped when it matched more than 1,000 documents, since the numbers then cover the newest 1,000. A relation that selects by an answer or outcome is capped when it found fewer thanlast_namong the newest 1,000 and more matched. A blocking relation is capped when it found more candidates in its block than itslast_n, so the right one may be unread.range_full: trueappears on a relation with anorderwhen more documents matched than the 1,000 it sorts.missing: trueappears on a relation that reads the document the judged one points at, when that document does not exist. See a missing referenced document.
What is cut first
Brussle removes content in this order when the context is overmax_tokens, until it fits, and records context_truncated: true:
- Whole images, on an engine that reads them: those of the relations, then of
previous, then offields, the last one first. Each becomes{"digest": "sha256:...", "omitted": "over_the_limit"}, and if the text alone is over, every image goes. - The records of the last relation by name. Brussle keeps them whole, in the relation’s order, while they fit, so its oldest records go first (or, with an
order, those that sort last). Brussle cuts the first record that does not fit instead of removing it, by shortening its longest text value, and the records after it keep at most half of the space left. One long record (a meeting transcript, say) therefore does not push out all the others. An image in a record is never cut. - The records of the relation before it, and so on.
- Any
previousentries. - The
fields, from the end of the last field.
max_tokens if the engine must read long records whole. Brussle bills each answer by the size class of the context it sent rather than by max_tokens, so only the answers that read long records cost more.
Read the document the judged one points at
A relation withjoin: {"theirs": "id", "mine": "attributes.<name>"} reads the one document whose id is in the judged document’s attribute, its referenced document (an order line reads its product, a message its conversation).
- It shows as any relation, with at most one record, and takes no
window. - With a
last_n, up to 254, it reads the documents that a named list of ids names. - It can show the referenced document’s answers.
- It is empty when the attribute is missing, is not a document id, or names a document outside
match. - A change to what it shows judges again the documents that point at it, inside the judgment’s re-judge scope, which
freshness.fanoutsets. See fan-out and judge a document with the document it points at for the cost and the controls.
Pick documents with order
A relation keeps its newest documents by default, whileorder keeps the documents that come first by a field you choose (the invoices due soonest, or the oldest open item):
byiscreated_at(the default),updated_at, astate.<path>or anattributes.<name>.descistrue(largest first) unless you set it.- Values sort by type first: numbers, then strings, then
trueandfalse, then missing values and everything else.descreverses the order inside each type, never the order of the types, so a missing value always comes last. - Inside a type, numbers compare by value, whole numbers exactly up to 64 bits. Strings compare by Unicode code point, so an ISO date such as
"2026-10-02"sorts as a date, andfalsecomes beforetrue. - Ties go to the most recently created document, then the lower
id. recordsshow in that order, and overmax_tokensthe records that sort last go first.latestis still the newest document’s value.- Where it works.
orderworks on a relation that reads the documents that point at the judged one, and on a named list, where it replaces the list order. Brussle refuses it on a blocking relation, whose candidates are always the newest, and on a relation that reads one referenced document.
range_full: true tells you this happened. Use a window or a narrower match to keep the documents that matter inside the newest 1,000. The create response warns you if a judged document already has more than 1,000 matching documents.
To sort, a relation with an order reads up to 1,000 documents for each judged document, where one without reads only its last_n. The price of an answer does not change, since Brussle bills judging by the context and not by what it reads. A judged document with many related documents does take a larger share of a pass of judging, so a catch-up over many of them takes longer. A relation whose match names an answer or an outcome reads the same 1,000 documents. The create response warns with the factor (a relation with last_n: 5 reads up to about 200 times as much).
An edit never moves a document into or out of the selection when the order is by created_at. With an order by another field it can. A write that changes state.due can move that invoice in or out, and Brussle judges again if what the engine sees changed. The create response warns about this, and about an order by a long text field, which sorts up to 1,000 long values for each judgment.
Show a number as a band
An entry in a relation’sfields can show a number as its band in place of the number, so a score of 0.42 shows as medium:
bandsare 1 to 9 cut points, each larger than the one before.labelshas exactly one more entry, each up to 64 bytes.- A number shows as one label, at the position given by the number of cut points at or below it, so 0.29 is
low, 0.3 ismedium, and 0.7 or more ishigh. - A value that is not a number shows unchanged, and a missing value is left out.
- Aggregates use the number before banding. On a judgment with features, that
pis the corrected one once a fit applies (see which number readers use).