hyphae_query/model.rs
1// SPDX-License-Identifier: Apache-2.0
2
3use std::time::Duration;
4
5use crate::{FieldPath, Value};
6
7/// One logical record supplied to the reference query executor.
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub struct Record {
10 /// Globally unique nonempty binary key.
11 pub key: Vec<u8>,
12 /// Structured record value.
13 pub value: Value,
14}
15
16impl Record {
17 /// Creates a logical record. Key validity is checked by execution so a
18 /// complete shard batch can be rejected consistently.
19 pub fn new(key: impl Into<Vec<u8>>, value: Value) -> Self {
20 Self {
21 key: key.into(),
22 value,
23 }
24 }
25}
26
27/// Ordered comparison operator for one resolved field.
28#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub enum CompareOperator {
30 /// Exact type-and-value equality.
31 Equal,
32 /// Exact inequality; missing still evaluates false.
33 NotEqual,
34 /// Less than, only between the same value variants.
35 Less,
36 /// Less than or equal, only between the same value variants.
37 LessOrEqual,
38 /// Greater than, only between the same value variants.
39 Greater,
40 /// Greater than or equal, only between the same value variants.
41 GreaterOrEqual,
42}
43
44/// Deterministic filter expression.
45#[derive(Clone, Debug, Eq, PartialEq)]
46pub enum Filter {
47 /// Matches every record.
48 MatchAll,
49 /// Tests whether a path resolves, including to explicit null.
50 Exists(FieldPath),
51 /// Compares one resolved value against a literal.
52 Compare {
53 /// Field path.
54 path: FieldPath,
55 /// Comparison operator.
56 operator: CompareOperator,
57 /// Literal right-hand value.
58 value: Value,
59 },
60 /// Tests a UTF-8 or binary prefix of the same type.
61 Prefix {
62 /// Field path.
63 path: FieldPath,
64 /// String or bytes prefix.
65 prefix: Value,
66 },
67 /// Tests array membership, UTF-8 substring, or byte subsequence.
68 Contains {
69 /// Field path.
70 path: FieldPath,
71 /// Exact element or same-type needle.
72 needle: Value,
73 },
74 /// Every child must match; an empty list is true.
75 All(Vec<Self>),
76 /// At least one child must match; an empty list is false.
77 Any(Vec<Self>),
78 /// Ordinary two-valued negation.
79 Not(Box<Self>),
80}
81
82/// Sort direction for one field.
83#[derive(Clone, Copy, Debug, Eq, PartialEq)]
84pub enum SortDirection {
85 /// Natural ascending value order.
86 Ascending,
87 /// Reverse value order.
88 Descending,
89}
90
91/// Explicit placement for missing and null sort values.
92#[derive(Clone, Copy, Debug, Eq, PartialEq)]
93pub enum NullPlacement {
94 /// Missing and null precede non-null values.
95 First,
96 /// Missing and null follow non-null values.
97 Last,
98}
99
100/// One requested sort component.
101#[derive(Clone, Debug, Eq, PartialEq)]
102pub struct SortField {
103 /// Value path.
104 pub path: FieldPath,
105 /// Value comparison direction.
106 pub direction: SortDirection,
107 /// Placement for missing and explicit null.
108 pub nulls: NullPlacement,
109}
110
111/// Logical continuation position after one emitted record.
112#[derive(Clone, Debug, Eq, PartialEq)]
113pub struct Cursor {
114 /// One normalized sort value per requested sort field. `None` represents
115 /// both missing and explicit null for sorting.
116 pub sort_values: Vec<Option<Value>>,
117 /// Mandatory final binary-key tie-breaker.
118 pub key: Vec<u8>,
119}
120
121/// One aggregate calculation.
122#[derive(Clone, Debug, Eq, PartialEq)]
123pub enum Metric {
124 /// Count every matched record.
125 Count,
126 /// Checked integer sum, ignoring missing and null.
127 Sum(FieldPath),
128 /// Minimum non-null resolved value under the total value order.
129 Min(FieldPath),
130 /// Maximum non-null resolved value under the total value order.
131 Max(FieldPath),
132}
133
134/// Named aggregate calculation.
135#[derive(Clone, Debug, Eq, PartialEq)]
136pub struct NamedMetric {
137 /// Unique nonempty result name.
138 pub name: String,
139 /// Calculation.
140 pub metric: Metric,
141}
142
143/// Optional global or grouped aggregation plan.
144#[derive(Clone, Debug, Eq, PartialEq)]
145pub struct AggregationPlan {
146 /// Group-key paths. Empty means one global group.
147 pub group_by: Vec<FieldPath>,
148 /// Calculations applied to each group.
149 pub metrics: Vec<NamedMetric>,
150}
151
152/// Complete deterministic query request.
153#[derive(Clone, Debug, Eq, PartialEq)]
154pub struct Query {
155 /// Filter applied before sorting and aggregation.
156 pub filter: Filter,
157 /// Requested sort fields; binary key ascending is always appended.
158 pub sort: Vec<SortField>,
159 /// Optional continuation position.
160 pub cursor: Option<Cursor>,
161 /// Maximum rows in this page; must be nonzero.
162 pub limit: usize,
163 /// Optional aggregation over the full filtered set.
164 pub aggregation: Option<AggregationPlan>,
165}
166
167/// Runtime and shape budgets for one complete global execution.
168#[derive(Clone, Debug, Eq, PartialEq)]
169pub struct ExecutionLimits {
170 /// Maximum records inspected across all shards.
171 pub max_scanned_records: u64,
172 /// Maximum records retained after filtering.
173 pub max_matched_records: u64,
174 /// Maximum requested page size.
175 pub max_returned_records: usize,
176 /// Maximum distinct aggregation groups.
177 pub max_groups: usize,
178 /// Maximum nodes in the recursive filter expression.
179 pub max_filter_nodes: usize,
180 /// Maximum recursive filter depth, counting the root as one.
181 pub max_filter_depth: usize,
182 /// Maximum explicit sort fields.
183 pub max_sort_fields: usize,
184 /// Maximum group-key fields.
185 pub max_group_fields: usize,
186 /// Maximum metrics in one plan.
187 pub max_metrics: usize,
188 /// Cooperative monotonic execution timeout.
189 pub timeout: Duration,
190}
191
192impl Default for ExecutionLimits {
193 fn default() -> Self {
194 Self {
195 max_scanned_records: 1_000_000,
196 max_matched_records: 100_000,
197 max_returned_records: 1_000,
198 max_groups: 10_000,
199 max_filter_nodes: 256,
200 max_filter_depth: 64,
201 max_sort_fields: 16,
202 max_group_fields: 8,
203 max_metrics: 32,
204 timeout: Duration::from_secs(30),
205 }
206 }
207}
208
209/// Value emitted by one aggregate metric.
210#[derive(Clone, Debug, Eq, PartialEq)]
211pub enum MetricValue {
212 /// Record count.
213 Count(u64),
214 /// Checked integer result; `None` means no non-null inputs.
215 Integer(Option<i128>),
216 /// Minimum or maximum value; `None` means no non-null inputs.
217 Value(Option<Value>),
218}
219
220/// Named aggregate output.
221#[derive(Clone, Debug, Eq, PartialEq)]
222pub struct NamedMetricValue {
223 /// Metric name copied from the plan.
224 pub name: String,
225 /// Calculated value.
226 pub value: MetricValue,
227}
228
229/// One deterministic aggregation group.
230#[derive(Clone, Debug, Eq, PartialEq)]
231pub struct GroupResult {
232 /// Group values in plan order. `None` means a missing path; explicit null is
233 /// represented by `Some(Value::Null)`.
234 pub key: Vec<Option<Value>>,
235 /// Metric values in plan order.
236 pub metrics: Vec<NamedMetricValue>,
237}
238
239/// Aggregation output. An ungrouped plan has one empty-key group.
240#[derive(Clone, Debug, Eq, PartialEq)]
241pub struct AggregationResult {
242 /// Whether the originating plan had explicit group fields.
243 pub grouped: bool,
244 /// Groups in deterministic key order.
245 pub groups: Vec<GroupResult>,
246}
247
248/// Complete successful query response.
249#[derive(Clone, Debug, Eq, PartialEq)]
250pub struct QueryResult {
251 /// Page rows after global merge, sort, cursor, and limit.
252 pub rows: Vec<Record>,
253 /// Cursor after the last row when more rows remain.
254 pub next_cursor: Option<Cursor>,
255 /// Optional aggregation over every filtered record before pagination.
256 pub aggregation: Option<AggregationResult>,
257 /// Records inspected across every shard.
258 pub scanned_records: u64,
259 /// Records matching the filter before pagination.
260 pub matched_records: u64,
261}