> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bijection.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Composable collection queries

> Filter, traverse, combine and aggregate public collections with the generated workspace SDK.

The generated workspace client exposes `collection.query()` for public
collections. Query construction is synchronous and performs no network requests.
Each terminal call resolves the current public contracts and executes under your
business session.

Standalone consumer SDK packages expose the same query builders for their
selected public collections and relationships. Their locked contract includes
composition capabilities and remote identity assertions.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { createWorkspace } from "./bijection/_generated/client";

const workspace = createWorkspace({
  url: deploymentUrl,
  token: async () => businessSessionToken,
});

const customers = workspace.collections.customers.query();
const europeanCustomers = customers.where([
  { field: "region", operator: "eq", value: "Europe" },
]);
const openInvoices = europeanCustomers
  .related("invoices")
  .where([{ field: "status", operator: "eq", value: "open" }]);
const totals = openInvoices
  .groupBy((invoice) => ({ currency: invoice.currency }))
  .aggregate((m) => ({
    invoiceCount: m.count(),
    outstandingMinorUnits: m.sum((invoice) => invoice.outstandingMinorUnits),
  }));

const result = await totals.read({ signal });
console.log(result.rows, result.basis);
```

This example assumes a declared integer `outstandingMinorUnits` property.
Integer sums and counts remain `bigint`. Decimal sums use
`m.decimalSum(field, scale)` and return fixed-scale text. Measures also include
distinct count, minimum, maximum, average and exact discrete percentile, subject
to native type and execution admission. Missing values and explicit null retain
the engine's existing meanings. Presence predicates use the existing collection
meanings: `is_empty` matches a missing property, while an explicit null remains
present. Open lower range bounds exclude both null and missing values.

`related()` follows a public relationship alias and returns each reached target
object once, including across multiple starting objects. An invoice reached
through two customers contributes once to this total. Totals attributed
separately to each customer require an explicitly declared attribution rule.
Traversal queries current target objects; an unavailable source identity is not
a current object contribution. Point and relationship readers retain their
separate retained-identity contracts.

The generated relationship map supports repeated typed traversal. Set methods
accept sets from the same session and public collection:

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
const priority = customers.where([
  { field: "priority", operator: "eq", value: "high" },
]);
const either = europeanCustomers.union(priority);
const both = europeanCustomers.intersect(priority);
const europeanOnly = europeanCustomers.subtract(priority);

const page = await either.select("name", "region").page({ page_size: 20 });
```

Builders are immutable. Filters, projections, grouping keys and measures come
from the admitted contract. Projection and grouping produce result queries
without business-object navigation or identity. Use
`orderBy({ field, order: "asc" })` on the final result when its fields have an
admitted exact ordering.

## Bounded native reads

`read()` returns `{ rows, complete: true, basis, binding }`. It establishes the
whole composed result or refuses. The initial native contract allows 64
expression nodes, 16 expression levels, 1,000 shared native row steps, at most
1,000 output rows and 1 MiB of processed/output data. A smaller page does not
reduce the work needed to establish a total. Each filter step allows 16
predicates, and a group allows eight keys and 16 measures.

`page()` continues this complete bounded result. Its `next_cursor` binds the
exact query, contract and native basis; `end` is `page` or `complete`. Continue
on the same query object with `{ cursor, page_size }`. Pages re-execute at the
pinned basis; they do not retain a separate dataset. An expired basis requires a
new first page and reports `continuation_expired` with `restart_query` recovery.
Object continuations append identity keys to the selected order. Projected and
aggregate continuations use the shared complete-row order; ordering must be
admitted for every field the native sort needs.

The initial union lowering uses identity-based existence joins over the
collection's complete population. Its work and authorization requirements can
therefore exceed those of either filtered operand. The server refuses a
composition whose complete population, authority or work bounds cannot be
established. Denied evidence never becomes a zero aggregate or an empty
subtraction operand.

## Observation and diagnostics

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
const stop = totals.watch((observation) => {
  switch (observation.kind) {
    case "ready":
      showCurrentRows(observation.value.rows);
      break;
    case "loading":
    case "reconnecting":
      showPending();
      break;
    case "unavailable":
      clearProtectedRows();
      showReadFailure(observation.error);
      break;
  }
});

const contract = await totals.describe();
console.log(contract.validator_json, contract.execution, contract.limits);
const plan = await totals.explain();
console.log(plan.composition.relationships, plan.composition.observation);
stop();
```

Observation uses ordinary native dependencies, including relationship and
permission changes. It represents the latest checked state. Reconnection marks
the previous answer as noncurrent, and terminal refusal clears it. Ending
observation does not start or cancel retained work.

Discovery descriptions expose `composition`: admitted operators, predicates,
measures, limits, identity semantics and execution availability. A generated
type establishes field-level compatibility; `describe`, `explain` and execution
still admit the complete expression against the current schema. `ReadError`
distinguishes invalid selection, changed contracts, incompatible sets and
unsupported execution. Native engine refusals retain their native diagnostic
details. Public-expression errors also carry a `step` such as
`expression.right.input` and a `recovery` action. Native work-bound refusals
report `query_budget_exceeded`; a partial aggregate is never returned.

## Explicit retained and external execution

Remote collections expose query bindings without local point readers. Set
algebra over a remote collection requires an admitted key, whose uniqueness is
checked by the existing join assertion machinery. Unkeyed remote rows support
filtering, projection and aggregation. `read()` and `watch()` refuse remote
execution; start it explicitly through the existing retained answer owner:

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
const request = await totals.run({
  execution: admittedExecutor,
  read: { consistency: "observed" },
  max_rows: 1000,
  max_bytes: 1024 * 1024,
});
try {
  const answer = await request.result();
  const page = await answer.outputs.rows.page({ page_size: 50 });
  console.log(page.rows);
} finally {
  await request.close();
}
```

`admittedExecutor` is a public executor binding already configured for the
application. It contains no credentials. The existing planner determines whether
the inputs, operators, source contexts and requested consistency can execute
together. Fixed input requirements must be supported by those sources; an
observed answer does not assert a coordinated snapshot across providers.
Unsupported combinations refuse. Bijection continues to use external compute
providers.

The returned request owns observation, cancellation, recovery, result paging and
release through the existing retained lifecycle. Retained access still checks
current source authority and erasure. `run()` captures its options before
preparing the query, so later edits do not retarget accepted work. The native
input digest from preparation is included in the retained request and its
recovery journal. Admission and execution independently compare it with the
current logical inputs, including native collection identities, view membership
and authorization definitions.
A schema change between preparation and execution refuses the run instead of
silently changing its meaning.

For a Node service or CLI, configure the existing file-backed request journal so
accepted work can be recovered after a process restart:

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { fileRequests } from "bijection/node";

const workspace = createWorkspace({
  url: deploymentUrl,
  token: async () => businessSessionToken,
  requests: fileRequests("./request-journal", {
    deployment: deploymentUrl,
    namespace: requestNamespace,
  }),
});
```

Use the same directory and namespace when recovering that client's requests.
Native caller scope and current authorization still decide access.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.