Skip to main content

Module drive_document_having_query

Module drive_document_having_query 

Source
Expand description

A query to filter an index’s groups by a per-group aggregate bound — “hashtags with more than 100 posts” — served as a value-bounded range read of the same per-axis secondary Merk the ranked surface walks (PR #657, PV14). Like ranked, it never opens the value trees, so a having-range read is O(log n + k) with a proof. Types and module structure for the boolean-HAVING range document query — SELECT <agg> GROUP BY <prop> HAVING <agg> <op> <value> [ORDER BY <agg> ASC|DESC] LIMIT n.

A having-range query answers “which groups’ aggregate falls inside a value bound?” (“hashtags with more than 100 posts”) in O(log n + k) with a proof, by range-reading the same per-axis secondary Merk the ranked query walks (grovedb PR #657): the secondary is keyed by (sort_key ‖ group_key) with an order-preserving sort-key encoding, so an inclusive numeric bound on the aggregate is a contiguous byte range in the secondary’s keyspace. The same contract opt-in applies — rankedCountable / rankedSummable / rankedAverageable (meta schema v3 / PV14) — and a HAVING on an axis the index does not declare is rejected, because serving it would mean walking every group.

The implementation mirrors [super::drive_document_ranked_query] sibling-for-sibling and reuses its axis / entry / pagination types (RankedAxis, RankedEntry, RankedEntryValue, [super::RankedPaginationInputs]) and its covering-index picker — both surfaces read the same tree, so sharing the resolution logic is what keeps them provably about the same subtree:

§What makes this query shape different from ranked

Ranked addresses groups by rank position (k best, starting at rank offset); having-range addresses them by value bound (aggregate ∈ [lo, hi]). Three consequences:

  1. The bound is part of the proof contract. Prover and verifier build the same Bounded axis PathQuery from the request’s inclusive bounds (AxisRangeBounds::inclusive_bounds_i128), and grovedb re-executes the proof against that traversal — so the two sides share one bounds-to-query translation, exactly as they share the grove path. Completeness comes from the Merk range proof: the boundary commitments show no in-range group was omitted.
  2. No OFFSET, no start_at — and no full pagination. The range primitives take a limit but no skip, and a request carrying either knob is rejected loudly. A page cut at limit can only be continued past distinct aggregate values, by tightening the bound past the last value seen; a cut that lands inside a tie (several groups sharing the boundary aggregate) cannot be continued at all — moving the threshold past the tied value skips the uncollected tied groups, and keeping it returns the same page. Enumerating through a tie wider than MAX_HAVING_LIMIT needs a cursor on the (sort_key ‖ group_key) composite keyspace, a future capability; until then, size limit above the widest tie the data can produce, or accept the cut.
  3. Entry order is axis order in the walk direction. Ascending by default (ORDER BY is optional here — the bound, not the ordering, is the point of the query); an explicit ORDER BY on the selected aggregate flips the walk. Ties break by group key in the direction of the walk, same as ranked.

Re-exports§

pub use drive_dispatcher::DocumentHavingRequest;
pub use drive_dispatcher::DocumentHavingResponse;

Modules§

drive_dispatcher
DocumentHavingRequest / DocumentHavingResponse and the having-range dispatcher on impl Drive — the ABI drive-abci’s routing layer names.
execute_range
The two having-range executors on DriveDocumentHavingQuery: a direct value-bounded read of the axis secondary, and generation of the equivalent proof.
executors
Per-mode having-range executors on impl Drive. The dispatcher (super::drive_dispatcher) picks between the two executors on the request’s prove flag.
mode_detection
Request-shape validation for the having-range query, and the versioned (select, group_by, having, order_by, limit)DocumentHavingMode resolution — including the operator → inclusive-bounds translation that turns a HAVING <agg> <op> <value> clause into an AxisRangeBounds.

Structs§

DocumentHavingMode
The resolved shape of a having-range request: the bounds (which carry the axis), the walk direction, the limit, and the (group property, aggregate field) pair the index picker needs.
DriveDocumentHavingQuery
A resolved having-range query. Shared by the prover and the verifier — both build the grove path through DriveDocumentHavingQuery::indexed_property_name_tree_path and the secondary query through AxisRangeBounds::merk_query, so the two cannot drift on which subtree or which range the proof is about.

Enums§

AxisRangeBounds
Inclusive numeric bounds on one axis of an indexed tree — the resolved form of a HAVING <aggregate> <operator> <value> clause.

Constants§

MAX_HAVING_LIMIT
Hard ceiling on a having-range request’s LIMIT. Same value and same rationale as super::drive_document_ranked_query::MAX_RANKED_LIMIT: the proof commits one secondary entry per returned group, so proof bytes grow linearly in the limit, and the ceiling is a hard rejection rather than a clamp because the limit is part of the traversal the verifier re-executes: a server-side clamp would truncate the walk and fail coverage under the client’s own reconstruction.

Functions§

resolve_having_query_for_mode
Resolve a validated DocumentHavingMode against a document type’s indexes into the executable DriveDocumentHavingQuery: pick the covering index (shared with the ranked surface — both read the same indexed tree), encode the prefix pins into prefix branches (one branch for all-== pins, one branch per element of the single permitted IN), and assemble the query.