Query, Mutation, Analytics, and Facets
This is the common entry point for application data operations in TeaQL. Start here to choose the operation shape, then open the language page for exact generated syntax.
| Business question | TeaQL operation | Result shape |
|---|---|---|
| Which objects match? | Typed Query with predicates, projection, relation selection, order, and pagination | SmartList<Entity> or one typed entity |
| What are the totals? | Aggregate-only Query | Named aggregate metadata/result values |
| How are rows distributed? | Group by plus named aggregates | One result row per group |
| What statistics belong to each parent? | Relation count/statistic | Named values attached to each parent row |
| Which filter choices and counts should the UI show? | One or more relation Facets | Named SmartList<Record> sidecars on the main SmartList |
| How do I create, update, or delete? | Generated Mutation methods plus audited graph save | Authoritative saved entity and version |
Query: select the business rows
A Query starts from generated Q, composes only modeled fields and relations,
declares non-empty comment and purpose, and executes with trusted context.
Q.<entities>()
-> typed predicates
-> projection and relation selection
-> deterministic order and bound
-> comment + purpose
-> execute with context
The two intent calls may appear in either order and need not be adjacent. They
describe the operation; they do not grant authorization. Tenant, actor,
permission, provider, and safety limits come from context.
See the cross-language Query contract for predicate, relation, pagination, and evidence details.
Aggregation: calculate over one filtered population
TeaQL has three related analytics shapes:
- Aggregate-only:
count,sum,avg,min, ormaxover the complete filtered population. - Grouped aggregate: add one or more generated
groupBy...calls, then named aggregate aliases. Each group produces a result row. - Relation aggregate: decorate every parent with a count or statistic over a typed, optionally filtered child Request.
Aliases such as schoolCount and capacityTotal are part of the result
contract. They are analytic values, not modeled writable fields. Keep filters
inside the request being aggregated; do not load all entities and recalculate
database totals in application memory.
The portable seven-runtime aggregate family is:
| Function | Typical generated spelling |
|---|---|
| Count | countAs("schoolCount") |
| Sum | sumStudentCapacityAs("capacityTotal") |
| Average | avgStudentCapacityAs("capacityAverage") |
| Minimum | minStudentCapacityAs("capacityMinimum") |
| Maximum | maxStudentCapacityAs("capacityMaximum") |
Casing follows the target language. Some runtimes expose additional functions, but variance, deviation, and bitwise aggregates are not part of the portable seven-language baseline.
Facet: return navigation buckets beside the rows
A Facet is not just a group-by. It combines two Requests:
outer School request
filter: name contains "Primary"
main result: matching School rows
facet "schoolTypes" on School.schoolType
nested SchoolType request
projection: code
metric: count matching Schools as "schoolCount"
result: named bucket list attached to the outer SmartList
The nested Request decides which bucket fields and metrics are returned. The outer Request decides the active business population. Multiple Facets can be attached to one outer Request under different names.
Facet syntax variants
| Variant | Request shape | Effect |
|---|---|---|
| Default include-all | facetByRelationAs(name, child) | All allowed child values; count metrics may be zero |
| Explicit include-all | facetByRelationAs(name, child, true) | Same semantics, useful when the choice should be visible in code |
| Matched-only | facetByRelationAs(name, child, false) | Remove buckets absent from the filtered outer population |
| Restricted bucket domain | Put a typed predicate such as withCodeIn(...) on child | Consider only the allowed subset of relation values |
| Rich bucket projection | Add selectCode, selectName, or other generated selections to child | Return display-ready bucket fields |
| Multiple metrics | Add count...As, sum...As, or other relation statistics to child | Return several named values per bucket |
| Multiple Facets | Chain two or more differently named facetBy...As calls | Return independent filter panels beside the same main rows |
The child predicate and the include-all flag solve different problems. A child predicate defines which bucket values are allowed at all. Include-all decides whether allowed-but-unmatched values remain visible with zero counts.
Include-all versus matched-only
| Option | Meaning | Example use |
|---|---|---|
includeAllFacets = true (default) | Return every allowed relation value; unmatched buckets carry zero for count metrics | Stable filter panels that must keep disabled/zero-count choices visible |
includeAllFacets = false | Return only relation values present in the filtered outer population | Compact drill-down lists and search suggestions |
For a Primary school filter, include-all might return PRIMARY: 1 and
SECONDARY: 0; matched-only returns only PRIMARY: 1. The Facet result is
read by the name supplied at construction time:
| Language | Construct a SchoolType facet | Read it from the result |
|---|---|---|
| Java | facetBySchoolTypeAs("types", child, true) | rows.getFacet("types") |
| Rust | facet_by_school_type_as_with_options("types", child, true) | rows.facet("types") |
| TypeScript | facetBySchoolTypeAs("types", child, true) | rows.facet("types") |
| Swift | facetBySchoolTypeAs("types", child, includeAllFacets: true) | rows.facet("types") |
| Python | facet_by_school_type_as("types", child, include_all_facets=True) | rows.facet("types") |
| C#/.NET | FacetBySchoolTypeAs("types", child, includeAllFacets: true) | rows.Facet("types") |
| Go | FacetBySchoolTypeAs("types", child, true) | rows.Facet("types") |
Use the same normalized outer filter for page rows, totals, aggregates, and Facets when they are meant to describe one screen. A Facet request does not bypass context policy or authorization.
Mutation: change a graph at one audited boundary
Create, update, and deletion use one lifecycle:
- Create from a generated Request carrying both
commentandpurpose, or query the current entity before updating. - Change fields and relationships through generated methods.
- Use
markForDeletion/language equivalent to record deletion intent. - Attach a non-empty audit reason.
- Save the graph with trusted
contextand retain the returned entity.
Checker and Fix run before provider mutation. Optimistic version checks reject
stale updates. Deletion intent is committed by the same audited save boundary;
it is not a separate unaudited delete channel. See the
cross-language Mutation lifecycle for the graph,
validation, audit, and retry semantics.
Language examples
The scenario page for each language introduces the individual operations. Its advanced page then composes them into a search screen, a relation graph with per-parent Top-N, analytics, Facets, and an audited read-modify-save workflow:
Names such as School, SchoolType, and studentCapacity come from the
audited School Management fixture. Your model generates different names. Use
current Assist or generated Request source to obtain the exact methods; do not
translate a method name from another language by hand.