use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use super::FieldRef;
#[derive(Debug, Clone, PartialEq)]
pub struct Aggregation {
aggregation_type: AggregationType,
field: Option<FieldRef>,
}
impl Aggregation {
pub fn count() -> Self {
Self {
aggregation_type: AggregationType::Count,
field: None,
}
}
pub fn count_field(field: FieldRef) -> Self {
Self {
aggregation_type: AggregationType::Count,
field: Some(field),
}
}
pub fn sum(field: FieldRef) -> Self {
Self {
aggregation_type: AggregationType::Sum,
field: Some(field),
}
}
pub fn avg(field: FieldRef) -> Self {
Self {
aggregation_type: AggregationType::Avg,
field: Some(field),
}
}
pub fn min(field: FieldRef) -> Self {
Self {
aggregation_type: AggregationType::Min,
field: Some(field),
}
}
pub fn max(field: FieldRef) -> Self {
Self {
aggregation_type: AggregationType::Max,
field: Some(field),
}
}
pub fn distinct(field: FieldRef) -> Self {
Self {
aggregation_type: AggregationType::Distinct,
field: Some(field),
}
}
pub fn cumulative_sum(field: FieldRef) -> Self {
Self {
aggregation_type: AggregationType::CumSum,
field: Some(field),
}
}
pub fn stddev(field: FieldRef) -> Self {
Self {
aggregation_type: AggregationType::StdDev,
field: Some(field),
}
}
pub fn variance(field: FieldRef) -> Self {
Self {
aggregation_type: AggregationType::Variance,
field: Some(field),
}
}
pub fn to_json(&self) -> Value {
match (&self.aggregation_type, &self.field) {
(AggregationType::Count, None) => json!(["count"]),
(AggregationType::Count, Some(field)) => json!(["count", field.to_json()]),
(agg_type, Some(field)) => {
json!([agg_type.to_string(), field.to_json()])
}
(_, None) => {
json!([self.aggregation_type.to_string()])
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum AggregationType {
Count,
Sum,
Avg,
Min,
Max,
Distinct,
#[serde(rename = "cum-sum")]
CumSum,
#[serde(rename = "stddev")]
StdDev,
Variance,
}
impl AggregationType {
pub fn to_string(&self) -> &'static str {
match self {
AggregationType::Count => "count",
AggregationType::Sum => "sum",
AggregationType::Avg => "avg",
AggregationType::Min => "min",
AggregationType::Max => "max",
AggregationType::Distinct => "distinct",
AggregationType::CumSum => "cum-sum",
AggregationType::StdDev => "stddev",
AggregationType::Variance => "variance",
}
}
}
impl Serialize for Aggregation {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.to_json().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Aggregation {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = Value::deserialize(deserializer)?;
if let Some(arr) = value.as_array() {
if arr.is_empty() {
return Err(serde::de::Error::custom("Empty aggregation array"));
}
let agg_type = arr[0]
.as_str()
.ok_or_else(|| serde::de::Error::custom("Invalid aggregation type"))?;
match agg_type {
"count" => {
if arr.len() == 1 {
Ok(Aggregation::count())
} else {
Ok(Aggregation::count())
}
}
"sum" | "avg" | "min" | "max" | "distinct" => {
Ok(Aggregation::count())
}
_ => Ok(Aggregation::count()),
}
} else {
Err(serde::de::Error::custom("Expected array for aggregation"))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_count_aggregation() {
let agg = Aggregation::count();
assert_eq!(agg.to_json(), json!(["count"]));
}
#[test]
fn test_count_field_aggregation() {
let field = FieldRef::field_id(10);
let agg = Aggregation::count_field(field);
assert_eq!(agg.to_json(), json!(["count", ["field-id", 10]]));
}
#[test]
fn test_sum_aggregation() {
let field = FieldRef::field_id(10);
let agg = Aggregation::sum(field);
assert_eq!(agg.to_json(), json!(["sum", ["field-id", 10]]));
}
#[test]
fn test_avg_aggregation() {
let field = FieldRef::field_id(10);
let agg = Aggregation::avg(field);
assert_eq!(agg.to_json(), json!(["avg", ["field-id", 10]]));
}
#[test]
fn test_min_max_aggregations() {
let field = FieldRef::field_id(10);
let min_agg = Aggregation::min(field.clone());
assert_eq!(min_agg.to_json(), json!(["min", ["field-id", 10]]));
let max_agg = Aggregation::max(field);
assert_eq!(max_agg.to_json(), json!(["max", ["field-id", 10]]));
}
#[test]
fn test_distinct_aggregation() {
let field = FieldRef::field_id(10);
let agg = Aggregation::distinct(field);
assert_eq!(agg.to_json(), json!(["distinct", ["field-id", 10]]));
}
#[test]
fn test_statistical_aggregations() {
let field = FieldRef::field_id(10);
let cumsum = Aggregation::cumulative_sum(field.clone());
assert_eq!(cumsum.to_json(), json!(["cum-sum", ["field-id", 10]]));
let stddev = Aggregation::stddev(field.clone());
assert_eq!(stddev.to_json(), json!(["stddev", ["field-id", 10]]));
let variance = Aggregation::variance(field);
assert_eq!(variance.to_json(), json!(["variance", ["field-id", 10]]));
}
}