Skip to main content

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}