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:
- [
mode_detection] — request-shape validation + the versioned(select, group_by, having, order_by, limit)→DocumentHavingModeresolution, including the operator → inclusive-bounds translation. - [
execute_range] — the two executors onDriveDocumentHavingQuery(no-proof read, proof generation). - [
executors] — theimpl Drivewrappers the dispatcher calls. - [
drive_dispatcher] —DocumentHavingRequest/DocumentHavingResponseandcrate::drive::Drive::execute_document_having_request. - [
tests] (cfgserver+test) — unit + integration tests.
§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:
- The bound is part of the proof contract. Prover and verifier
build the same
BoundedaxisPathQueryfrom 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. - No
OFFSET, nostart_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 atlimitcan 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 thanMAX_HAVING_LIMITneeds a cursor on the(sort_key ‖ group_key)composite keyspace, a future capability; until then, sizelimitabove the widest tie the data can produce, or accept the cut. - Entry order is axis order in the walk direction. Ascending by
default (
ORDER BYis optional here — the bound, not the ordering, is the point of the query); an explicitORDER BYon 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/DocumentHavingResponseand the having-range dispatcher onimpl 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’sproveflag. - mode_
detection - Request-shape validation for the having-range query, and the versioned
(select, group_by, having, order_by, limit)→DocumentHavingModeresolution — including the operator → inclusive-bounds translation that turns aHAVING <agg> <op> <value>clause into anAxisRangeBounds.
Structs§
- Document
Having Mode - 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. - Drive
Document Having Query - A resolved having-range query. Shared by the prover and the verifier —
both build the grove path through
DriveDocumentHavingQuery::indexed_property_name_tree_pathand the secondary query throughAxisRangeBounds::merk_query, so the two cannot drift on which subtree or which range the proof is about.
Enums§
- Axis
Range Bounds - 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 assuper::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
DocumentHavingModeagainst a document type’s indexes into the executableDriveDocumentHavingQuery: 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 permittedIN), and assemble the query.