# Query and Aggregation Guide Design §17–§19, decision D-11. Every query and every pipeline is a registered, bounded thing. Free-form JSON queries and unbounded pipelines are explicitly unsupported (§3.4). ## 1. Registered operations Every execution carries a `MongoOperationContext`: a `MongoOperationName`, a `DatabaseProfileName`, a `CollectionProfileName`, a `MongoOperationType` and a `MongoOperationScope`. `MongoOperationName` matches `[a-z][a-z0-9.-]{2,95}`. It is the join key for the budget registry, the consistency registry, the metric tag and the log line — a free-form or interpolated name breaks all four at once, which is why the pattern is enforced at construction. `MongoOperationScope` uses an `UNSPECIFIED` sentinel rather than `null`, so "the caller did not say" is a value the policy layer can reject rather than an NPE further down. ## 2. Query guardrails `PolicyAwareMongoQueryBuilder` builds a query from `MongoFieldDescriptor` + `MongoOperator` pairs against a `MongoQueryPolicy`. The policy refuses: - a field not in the collection's allowlist - an operator not allowed for that field - a sort on an unindexed field - `$where`, `$expr` with arbitrary JavaScript, and server-side evaluation generally - an unbounded `$regex` `MongoRegexPolicy` requires an anchored prefix pattern and bounds the pattern length. An unanchored regex is a collection scan wearing an index's clothes, and a user-supplied one is a denial-of-service primitive. `MongoSortDescriptor` pairs a field with a direction and is validated against the index manifest, so a sort that would spill to disk fails review rather than production. ## 3. Operation budgets `MongoOperationBudget` bounds four things at once: | Bound | Why | |---|---| | `maxTimeMS` | The server stops working on a query nobody is waiting for. | | result limit | An unbounded result set is an OOM with extra steps. | | batch size | Bounds the per-round-trip memory. | | examined-document ceiling | Catches an index regression that a time limit alone would hide on a fast day. | `MongoBudgetPolicyRegistry` binds a budget to an operation name; `MongoBudgetEnforcer` applies it and raises `MongoOperationRejectedException` before execution when a request exceeds it, and `MongoTimeoutException` when the server enforces it. ## 4. Keyset pagination Unbounded `skip` is unsupported: `skip(1_000_000)` makes the server walk a million documents to throw them away, so page 1000 costs a thousand times page 1. `MongoKeysetQueryBuilder` builds the resume predicate lexicographically. For a sort on `(a DESC, _id DESC)` resuming after `(A, I)`: ``` (a < A) OR (a = A AND _id < I) ``` `validate()` rejects a `MongoKeysetSort` without a unique tie-breaker. Without one, two documents with the same sort value straddle the page boundary and one of them is skipped or repeated — invisibly, and only under concurrency. `MongoNullSortOrdering` makes null placement explicit, because MongoDB's own ordering of missing versus null versus present is not what most people assume. ### Cursors are authenticated `MongoKeysetCursorCodec` signs the cursor with HMAC-SHA256 and compares with `MessageDigest.isEqual` (constant time). An unsigned cursor is a client-controlled query predicate: a caller can edit it to read a range they were never offered. A tampered or truncated cursor yields `MongoCursorException`, never a partially-decoded resume position. ## 5. Aggregation guardrails `MongoAggregationPlan` is a registered pipeline: an ordered list of `MongoAggregationStageDescriptor` validated against a `MongoAggregationProfile`. `PolicyAwareMongoAggregationExecutor` runs only a registered plan. `MongoAggregationRisk` grades each stage, and the profile sets the ceiling: | Risk | Stages | Policy | |---|---|---| | low | `$match` on an indexed prefix, `$limit`, `$project` | Always allowed. | | moderate | `$group`, `$sort` with an index, `$unwind` with a bound | Allowed within budget. | | high | `$lookup`, `$graphLookup`, `$facet`, unindexed `$sort` | Requires explicit approval in the profile. | | forbidden | `$out`, `$merge` outside the admin plane, `$function`, `$accumulator` | Refused. | `allowDiskUse` is a declared property of the plan, not a runtime flag. A pipeline that needs disk is a pipeline whose shape should be reviewed. ## 6. Reactive execution and cursors `ReactiveMongoExecutor` / `DefaultReactiveMongoExecutor` carry the operation context in the Reactor context. `MongoCursorGuard` and `MongoCursorLease` bound cursor lifetime: - a cursor has a lease with a deadline - cancellation closes the server-side cursor (`MongoCursorTermination`) - an abandoned cursor is a server-side resource, so the lease is released on cancel, error *and* completion — `MongoReactiveCursorPublisher` uses `Flux.using` so all three paths run the same release A leaked cursor does not fail anything locally; it consumes a connection and a snapshot on the server until the server's own timeout, which is why the guard is not optional. ## 7. Geospatial `MongoGeoQuery` + `MongoGeoPoint` + `MongoGeoDistance` over a `2dsphere` index. Distances are metres on a sphere (`nearSphere` with `maxDistance`), never degrees — a degree of longitude is a different distance in Oslo than in Nairobi, and a radius expressed in degrees is a bug that only shows up away from the equator. `SpringMongoGeospatialOperations` is the Spring Data binding; `MongoGeospatialOperations` is the port. ## 8. Native capability gateway When a registered operation genuinely needs something outside the Stable API, it goes through `MongoNativeCapabilityGateway` (`PolicyAwareMongoNativeGateway`), never through the driver directly. The admission order is fixed: ``` capability registered → database profile → collection allowlist → operation name present → timeout / maxTimeMS → consistency profile → result / batch limit → trace → log redaction → command category (MongoNativeCommandCategory) → D4 admin command refused → execute ``` `ApprovedMongoNativeOperation` is the registration record; `MongoNativeOperationPolicy` is the policy. An admin-plane command reaching this gateway is refused regardless of capability — the admin plane has its own credential and its own client (see [security-observability.md](security-observability.md)).