Apply GROUP BY + aggregate functions on a join response payload. The input response payload is a msgpack array of maps (merged from all cores). Returns a new response with aggregated results (also msgpack).
(
resp: Response,
group_by: &[String],
aggregates: &[(String, String)],
)
| 16 | /// The input response payload is a msgpack array of maps (merged from all cores). |
| 17 | /// Returns a new response with aggregated results (also msgpack). |
| 18 | pub fn apply_post_aggregation( |
| 19 | resp: Response, |
| 20 | group_by: &[String], |
| 21 | aggregates: &[(String, String)], |
| 22 | ) -> crate::Result<Response> { |
| 23 | let payload_bytes = resp.payload.as_bytes(); |
| 24 | |
| 25 | // Parse the msgpack array of row-maps. |
| 26 | let rows = parse_msgpack_rows(payload_bytes)?; |
| 27 | |
| 28 | // Group rows by the GROUP BY columns. |
| 29 | let mut groups: HashMap<Vec<String>, Vec<&[u8]>> = HashMap::new(); |
| 30 | for row in &rows { |
| 31 | let key: Vec<String> = group_by |
| 32 | .iter() |
| 33 | .map(|col| extract_field_str(row, col).unwrap_or_default()) |
| 34 | .collect(); |
| 35 | groups.entry(key).or_default().push(row); |
| 36 | } |
| 37 | |
| 38 | // Build result as msgpack array. |
| 39 | use nodedb_query::msgpack_scan::writer; |
| 40 | let mut buf = Vec::with_capacity(payload_bytes.len()); |
| 41 | writer::write_array_header(&mut buf, groups.len()); |
| 42 | |
| 43 | for (key, group_rows) in &groups { |
| 44 | let field_count = group_by.len() + aggregates.len(); |
| 45 | writer::write_map_header(&mut buf, field_count); |
| 46 | |
| 47 | // Write GROUP BY columns. |
| 48 | for (i, col) in group_by.iter().enumerate() { |
| 49 | writer::write_kv_str(&mut buf, col, &key[i]); |
| 50 | } |
| 51 | |
| 52 | // Compute and write each aggregate. |
| 53 | for (op, field) in aggregates { |
| 54 | let agg_key = canonical_agg_key(op, field); |
| 55 | let value = compute_aggregate(op, field, group_rows); |
| 56 | match value { |
| 57 | AggValue::Int(n) => writer::write_kv_i64(&mut buf, &agg_key, n), |
| 58 | AggValue::Float(f) => writer::write_kv_f64(&mut buf, &agg_key, f), |
| 59 | AggValue::Null => writer::write_kv_null(&mut buf, &agg_key), |
| 60 | } |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | Ok(Response { |
| 65 | payload: Payload::from_vec(buf), |
| 66 | ..resp |
| 67 | }) |
| 68 | } |
| 69 | |
| 70 | enum AggValue { |
| 71 | Int(i64), |
no test coverage detected