Ask once; HeavenBase will route the question, but it still expects you to open the answer.
1. Motivation
Application code should describe data intent without choosing provider clients or merge algorithms. HeavenBase queries are immutable request values that a workspace resolves against its Entity schema, placements, Backends, handlers, and Context policy. Construction performs no I/O. Onlyexecute() and explain() cross the routing boundary.
2. Build an Immutable Query
hb.Query; there is no mutable QueryBuilder.
Branch with ordinary assignment, as base and nearest do above.
to_spec() returns the structurally immutable request consumed by routing.
It does not choose a Backend or perform I/O.
3. Read a ResultFrame
Everyexecute() recomputes and returns a newly detached hb.ResultFrame.
Results are not cached or persisted automatically.
rows(), scalar(), to_pandas(), to_pyarrow(), to_numpy(), to_pydantic(), or to_daft().
Frame transforms return new frames, and accessors return caller-owned values.
object_id remains available after projection so a compact result can still drive later get, set, or delete calls.
Pagination is explicit. frame.total is the exact row count before the final offset and limit, and frame.truncated says whether pagination withheld rows. An unsliced frame uses total=None because len(frame) already describes the whole result. When a preceding near(...) stage is bounded by top_k, the total describes that nearest-neighbor window rather than every Entity row.
4. Filter and Search
Field references build typed expressions and combine with&, |, and ~.
contained_in(...) performs reverse containment: it asks which stored short values occur inside a longer query string.
On Array[ShortText], SQL Backends use SparseGramIndex when available and preserve matching keyword evidence in the reserved match metadata column.
Text operations may use a portable scan when provider-native semantics cannot be proven.
Inspect the plan before trading portability for speed with query-scoped .unsafe().
5. Accept JSON Queries
query_json validates a serializable request and lowers it to the same immutable query value.
filter, available, near, traverse, select, group_by, aggregate, having, order, offset, limit, and unsafe.
Unknown or obsolete keys fail instead of being ignored.
where() also accepts Mongo-style filter mappings with registered operators such as $eq, $in, $match, $contained_in, and $array_contains.
6. Count and Aggregate
count() is a source-only terminal query specification.
Only source filters, search, traversal, and unsafe policy may precede it.
having(...) after aggregate(...) and order_by(...) for result ordering.
Routing may execute a provider-native aggregate or an exact portable fold; explain() tells you which.
Json fields also expose registered exact folds such as value_counts(), key_counts(), and key_sums(). Registered reducers inspect the complete post-filter value stream rather than deriving statistics from a displayed page.
code_reduce(...) is the escape hatch for a genuine one-off reduction:
7. Inspect the Plan
8. Keep Writes Outside Query
Queries read. Workspace CRUD owns mutation:entity=... when an object id may exist under more than one Entity type.
Summary
- Query values are immutable and lazy.
- Every execution returns a fresh detached ResultFrame.
totalandtruncateddistinguish a complete result from a page.- Python and JSON surfaces share one validated request model.
- Registered reducers cover reusable exact folds; caller-authored
code_reduceis an explicit trust boundary. - Explain output is the authority for actual routing behavior.

