MAP Descriptor-Aware Query Engine Design Specification¶
Status: normative execution semantics for the storage-grounded query model.
This specification describes the target design for collection-based query execution, including shared scalar predicates and filtering. Delivery stages, implementation status, and transitional limitations belong to the implementation plan.
This specification defines how MAP executes a saved query definition. The
independently loadable Query Schema is
map-holons/schema-src/query/schema.tdl; its Dance adapter is
map-holons/schema-src/query-dance/schema.tdl.
The query schema design companion and
adapter design companion prescribe their
respective declarations. The
storage-grounded query architecture
defines query-tree topology and the boundary to the storage algebra. The
storage-layer design specification
is authoritative for SmartLink encoding and storage operations.
Purpose¶
MAP query execution is descriptor-aware graph navigation over holons. It is not a separate property-graph engine, and it does not make a row stream its default runtime carrier.
The initial operand algebra is deliberately narrow:
HolonCollectionReference -> QueryExpression -> HolonCollectionReference
HolonReference and HolonCollectionReference preserve identity and deferred
access. Materialized scalar, projection, path, correlation, and row-like values
are introduced only where a concrete expression or compatibility surface
requires them.
Normative Execution Model¶
The Query engine executes a reusable Query directly and has no dependency on
Dance. A direct caller supplies a focal HolonSpace invocation context,
optional initial HolonCollectionReference, and bindings without importing
Dance types. QueryDance is a separate descriptor-afforded adapter: it adapts
the Dance's AffordingHolon into the same focal-space context and forwards an
optional collection reference through the Dance layer.
QueryCore is the internal direct-execution submodule of map-query-schema. It
owns the execution contract and lifecycle beneath the Query entry point; it
is not a separately loadable schema package or a generic direct-caller API.
Extract it only if another independently loadable consumer needs that contract
without Query definitions, or it needs independent versioning.
For every invocation, the engine creates one ExecutionInstance. It creates a
QueryExpressionExecution for each expression invocation. Runtime input,
output, status, and resolved bindings belong exclusively to these execution
holons; they must not be written onto the reusable query definition.
Query
RootExpression -> QueryExpression
ExecutionInstance
ExecutesQuery -> Query
FocalSpace -> HolonSpace
ExpressionExecutions -> QueryExpressionExecution*
ExecutionResult -> HolonCollection?
ExecutionInstance.FocalSpace is transient per-invocation context, required
exactly once, and is not saved on Query or a QueryExpression. It is local
to SeedHolons scope in this version; it is not the future multi-space
ExecutionDomain.
A root expression is either source-producing or operand-consuming. A source
root receives no collection input; an operand-consuming root receives the
caller-supplied HolonCollectionReference. Each successful expression
execution supplies its result to the next expression in the chain. The result
of the root chain becomes ExecutionResult. When the caller used the Dance
adapter, it also becomes QueryDanceResponse.ResponseBody.
Expression Semantics¶
Every executable operator is a concrete holon type extending QueryExpression.
Ordinary MAP typing classifies an expression; no separate expression-kind or
expression-type relationship exists.
Next is the only declared sequential-continuation relationship. Previous
is its inverse. QuerySubTree.Subtree is implementation containment, not
continuation. A query tree is consequently a hierarchy of linear chains, not a
general DAG. Cycles and shared expression nodes are invalid.
A composite expression receives a collection, executes its contained subtree,
applies the merge or selection semantics of its concrete type to the subtree
exits, then continues at its own Next expression. Child expressions must not
reference the parent continuation.
Descriptor Ownership¶
Descriptors, rather than query-specific metadata, own query meaning:
- holon-type descriptors determine the structural surface available to an input holon;
- relationship descriptors determine legal channels, endpoint roles, and inverse names;
- property and value descriptors determine projection, comparison, ordering, and predicate legality;
- concrete expression descriptors determine their parameter contracts and execution behavior.
An engine must reject an expression whose descriptor-backed contract cannot be satisfied. It must not silently omit invalid input members or treat invalid structure as an empty result.
Name selection and execution-time descriptor resolution¶
Holonic query expressions select properties and relationship channels by name. The expression definition represents the query before schema binding. During execution, each expression's Rust implementation resolves its selectors against the actual input holon's effective type surface through shared descriptor facilities, then uses the resolved descriptors to validate and execute the operation. A textual front end, including a future GQL or OpenCypher front end, can construct the same holonic representation as direct holonic authoring without first binding names to schema descriptor identities.
Name resolution is descriptor-backed: it does not read arbitrary stored fields or bypass declaration, requiredness, value-type, operator, or traversal checks. Duplicate property names within a holon type's effective property surface are rejected during type-definition validation. Query expressions rely on that invariant and propagate descriptor-resolution errors; they do not introduce a parallel duplicate-declaration validator or choose arbitrarily among matches. An undeclared name is an error, distinct from an absent optional value.
Independently valid holon types may declare the same property name using
different PropertyType descriptors. Selector resolution accepts those distinct
property identities; each operation still enforces its own compatibility rules.
For example, Book and Film may each declare a distinct Title PropertyType.
An OrderBy selector Title applies to both when their resolved value descriptors
satisfy the ordering compatibility contract. Neither type contains duplicate
property names. Shared primitive representations alone do not establish
semantic comparison compatibility.
This policy applies to Expand relationship selectors, OrderBy property selectors, ComparisonPredicate property selectors, and future Project definitions. Filter must resolve selected operands before validating applicable operators; Project must resolve selected properties before materializing their values and type information. Predicate compatibility and missing-value semantics are defined below; Project's concrete shape remains separately defined. Skip and Limit are positional transformations and perform no member-property selection.
Resolution belongs to the execution in its transaction context. It must not replace names with descriptor references in caller-owned query definitions. Shared descriptor facilities own lookup and inheritance semantics rather than individual expressions implementing competing resolution rules. Names reduce coupling to particular declaration versions, but do not guarantee that every schema revision remains semantically compatible.
Selector resolution is distinct from invocation parameter binding and from query persistence. Constructing a name-based query does not require a parameter binder, and saving a definition does not by itself specify when its selectors are bound. Saved forms, any bound form, and replay/version guarantees require separate contracts; execution-time resolution does not settle those choices.
Initial Expression Set¶
The first expression family includes:
SeedHolons, which establishes the focal-space-owned initial collection;Expand, which follows one named relationship channel;Filter, which retains collection members satisfying a shared Predicate;OrderBy,Distinct,Skip, andLimit, which transform a collection;Project, which is the explicit materialization boundary.
These names identify expression semantics. Concrete expression types declare
their parameter contracts in the schema. SeedHolons.SeedPredicate and
Expand.ExpansionPredicate are optional relationships to QueryPredicate.
They carry expression definition state, not collection operands.
SeedHolons¶
SeedHolons is root-only. It declares no parameters and consumes no collection
operand, but may carry an optional SeedPredicate definition payload. It
derives its source from the current execution's required focal space and
establishes its unfiltered candidates by:
Expand(ExecutionInstance.FocalSpace, Owns)
It is not GetAll, global enumeration, all-relationship expansion, or an
ambient query-definition context. A supplied collection input for a
SeedHolons root is a validation error. The resulting transient collection
preserves the Owns traversal order and duplicate occurrences.
When SeedPredicate is present, the shared predicate evaluator filters those
candidates under the same semantics as a subsequent Filter expression.
Expand¶
As a root, Expand is operand-consuming and requires exactly one
HolonCollectionReference supplied by the caller. As a non-root expression,
it consumes its predecessor result. Independently, it may carry an optional
ExpansionPredicate definition payload. It never treats a missing relationship
name as an instruction to expand all relationships.
For each source member, Expand obtains its HolonDescriptor and calls
HolonDescriptor::resolve_available_relationship(requested_name). That existing helper
resolves one declared-or-inverse outbound relationship or returns its ordinary
HolonError; Expand propagates that result and uses the returned relationship
for traversal. It does not preflight the whole collection, construct a separate
effective-descriptor model, or add query-specific relationship validation.
If a requested traversal is legal but has no targets, Expand returns an empty
collection for that input. If any input member cannot legally traverse the
requested channel, the expression fails validation. Successful expansion
preserves storage traversal order and duplicate occurrences. Explicit
Distinct and OrderBy expressions own deduplication and reordering.
When ExpansionPredicate is present, it evaluates against the expanded target
holons, not the source holons. It has the same meaning as filtering the expanded
collection with that Predicate. It does not filter relationship descriptors or
SmartLink payloads as though they were candidate holons.
Shared Predicate model and schema contract¶
Predicate semantics are standardized; Predicate authoring experiences are plural. Query, Space Navigator, agents, and saved definitions use the same representation, validation, and evaluation. A consumer may expose a restricted subset without creating its own predicate language or comparison semantics.
QueryPredicate remains the existing abstract schema anchor for the shared
Predicate model. Its name does not give Query exclusive ownership of Predicate
semantics. The concrete forms are:
QueryPredicate (abstract)
ComparisonPredicate
Not
AllOf
AnyOf
Ordinary MAP typing identifies each concrete schema form. A Predicate
is definition state and evaluates against one candidate to produce a Boolean;
it is neither a collection operand nor a QueryExpression continuation.
ComparisonPredicate: one filter specification¶
A single filter specification is a ComparisonPredicate, not a second
FilterSpec representation. Its declared semantic members are:
| Member | Contract |
|---|---|
PropertyName |
One required string-valued property selector, using the existing name-selection contract. No path syntax, computed expression, or Search-specific selector. |
Operator |
Exactly one relationship to an OperatorType descriptor. The selected descriptor must be effectively afforded by the resolved property's ValueType. |
Operands |
An ordered relationship to authored operand-value holons. Each carries a typed literal value; its position corresponds to the operator's authored-operand specification. Zero, one, or multiple operands are permitted as required by that operator. |
The operand-value envelope uses MAP value representations and ValueType
validation; it is not an untyped string bag or a consumer-specific JSON AST.
The selected property's value supplies the subject operand. Authored values
remain definition state, not QueryParameterBinding instances. Invocation
parameter binding is a separate capability.
For example, the semantic content of Age GreaterThan 30 is property selector
Age, the afforded GreaterThan operator descriptor, and one authored integer
operand. Examples name operators by their semantic labels.
Selectors remain names in reusable definitions. An authoring context may supply
a PropertyDescriptor to guide construction, but the predicate retains the
property selector and execution resolves it against each candidate. There is
no separate key-only predicate syntax: selecting the canonical Key property
uses the same representation as selecting another scalar property.
Recursive logical composition¶
Not declares exactly one Predicate child relationship to QueryPredicate.
AllOf and AnyOf each declare an ordered Predicates relationship to
QueryPredicate, with one or more children. Children may themselves be leaves
or composites. Empty groups, missing children, and cycles are invalid; the
absence of a filter is represented by omitting the filter, not an empty group.
Notreturns the Boolean negation of its child.AllOfreturns true exactly when every child returns true.AnyOfreturns true exactly when at least one child returns true.
For example:
AllOf(
GreaterThan(Age, 30),
AnyOf(Equals(Status, Active), Equals(Status, Pending))
)
Composition scope is explicit regardless of the author's presentation. Predicate
child relationships are not Next; Next remains collection-expression
continuation. Shared validation rejects malformed or unsupported branches even
when another branch's Boolean value would determine the outcome. An evaluator
may short-circuit Boolean combination only after required validation and value
access checks; it must not use short-circuiting to hide an invalid candidate,
unsupported operator, or unreadable property.
Operator and authored-operand contracts¶
Operators belong to ValueTypes. Predicate execution and authoring reuse
OperatorType, MetaOperatorType, Arity, OperatorCategory, inherited
AffordsOperator, and the shared effective-operator lookup. They must not
introduce a filtering-specific operator registry or comparison implementation.
Execution arity and authored operand count are different. Equals has two input
values but one authored operand when the property supplies its subject;
Between has a subject plus two authored bounds. An authorable operator exposes
an ordered set of operand specifications, each identifying its semantic role,
required ValueType, and human-facing label where needed. The contract must
resolve those ValueTypes for the selected subject ValueType; inherited generic
operators must not lose the subject's semantic type constraints. Controls must
not be inferred from Arity alone.
Arity and OperatorCategory metadata alone do not fulfill this
authored-operand contract. The operand-specification relationship and value
envelope require concrete declarations in the Query/Operator schema companions;
their storage details must preserve the semantic members and ordering above.
Operator categories remain extensible beyond equality and ordering.
Operator descriptors provide human-facing labels/descriptions and sufficient operand metadata for generic authoring. ValueTypes own value validation and value-authoring semantics. An editor discovers:
Property -> ValueType -> effective AffordsOperator
-> authored-operand specifications -> ValueType-driven value authoring
The shared invocation contract accommodates a subject value and an ordered
operand sequence, conceptually apply_operator(operator, subject, operands[]).
The exact Rust API is an implementation choice; its semantic model must support
zero, one, and multiple authored operands rather than permanently assuming
lhs, rhs. Operator-specific constraints belong to the operator/value layer.
Security-aware operator availability and array predicate semantics are outside
this contract.
String matching operators¶
The string ValueType family affords three distinct matching operators through
the ordinary inherited AffordsOperator relationship. Each has a string subject,
one authored string operand, and a Boolean result:
| Operator | Authored operand | Meaning |
|---|---|---|
Contains |
Literal text | The subject contains the supplied text as a literal substring. Pattern metacharacters have no special meaning. |
Like |
Wildcard pattern | The subject matches a wildcard pattern under the operator's declared wildcard and escaping rules. This is not regex syntax. |
MatchesRegex |
Regular expression | The subject matches a regular expression under the operator's declared regex dialect, flags, and match-scope rules. |
These are distinct string-family affordances. The detailed pattern contracts remain open design decisions: Like requires a wildcard alphabet, escaping, and whole-value versus partial-match rules; MatchesRegex requires a dialect, flags, match scope, and invalid-pattern/resource-limit error behavior. No caller may infer those rules from its local programming language.
The operators may share execution machinery, but their semantic contracts remain distinct. A regex-backed Contains implementation must treat its operand as literal text and preserve containment semantics; it must not interpolate raw input into a pattern or expose regex behavior to its caller. Unsupported Like or MatchesRegex execution fails explicitly rather than falling back to Contains.
Case handling belongs to the generic operator/value matching policy, not Query or an authoring experience. The contract must support case-insensitive containment and identify the effective policy reproducibly; it must not depend on the executing browser's locale or change existing Equals/LessThan semantics. The policy's schema placement and exact Unicode case-folding/normalization rules remain an explicit ValueType/operator design decision. They must be defined before case-insensitive evaluation is considered specified completely. This is not a Search-specific property, operand, or invocation shape. Like and MatchesRegex must declare how their pattern rules interact with this policy; a case-insensitive literal policy is not permission to rewrite regex or wildcard syntax by lowercasing the pattern.
An explicitly authored Contains with an empty literal follows substring semantics and matches every present valid string. It remains a predicate application. A consumer wishing to express no filtering omits its predicate/filter instead.
Shared validation and evaluation¶
Validate predicate shape, child cardinalities, operator descriptors, authored operand counts and values, and supported capabilities before publishing a result, including for an empty collection. Empty input provides no candidate descriptor for property resolution; candidate-dependent checks apply when candidates exist, not against an invented representative type.
For every candidate and comparison leaf, the shared layer:
- Resolves
PropertyNamethrough the candidate's effective property surface. - Obtains that PropertyDescriptor's effective ValueType and requiredness.
- Resolves and verifies the selected operator's effective affordance.
- Resolves its authored-operand specifications and validates operand values and operator-specific constraints.
- Obtains the subject value through a valid access path and validates it.
- Invokes the value/operator machinery and combines Boolean results according to the predicate structure.
An undeclared property, incompatible candidate type, unavailable or unafforded operator, incorrect operand count, malformed value, or failed access is an explicit error. No implicit stringification, conversion fallback, or silent omission is permitted. Distinct PropertyDescriptors with the same selected name are allowed; each must independently satisfy the operator and operand contracts. A shared primitive representation alone does not prove compatibility.
For the initial scalar comparison contract, an absent optional property makes
the comparison false; an absent required property fails execution. Operator
and operand validation still applies when the optional value is absent. Not
negates that Boolean result, so it can include a candidate whose optional
property is absent. There is no implicit three-valued/null logic. Future
presence-testing operators require their own explicit missing-subject contract.
Validation and evaluation are shared capabilities. Consumers may guide authors away from invalid combinations, but execution does not trust UI validation as a substitute for the semantic checks above. Neither success nor failure mutates the supplied predicate or query definition.
Filter and predicate-bearing expansion¶
Filter is a concrete QueryExpression with exactly one Predicate
relationship to QueryPredicate. A root Filter requires one explicit
HolonCollectionReference; a successor consumes its predecessor's result.
It retains precisely those candidate occurrences for which the Predicate is
true, preserving their relative order, duplicate occurrences, reference identity,
and transaction binding. The output is an ordinary collection holon, including
when empty, and supplies the next expression's Input through Next.
Filtering does not mutate the input or the underlying relationship. Source cardinality and result cardinality are distinct facts. A Predicate that happens to match every member is still evaluated; it is not equivalent definition or execution state to an absent Predicate.
The optional SeedPredicate and ExpansionPredicate relationships accept the
same QueryPredicate forms and invoke the same evaluator as Filter. Their
meaning is expansion followed by filtering. They do not introduce different
operator semantics, source-versus-target ambiguity, or a new Search operation.
An attached predicate and a subsequent Filter remain separately authored
applications; an implementation must not accidentally apply one attachment twice.
An author can therefore express either an expansion with an attached Predicate
or an explicit Expand -> Filter chain. SeedHolons provides the focal-space
Owns expansion. The choice of attachment or expression does not change the
predicate representation supplied by a programmatic caller, agent, or UI.
Indexed property access¶
The evaluator may obtain a selected property from complete, valid collection index evidence rather than dereference each candidate. This is an access-path choice below Predicate syntax, not a special operand or operator. It must preserve descriptor validation, value semantics, candidate coverage, order, and multiplicity.
For a fully materialized collection with a complete member-key index, a comparison selecting Key evaluates against those indexed values. Matching and result construction must not retrieve every target merely to read or rebuild its key. Retain known key/reference associations through expansion and filtering and adjust result positions from that evidence. Scanning the complete local index is sufficient for substring containment; an exact or prefix lookup is not a substitute for Contains.
Index completeness includes occurrence coverage and valid absent-value evidence where applicable. A key-to-single-position map does not by itself cover duplicate occurrences or prove that an unindexed member lacks an optional property. Preserve sufficient collection evidence or reject an index-only execution that cannot meet this contract; never silently drop uncovered members. General evaluation may use ordinary property reads where indexed evidence is unavailable, but an execution constrained to a complete materialized index must fail rather than silently switch to per-target fetching. Descriptor evidence may be reused only within its valid transaction/schema context.
Index-backed execution does not change the storage boundary. Substring matching over an already materialized collection does not require a new storage search operation. Future guest-side evaluation or pushdown must preserve the same validation, errors, and results and state its fallback/failure contract explicitly.
Authoring and consumer boundaries¶
A general authoring experience can expose Property, Operator, and authored operands. A pre-scoped experience may fix any of them while producing the same complete Predicate. A search field can fix Key and Contains and supply a single literal; the backend sees an ordinary ComparisonPredicate. A column filter can fix the selected property. Neither teaches Space Navigator operator semantics.
Predicate Editors may expose groups, sentence-oriented conditions, visual trees,
or constrained forms. Their model includes Not, AllOf, and AnyOf even when
a particular experience exposes only a single leaf. Operator metadata enables
new operators to participate without changes to a generic editor.
The authoring architecture proposes a PredicateEditor VisualizerKind and
a PredicateAuthoringContext carrying candidate-type context, a pre-scoped
property, an existing predicate, editability, and permitted operator/composition
subsets. Their concrete DAHN schema and slot contracts are separate HX design
work, subject to the current
slot-directed Visualizer selection architecture.
Query execution depends on none of those visualizers. ValueTypes describe
operand values; operators describe required operands; HX designers choose how
to present their authoring. These remain independent extension points.
Unsupported predicates and execution outcomes¶
A runtime lacking a concrete Predicate form, operator, or predicate-bearing
expression must fail explicitly with HolonError::NotImplemented, never return
unfiltered members. Misplaced or multiple attachments must not bypass
validation. The root-only contract for SeedHolons takes precedence: an off-root
SeedHolons remains an InvalidParameter error even with a predicate.
A reached expression that fails validation or evaluation is Failed with no
Result; its execution instance is Failed with no ExecutionResult. Earlier
completed steps retain their results. No partially filtered output is published
as success. A runtime may support a subset of the full Predicate model, but its
capability boundary must be explicit and must not redefine the shared grammar.
OrderBy validates sortable values through value descriptors. Distinct,
Skip, and Limit have explicit collection semantics and do not change the
meaning of preceding expressions. Project selects descriptor-valid properties
and produces materialized output; it is not the default navigation carrier.
OrderBy¶
OrderBy consumes a HolonCollectionReference and returns a collection of the
same holon-reference occurrences in sorted order. A root requires an explicit
input collection; a successor consumes its predecessor's result. It neither
deduplicates members nor materializes projected property values in its output.
Holonic ordering specification¶
An OrderBy expression has an OrderBySpecs declared relationship to
OrderBySpec with IsOrdered = true, minimum cardinality 1, and maximum
cardinality 5. Relationship target order determines sort precedence.
Each OrderBySpec is a holon with concrete members:
| Member | Contract |
|---|---|
PropertyName |
Required string-valued InstanceProperty with no default. Names one property on each input holon's effective property surface. No path syntax or computed expression. |
SortDirection |
Required InstanceProperty with enum variants Ascending and Descending; default Ascending. |
NullPlacement |
Required InstanceProperty with enum variants Missing-First and Missing-Last; default Missing-Last, independent of direction. |
See the OrderBySpec schema.
An author may construct the query, expressions, and specs as transient holons,
attach concrete values and related holons, then submit the query for execution.
Authoring does not require staging, committing, or a separate parameter-binding
wrapper. Definition state can be transient; it is distinct from execution state.
Evaluation resolves required argument values without modifying the supplied
holons. For SortDirection and NullPlacement independently, call
property_value() first:
Some(value): validate and use that value without resolving a default.None: invoke the shared descriptor-backed read-only default accessor, validate the resolved default, and use it locally without writing it back.- A read error propagates immediately; it does not trigger default lookup.
A missing value with no applicable default is a required-value error. Malformed explicit values fail validation rather than falling back to defaults; descriptor-resolution and invalid-default errors propagate.
This read-only resolution is distinct from populate_defaults(), which
materializes values during explicit construction. Query evaluation must not
call that mutating operation on caller-supplied arguments. Ordinary property
reads retain their existing stored-value semantics; default fallback is an
explicit accessor operation. Required SortDirection and NullPlacement may
therefore be omitted on a transient OrderBySpec when their descriptor-defined
defaults resolve successfully. Neither success nor failure changes the supplied
query graph. This execution behavior does not relax staged-holon validation or
commit requirements for populated required properties.
Execution reads the attached specs without mutating them. It records status, input, and results in separate execution records. Zero or more than five specs, missing or non-string PropertyName values, invalid enum values, or required values unresolved after read-only effective-value resolution are contract errors. Validate spec shape even for empty input collections.
Comparison and missing values¶
Read the required PropertyName from each spec and resolve that name against
each member's effective property surface using the shared property-descriptor
lookup. Use each resolved PropertyDescriptor's requiredness and effective
ValueType; do not select one member's PropertyDescriptor as the declaration for
all other members. Validate every input occurrence, including singleton
collections and keys that do not affect the eventual ordering. An undeclared
property, unreadable member, malformed value, or absent required property fails execution; none is treated as a
missing optional value or silently omitted.
An absent value is sortable only when the effective property contract permits
its absence. For a given key, a missing value sorts before every present value
under Missing-First, and after every present value under Missing-Last. Two
missing values tie for that key. Direction changes only present-value order.
The supported comparison domains are descriptor-backed integer and string values. For each key, members must resolve to the same effective value-type descriptor identity, and that descriptor must afford supported equality and less-than operations. This conservative compatibility rule also applies when values are absent; matching primitive representations alone do not establish compatibility between distinct value types. Distinct PropertyType identities with the selected name are permitted when they resolve to that same effective ValueType identity. Different keys may use different value types. Requiredness is checked against each member's own resolved property declaration.
Empty input has no member descriptors against which to resolve PropertyName or validate comparison domains. It returns an empty collection after validating spec cardinality, the required string-valued PropertyName, and the effective enum arguments. It does not require an authoring-time property descriptor.
Integer comparison is numeric. String comparison uses the value descriptor's ordinal, case-sensitive lexicographic semantics, without locale collation, case folding, or normalization. Descriptor validation and comparison errors propagate. Unsupported domains, including boolean, enum, bytes, and arrays, fail explicitly; there is no stringification or reference-identity fallback.
Stable ordering and execution outcomes¶
Compare keys lexicographically in relationship target order: the first non-tied key decides the order. If all keys tie, preserve input occurrence order. Descending order must preserve this stability rather than reversing a completed ascending collection. Duplicate occurrences remain separate occurrences.
For example, keys Name Ascending Missing-Last followed by
Age Descending Missing-Last group equal names by decreasing age. Occurrences
with equal names and ages retain their input order. A missing Name follows
every present Name, regardless of Age; two missing Names are compared by Age.
Execution follows authored Next order. Thus OrderBy -> Skip -> Limit slices
the sorted result, whereas Skip -> OrderBy sorts only the retained input.
Ordering does not guarantee repeatable pagination across changed data or
different input ordering among fully tied values.
The expression publishes its result only after validation and sorting succeed.
A reached OrderBy that fails specification validation, property validation, or comparison is
Failed with no Result; its execution instance is Failed with no
ExecutionResult. Earlier completed steps retain their results. The result
remains a collection holon whose identity is passed to the next step as Input.
Skip and Limit¶
Skip and Limit are concrete QueryExpression HolonTypes. Skip declares
SkipCount in its InstanceProperties; Limit declares LimitCount in its
InstanceProperties. Each count PropertyType has an integer ValueType and
IsValueRequired = true, with no default. Consequently, each Skip instance
must supply SkipCount, and each Limit instance must supply LimitCount.
Neither property is attached to the base QueryExpression type or required of
other expression types. See the
pagination schema contract.
Counts must be nonnegative. A missing, negative, or non-integer count is a
contract error, including for empty input. A root requires an explicit input
collection; a successor consumes its predecessor's result. Skip removes the
first SkipCount occurrences; Limit retains at most LimitCount occurrences.
Zero Skip retains all members; zero Limit returns an empty collection. A count
at or above input length returns empty for Skip and all members for Limit.
Large counts must retain these semantics without overflowing an index conversion.
Both expressions preserve relative order and duplicate occurrences among
retained members. They operate in authored Next order and do not require an
OrderBy. Results remain collection holons under the ordinary execution
identity and failure contracts.
Distinct¶
Distinct is a concrete QueryExpression HolonType that removes repeated holon
identities from a collection. It consumes a HolonCollectionReference and
returns a new collection holon containing the first occurrence of each identity,
in input order. A root requires an explicit input collection; a successor
consumes its predecessor's result. See the
Distinct schema contract.
Distinct is parameter-free. It declares no additional InstanceProperties or
InstanceRelationships, no predicate attachment, and no operator-specific
runtime bindings. Identity is not configurable per expression; there is no
selector, key specification, or comparison option.
Duplicate identity¶
Two occurrences are duplicates exactly when their HolonReference values are
equal under the existing reference-layer equality contract. Distinct
introduces no new concept of equality, normalization, or identity resolution.
Any optimized duplicate detection must produce the same duplicate decisions as
pairwise reference equality.
| Reference phase | Equality contract |
|---|---|
Saved (SmartReference) |
Existing SmartReference equality: equal HolonId values; local IDs also require the same Space Manager instance. |
Staged (StagedReference) |
Same transaction ID and temporary ID. |
Transient (TransientReference) |
Same transaction ID and temporary ID. |
References in different phases are unequal, even when they share a lineage.
Distinct does not resolve, reload, or normalize references to compare across
phases. Local and external HolonId variants are never equal under reference
equality. Within a runtime session, there is one Space Manager instance;
local-space identity is that instance, as used by the reference layer.
External references participate through existing reference equality without
dereferencing them. ExternalId routing and resolution semantics are outside
the Distinct contract; duplicate detection makes no claim about whether
unequal references could resolve to the same target.
Holon content and presentation do not establish equality. Property values, cached property hints, descriptors, keys, versioned keys, and diagnostic or summary strings do not participate. Different identities with equal stored values remain distinct. Reference identity remains owned by the reference layer rather than by individual query expressions.
Property/value-based distinctness is a separate capability requiring its own selector and comparison contract.
Survivor and order¶
The first occurrence of each identity survives; every later occurrence is removed. Each survivor retains the original reference value of that occurrence, including its phase and transaction binding. Retained occurrences preserve their relative input order.
For example, [A, B, A, C, B] produces [A, B, C]. Empty input produces an
empty collection. Input with no repeated identity preserves all reference
occurrences and their order. Successful executions always produce a new
collection holon and never reuse the input collection holon.
Composition and execution outcomes¶
Distinct operates in authored Next order and does not change the meaning of
preceding expressions. Its position is significant. If Expand(AuthoredBy)
produces [P1, P2, P1, P2, P1]:
Expand -> Distinctyields[P1, P2].Expand -> Skip(1) -> Distinctyields[P2, P1].Expand -> Distinct -> Skip(1)yields[P2].Expand -> Limit(1) -> Distinctyields[P1].Expand -> Distinct -> Limit(1)yields[P1].
OrderBy -> Distinct retains sorted order among survivors. Distinct neither
sorts nor reorders and does not require a preceding OrderBy.
Distinct has no operator-specific argument values to validate. Ordinary query
structure/type validation, unsupported-feature checks, collection access, and
result construction retain their existing contracts. A root without input is a
contract error. Invocation bindings remain governed by the query-wide
parameter contract.
Execution does not mutate the input collection, its members, or the query
definition. The expression publishes its new result collection only after
successful execution, under the ordinary execution identity and failure
contracts. A reached Distinct that fails is Failed with no Result; its
execution instance is Failed with no ExecutionResult. Earlier completed
steps retain their status and results.
Storage Boundary¶
The engine delegates storage access only to the storage algebra. The relevant
version 1 operations are source expansion, relationship selection, and exact
or prefix canonical-key selection. They return decoded SmartLinks. The query
coordination layer materializes those links as SmartReference values before
continuing the expression pipeline.
The engine must not invent target lookup, reverse lookup, or arbitrary property search beneath this boundary. Reverse traversal uses the persisted inverse SmartLink. General filtering remains above storage unless a later complete, explicitly maintained access path is added.
Parameters¶
QueryParameterDeclaration is reusable definition state. It names an accepted
parameter and identifies the concrete QueryParameterBinding type. A binding
is runtime state and must reference its declaration. A direct caller supplies
bindings to the engine; Dance-mediated bindings belong to QueryDanceRequest.
Expression-local resolved bindings belong to QueryExpressionExecution.
Expression types must validate that each received binding matches a declared parameter and its expected binding type before execution.
Execution Outcomes¶
An execution is pending, running, complete, or failed as defined by
QueryExecutionStatus. A complete execution has an ExecutionResult; a failed
execution does not return a partial collection as if it were a successful one.
Diagnostics belong to the dance and execution outcome contracts, not to saved
query or expression definitions.
Distributed Boundary¶
The shared engine is valid in a single HolonSpace and may identify an
expression boundary that requires distributed continuation. It does not perform
cross-space dispatch, trust-channel coordination, host task orchestration, or
branch merging. Those are host-coordinator responsibilities defined by
dist-query-concept.md.
Non-goals¶
This specification does not define an OpenCypher/GQL parser, optimizer,
row-stream runtime, generic relationship-edge payloads, or arbitrary graph
search. Declarative compatibility is a later compiler that produces the same
Query and QueryExpression model.