Skip to main content

siderite_orm/expr/
aggregate.rs

1//! Aggregate functions: `Count::all()`, `Sum::of(Book::price)`, ...
2//!
3//! Attach them to a queryset with `annotate("total", Sum::of(..))` (one value
4//! per group or row) or `aggregate([("total", Sum::of(..).into())])` (one row
5//! for the whole queryset).
6
7use super::Expr;
8
9/// The aggregate function computed.
10#[derive(Debug, Clone, PartialEq)]
11pub enum AggFunc {
12    /// `COUNT`.
13    Count,
14    /// `SUM`.
15    Sum,
16    /// `AVG`.
17    Avg,
18    /// `MIN`.
19    Min,
20    /// `MAX`.
21    Max,
22    /// Standard deviation (needs [`Feature::StatisticalAggregates`](crate::Feature::StatisticalAggregates)).
23    StdDev {
24        /// Sample (`true`) or population (`false`) deviation.
25        sample: bool,
26    },
27    /// Variance (needs [`Feature::StatisticalAggregates`](crate::Feature::StatisticalAggregates)).
28    Variance {
29        /// Sample (`true`) or population (`false`) variance.
30        sample: bool,
31    },
32    /// Collect values into an array (needs [`Feature::Arrays`](crate::Feature::Arrays)).
33    ArrayAgg,
34    /// Join text values with a separator (`STRING_AGG` / `group_concat`).
35    StringAgg {
36        /// Separator, bound as a parameter.
37        separator: String,
38    },
39}
40
41/// An aggregate call: function, argument, `DISTINCT` and `FILTER (WHERE ..)`.
42#[derive(Debug, Clone, PartialEq)]
43pub struct Aggregate {
44    /// Function.
45    pub func: AggFunc,
46    /// Argument; `None` means `COUNT(*)`.
47    pub arg: Option<Box<Expr>>,
48    /// Aggregate over distinct values only.
49    pub distinct: bool,
50    /// Only rows matching this predicate contribute.
51    pub filter: Option<Box<Expr>>,
52}
53
54impl Aggregate {
55    fn new(func: AggFunc, arg: Option<Expr>) -> Self {
56        Self {
57            func,
58            arg: arg.map(Box::new),
59            distinct: false,
60            filter: None,
61        }
62    }
63
64    /// Aggregate distinct values only (`COUNT(DISTINCT x)`).
65    #[must_use]
66    pub fn distinct(mut self) -> Self {
67        self.distinct = true;
68        self
69    }
70
71    /// Restrict the rows that contribute (`FILTER (WHERE ..)`).
72    #[must_use]
73    pub fn filter(mut self, predicate: Expr) -> Self {
74        self.filter = Some(Box::new(match self.filter.take() {
75            Some(existing) => existing.and(predicate),
76            None => predicate,
77        }));
78        self
79    }
80
81    /// The argument and filter expressions.
82    pub(super) fn children(&self) -> Vec<&Expr> {
83        self.arg
84            .as_deref()
85            .into_iter()
86            .chain(self.filter.as_deref())
87            .collect()
88    }
89
90    /// Mutable counterpart of [`children`](Self::children).
91    pub(super) fn children_mut(&mut self) -> Vec<&mut Expr> {
92        self.arg
93            .as_deref_mut()
94            .into_iter()
95            .chain(self.filter.as_deref_mut())
96            .collect()
97    }
98}
99
100impl From<Aggregate> for Expr {
101    fn from(agg: Aggregate) -> Self {
102        Expr::Aggregate(agg)
103    }
104}
105
106macro_rules! aggregate_of {
107    ($($(#[$doc:meta])* $name:ident => $func:expr;)*) => {$(
108        $(#[$doc])*
109        #[derive(Debug, Clone, Copy)]
110        pub struct $name;
111
112        impl $name {
113            /// Aggregate over `expr`.
114            pub fn of(expr: impl Into<Expr>) -> Aggregate {
115                Aggregate::new($func, Some(expr.into()))
116            }
117        }
118    )*};
119}
120
121aggregate_of! {
122    /// `SUM(expr)`. `NULL` for an empty group; PostgreSQL returns `numeric` for integer sums.
123    Sum => AggFunc::Sum;
124    /// `AVG(expr)`.
125    Avg => AggFunc::Avg;
126    /// `MIN(expr)`.
127    Min => AggFunc::Min;
128    /// `MAX(expr)`.
129    Max => AggFunc::Max;
130    /// Collect values into an array (PostgreSQL only).
131    ArrayAgg => AggFunc::ArrayAgg;
132}
133
134/// `COUNT(*)` and `COUNT(expr)`.
135#[derive(Debug, Clone, Copy)]
136pub struct Count;
137
138impl Count {
139    /// `COUNT(*)`: every row of the group.
140    pub fn all() -> Aggregate {
141        Aggregate::new(AggFunc::Count, None)
142    }
143
144    /// `COUNT(expr)`: rows where `expr` is not `NULL`.
145    pub fn of(expr: impl Into<Expr>) -> Aggregate {
146        Aggregate::new(AggFunc::Count, Some(expr.into()))
147    }
148}
149
150/// Standard deviation (PostgreSQL only).
151#[derive(Debug, Clone, Copy)]
152pub struct StdDev;
153
154impl StdDev {
155    /// Population standard deviation.
156    pub fn population(expr: impl Into<Expr>) -> Aggregate {
157        Aggregate::new(AggFunc::StdDev { sample: false }, Some(expr.into()))
158    }
159
160    /// Sample standard deviation.
161    pub fn sample(expr: impl Into<Expr>) -> Aggregate {
162        Aggregate::new(AggFunc::StdDev { sample: true }, Some(expr.into()))
163    }
164}
165
166/// Variance (PostgreSQL only).
167#[derive(Debug, Clone, Copy)]
168pub struct Variance;
169
170impl Variance {
171    /// Population variance.
172    pub fn population(expr: impl Into<Expr>) -> Aggregate {
173        Aggregate::new(AggFunc::Variance { sample: false }, Some(expr.into()))
174    }
175
176    /// Sample variance.
177    pub fn sample(expr: impl Into<Expr>) -> Aggregate {
178        Aggregate::new(AggFunc::Variance { sample: true }, Some(expr.into()))
179    }
180}
181
182/// Join text values with a separator (`STRING_AGG` on PostgreSQL, `group_concat` on SQLite).
183#[derive(Debug, Clone, Copy)]
184pub struct StringAgg;
185
186impl StringAgg {
187    /// Concatenate `expr` (text) separated by `separator`.
188    pub fn of(expr: impl Into<Expr>, separator: impl Into<String>) -> Aggregate {
189        Aggregate::new(
190            AggFunc::StringAgg {
191                separator: separator.into(),
192            },
193            Some(expr.into()),
194        )
195    }
196}