Package-level declarations

Types

Link copied to clipboard
class BackendPage(val rows: List<ObjectNode>, val total: Long? = null, val positions: List<CursorPosition>? = null)

One window of records. total is present when the window asked for it; positions holds each row's native cursor position, in row order, for a PageWindow.Keyset.

Link copied to clipboard
abstract class CachedQueryBindings<K : Any>(nameOf: (K) -> AggregateType) : QueryBindings<K>

QueryBindings that create each key's binding once and reuse it, by the key's name (nameOf): a key that no longer equals the one its binding was created for (a dataset redeclared with another table or other columns) gets a binding of its own, and forget drops a name's binding. A key it does not serve is asked again each time.

Link copied to clipboard
class CursorPosition(values: List<Any?>)

A record's position in a cursor sort: one native value per sort field, in sort order, exactly as the storage orders them (BSON values for MongoDB, hit.sort() values for Elasticsearch). Only the backend that produced it interprets the values.

Link copied to clipboard

The backend half of a cursor token: native position values to bytes and back. decode throws on any payload it did not produce for a sort of size fields; the core reports every such failure as the invalid cursor error.

Link copied to clipboard
sealed interface GroupWindow

Which groups QueryBackend.aggregate returns.

Link copied to clipboard
sealed interface PageWindow

Which records QueryBackend.page returns.

Link copied to clipboard
class PositionedRecord(val record: ObjectNode, val position: String)

One record of a resumable stream and its position: the cursor token of the record's place in the stream's effective sort, the same kind of token a cursor query's nextCursor is (the native position, never a value of the record, with the model and sort's fingerprint). A stream resumed after position continues strictly after this record.

Link copied to clipboard

Aggregate-bound storage port: four primitives that only check natively, translate and execute.

Link copied to clipboard
class QueryBackendBinding<out B : QueryBackend> @InternalWowApi constructor(val backend: B, val storage: QueryStorageAdapter)

What a storage supplies for one aggregate type's read model: the backend that executes admitted queries and the storage adapter that reports its native facts. The QuerySchemaCatalog compiles those facts with the model's sources; the storage never builds the schema itself.

Link copied to clipboard
class QueryBackendStorages @JvmOverloads constructor(snapshots: QueryBindings<AggregateType>? = null, datasetBindings: QueryBindings<QueryDataset>? = null, datasets: QueryDatasets = QueryDatasets.NONE, events: QueryBindings<AggregateType>? = null) : QueryStorageLocator

The storages of the query backends, as the QuerySchemaCatalog locates them: each aggregate's snapshots from snapshots, its event streams from events, each dataset of datasets from datasetBindings; null for a model without bindings, a key they do not serve, a dataset that is not declared and any other model.

Link copied to clipboard
fun interface QueryBindings<in K : Any>

What a storage serves for each K: the QueryBackendBinding of each aggregate type's snapshots or events (QueryBindings<AggregateType>), or of each dataset (QueryBindings<QueryDataset>), null for a key it does not serve. A dataset whose storage binding it cannot read fails with an IllegalArgumentException: that is a misconfiguration, not a key it does not serve.

Link copied to clipboard

A query's projection over a whole record, for a backend that reads the record and projects it itself: what the includes keep (the whole record when there are none), less what the excludes remove. Each path is the field's segments in the record as the backend built it.

Functions

Link copied to clipboard
fun QueryBackend.aggregate(query: AdmittedQuery<AggregationQuery>, budget: QueryBudget? = null): Flow<ObjectNode>

The groups of query. The core plans the residual operators the storage declares RESIDUAL: it removes them from the query it sends down, asks for every group when an operator needs them all, and applies dense fill, windows, HAVING and top-N (or the limit) to the rows that come back. An aggregation without groups always has its summary row: when the backend emits none, because no record matched, the core emits the empty summary (EmptyAggregationValues). Such a query has no HAVING (the protocol requires a group for one, AggregationQuery rejects it at construction), so a storage's native HAVING can never be what removed a summary row; with groups, rows a HAVING removes are not replaced. When it reads every group, budget's QueryBudget.maxResidualGroups bounds how many it processes, dense fill rows included: HAVING can discard every fill row, so without counting them a sparse fine-grained dense histogram would generate rows until the timeout.

Link copied to clipboard
fun QueryBackend.chunks(query: AdmittedQuery<CursorQuery>, limit: Int? = null): Flow<PositionedRecord>

The records of query as a resumable stream: keyset pages of CursorQuery.size records, each its own QueryBackend.page call within the admitted timeout, one after the other from after the query's cursor token, until a page comes back short or limit records were emitted (null: no limit). Each record carries the token of its position.

Link copied to clipboard

The number of records matching query, bounded by its limit: QueryBackend.count, a storage failure reported as storageFault, a negative answer as a server fault. A count above the limit is the limit, not exact: "at least limit".

Link copied to clipboard
suspend fun QueryBackend.cursor(query: AdmittedQuery<CursorQuery>): CursorPage<ObjectNode>

One cursor page of query: page(Keyset) for one row more than the page, the look-ahead that decides whether a next page exists. The next token encodes the native position of the page's last row, never a value from the rows, so no value of a row can leak into it. A token that does not decode for this model and effective sort is rejected as Invalid cursor. before any I/O.

Link copied to clipboard
fun QueryBackend.list(query: AdmittedQuery<ListQuery>): Flow<ObjectNode>

The records of query, streamed: at most its limit, or all of them when the limit is 0.

Link copied to clipboard
suspend fun QueryBackend.paged(query: AdmittedQuery<PagedQuery>, totalLimit: Int? = null): PagedList<ObjectNode>

One page of query with its total as PagedQuery.total asks: page(Offset(offset, size)) with the exact total for TotalMode.EXACT, the total bounded by totalLimit for TotalMode.BOUNDED (exact when totalLimit is null), and none for TotalMode.NONE.

Link copied to clipboard
suspend fun QueryBackend.single(query: AdmittedQuery<SingleQuery>): ObjectNode?

The first record of query: page(Offset(0, 1, withTotal = false)).

Link copied to clipboard

The direction of the unique key's fields that end this sort so it orders records totally, in a list, a page and a cursor alike: the direction of the sort's last field, ascending for an empty sort. Records tied on a descending sort so come in descending key order, and a sort whose fields all run one way is one index order, forward or backward (k DESC, key DESC is an index (k, key) read backward). Every backend that ends a sort with its key asks this.