harmos_storage/query/transform/
aggregate.rs1pub use harmos_domain::model::storage::query::transform::aggregate::{Aggregate, Measure};
17
18use crate::storage::query::transform::FieldPathExecution;
21use std::cmp::Ordering;
23use std::sync::Arc;
24use arrow_array::RecordBatch;
26use arrow_schema::{DataType, Field, Schema};
27use super::{exact_type, invalid, numeric_type, scalar_type, values_array};
29use crate::Result;
30use crate::storage::query::accumulate::Accumulator;
31use harmos_domain::model::storage::query::scalar::{Agg, ScalarValue};
32
33pub struct Groups {
42 plan: Aggregate,
43 schema: Arc<Schema>,
44 groups: Vec<Group>,
45 capture: u64,
46}
47
48struct Group {
49 keys: Vec<ScalarValue>,
50 accumulators: Vec<Accumulator>,
51}
52
53pub trait AggregateExecution {
58 fn output_schema(&self, input: &Schema) -> Result<Schema>;
59
60 fn start(&self, input: &Schema) -> Result<Groups>;
61
62 fn apply(&self, input: &RecordBatch, check: &mut impl FnMut() -> Result<()>) -> Result<RecordBatch>;
63}
64
65impl AggregateExecution for Aggregate {
68 fn output_schema(&self, input: &Schema) -> Result<Schema> {
69 if self.groups.len() > 64 || self.measures.is_empty() || self.measures.len() > 64 {
70 return Err(invalid("aggregate requires at most 64 groups and 1..64 measures"));
71 }
72 let mut fields = Vec::new();
73 for group in &self.groups {
74 let field = group.field.field(input)?;
75 if !scalar_type(field.data_type()) {
76 return Err(invalid("aggregate grouping requires scalar keys"));
77 }
78 fields.push(field.with_name(&group.name).with_nullable(true));
79 }
80 for measure in &self.measures {
81 let field = measure.field.field(input)?;
82 if !scalar_type(field.data_type()) {
83 return Err(invalid("aggregate requires scalar values"));
84 }
85 let statistic = measure.statistic;
86 if !matches!(statistic, crate::Aggregate::Count | crate::Aggregate::Min | crate::Aggregate::Max)
87 && !numeric_type(field.data_type())
88 {
89 return Err(invalid("numeric aggregate requires numeric field"));
90 }
91 if let crate::Aggregate::Percentile(rank) = statistic {
92 if rank > 100 {
94 return Err(invalid("invalid percentile rank"));
95 }
96 }
97 let output = match statistic {
98 crate::Aggregate::Count => DataType::UInt64,
99 crate::Aggregate::Min | crate::Aggregate::Max => field.data_type().clone(),
100 crate::Aggregate::Sum | crate::Aggregate::Mean | crate::Aggregate::Median
101 if !matches!(field.data_type(), DataType::Float32 | DataType::Float64) =>
102 {
103 exact_type()
104 }
105 _ => DataType::Float64,
106 };
107 let mut field = Field::new(&measure.name, output, !matches!(statistic, crate::Aggregate::Count));
108 if field.data_type() == &exact_type() {
109 field = field
110 .with_metadata(std::collections::HashMap::from([("harmos.logical_type".to_owned(), "exact-rational-v1".to_owned())]));
111 }
112 fields.push(field);
113 }
114 for field in &fields {
115 if field.name().is_empty() || fields.iter().filter(|other| other.name() == field.name()).count() != 1 {
116 return Err(invalid("aggregate output names must be nonempty and unique"));
117 }
118 }
119 Ok(Schema::new(fields))
120 }
121
122 fn start(&self, input: &Schema) -> Result<Groups> {
123 let schema = Arc::new(self.output_schema(input)?);
124 let mut groups = Groups {
125 plan: self.clone(),
126 schema,
127 groups: Vec::new(),
128 capture: 0,
129 };
130 if self.groups.is_empty() {
132 groups.groups.push(new_group(self, Vec::new()));
133 }
134 Ok(groups)
135 }
136
137 fn apply(&self, input: &RecordBatch, check: &mut impl FnMut() -> Result<()>) -> Result<RecordBatch> {
138 let mut groups = self.start(input.schema().as_ref())?;
139 groups.push(input, check)?;
140 groups.finish(check)
141 }
142}
143
144impl Groups {
145 pub fn push(&mut self, input: &RecordBatch, check: &mut impl FnMut() -> Result<()>) -> Result<()> {
146 for row in 0..input.num_rows() {
148 check()?;
149 let keys = self.plan.groups.iter().map(|group| group.field.value(input, row).map(normalized)).collect::<Result<Vec<_>>>()?;
150 let values =
151 self.plan.measures.iter().map(|measure| measure.field.value(input, row).map(normalized)).collect::<Result<Vec<_>>>()?;
152 let index = match self.find(&keys)? {
153 Ok(index) => index,
154 Err(index) => {
155 self.groups.insert(index, new_group(&self.plan, keys));
156 index
157 }
158 };
159 let group = &mut self.groups[index];
160 for (accumulator, value) in group.accumulators.iter_mut().zip(values) {
161 accumulator.push(self.capture, &value)?;
162 }
163 self.capture = self.capture.checked_add(1).ok_or_else(|| invalid("query capture count overflow"))?;
164 }
165 Ok(())
166 }
167
168 pub fn finish(self, check: &mut impl FnMut() -> Result<()>) -> Result<RecordBatch> {
169 check()?;
170 let width = self.schema.fields().len();
171 let mut values = vec![Vec::with_capacity(self.groups.len()); width];
172 for group in self.groups {
173 check()?;
174 for (column, key) in group.keys.into_iter().enumerate() {
175 values[column].push(key);
176 }
177 for (column, accumulator) in group.accumulators.into_iter().enumerate() {
178 values[self.plan.groups.len() + column].push(accumulator.finish(check)?);
179 }
180 }
181 let columns = self
182 .schema
183 .fields()
184 .iter()
185 .zip(values)
186 .map(|(field, values)| values_array(field.data_type(), &values))
187 .collect::<Result<Vec<_>>>()?;
188 RecordBatch::try_new(self.schema, columns).map_err(|_| invalid("aggregate output batch is invalid"))
189 }
190
191 fn find(&self, keys: &[ScalarValue]) -> Result<std::result::Result<usize, usize>> {
193 let (mut low, mut high) = (0usize, self.groups.len());
194 while low < high {
195 let middle = low + (high - low) / 2;
196 match compare_keys(&self.groups[middle].keys, keys)? {
197 Ordering::Less => low = middle + 1,
198 Ordering::Greater => high = middle,
199 Ordering::Equal => return Ok(Ok(middle)),
200 }
201 }
202 Ok(Err(low))
203 }
204}
205
206fn new_group(plan: &Aggregate, keys: Vec<ScalarValue>) -> Group {
211 Group {
212 keys,
213 accumulators: plan.measures.iter().map(|measure| Accumulator::new(Agg::Of(measure.statistic))).collect(),
214 }
215}
216
217fn compare_keys(left: &[ScalarValue], right: &[ScalarValue]) -> Result<Ordering> {
218 for (left, right) in left.iter().zip(right) {
219 let comparison = left.compare(right)?;
220 if !comparison.is_eq() {
221 return Ok(comparison);
222 }
223 }
224 Ok(left.len().cmp(&right.len()))
225}
226
227fn normalized(value: ScalarValue) -> ScalarValue {
228 match value {
229 ScalarValue::Float(number) if !number.is_finite() => ScalarValue::Null,
230 value => value,
231 }
232}
233
234