zino_orm/
aggregate.rs

1use self::Aggregation::*;
2use super::{query::QueryExt, Entity};
3use zino_core::model::Query;
4
5/// SQL aggregate functions.
6///
7/// # Examples
8/// ```rust,ignore
9/// use crate::model::{Task, TaskColumn};
10/// use zino_core::Map;
11/// use zino_orm::{Aggregation, QueryBuilder, Schema};
12///
13/// let query = QueryBuilder::<Task>::new()
14///     .aggregate(Aggregation::Count(TaskColumn::Id, false), Some("num_tasks"))
15///     .aggregate(Aggregation::Sum(TaskColumn::Manhours), Some("total_manhours"))
16///     .aggregate(Aggregation::Avg(TaskColumn::Manhours), Some("average_manhours"))
17///     .and_eq(TaskColumn::Status, "Completed")
18///     .group_by(TaskColumn::ProjectId)
19///     .having_ge(Aggregation::Avg(TaskColumn::Manhours), 50)
20///     .order_desc("total_manhours")
21///     .limit(10)
22///     .build();
23/// let entries = Task::aggregate::<Map>(&query).await?;
24/// ```
25#[derive(Debug, Clone, Copy)]
26#[non_exhaustive]
27pub enum Aggregation<E: Entity> {
28    /// The `COUNT` function with the `DISTINCT` modifier.
29    Count(E::Column, bool),
30    /// The `SUM` function.
31    Sum(E::Column),
32    /// The `AVG` function.
33    Avg(E::Column),
34    /// The `MIN` function.
35    Min(E::Column),
36    /// The `MAX` function.
37    Max(E::Column),
38    /// The `STDDEV` function.
39    Stddev(E::Column),
40    /// The `VARIANCE` function.
41    Variance(E::Column),
42    /// The `JSON_ARRAYAGG` function.
43    JsonArrayagg(E::Column),
44    /// The `JSON_OBJECTAGG` function.
45    JsonObjectagg(E::Column, E::Column),
46}
47
48impl<E: Entity> Aggregation<E> {
49    /// Returns a default alias for the aggregation.
50    pub(super) fn default_alias(&self) -> String {
51        match self {
52            Count(col, distinct) => {
53                if *distinct {
54                    [col.as_ref(), "_distinct"].concat()
55                } else {
56                    [col.as_ref(), "_count"].concat()
57                }
58            }
59            Sum(col) => [col.as_ref(), "_sum"].concat(),
60            Avg(col) => [col.as_ref(), "_avg"].concat(),
61            Min(col) => [col.as_ref(), "_min"].concat(),
62            Max(col) => [col.as_ref(), "_max"].concat(),
63            Stddev(col) => [col.as_ref(), "_stddev"].concat(),
64            Variance(col) => [col.as_ref(), "_variance"].concat(),
65            JsonArrayagg(col) => [col.as_ref(), "_arrayagg"].concat(),
66            JsonObjectagg(key_col, val_col) => {
67                [key_col.as_ref(), "_", val_col.as_ref(), "_objectagg"].concat()
68            }
69        }
70    }
71
72    /// Returns the SQL expression.
73    pub(super) fn expr(&self) -> String {
74        match self {
75            Count(col, distinct) => {
76                let col_name = E::format_column(col);
77                let field = Query::format_field(&col_name);
78                if *distinct {
79                    format!("count(distinct {field})")
80                } else {
81                    format!("count({field})")
82                }
83            }
84            Sum(col) => {
85                let col_name = E::format_column(col);
86                let field = Query::format_field(&col_name);
87                format!("sum({field})")
88            }
89            Avg(col) => {
90                let col_name = E::format_column(col);
91                let field = Query::format_field(&col_name);
92                format!("avg({field})")
93            }
94            Min(col) => {
95                let col_name = E::format_column(col);
96                let field = Query::format_field(&col_name);
97                format!("min({field})")
98            }
99            Max(col) => {
100                let col_name = E::format_column(col);
101                let field = Query::format_field(&col_name);
102                format!("max({field})")
103            }
104            Stddev(col) => {
105                let col_name = E::format_column(col);
106                let field = Query::format_field(&col_name);
107                format!("stddev({field})")
108            }
109            Variance(col) => {
110                let col_name = E::format_column(col);
111                let field = Query::format_field(&col_name);
112                format!("variance({field})")
113            }
114            JsonArrayagg(col) => {
115                let col_name = E::format_column(col);
116                let field = Query::format_field(&col_name);
117                if cfg!(any(
118                    feature = "orm-mariadb",
119                    feature = "orm-mysql",
120                    feature = "orm-tidb"
121                )) {
122                    format!("json_arrayagg({field})")
123                } else if cfg!(feature = "orm-postgres") {
124                    format!("jsonb_agg({field})")
125                } else {
126                    format!("json_group_array({field})")
127                }
128            }
129            JsonObjectagg(key_col, val_col) => {
130                let key_col_name = E::format_column(key_col);
131                let val_col_name = E::format_column(val_col);
132                let key_field = Query::format_field(&key_col_name);
133                let val_field = Query::format_field(&val_col_name);
134                if cfg!(any(
135                    feature = "orm-mariadb",
136                    feature = "orm-mysql",
137                    feature = "orm-tidb"
138                )) {
139                    format!("json_objectagg({key_field}, {val_field})")
140                } else if cfg!(feature = "orm-postgres") {
141                    format!("jsonb_object_agg({key_field}, {val_field})")
142                } else {
143                    format!("json_group_object({key_field}, {val_field})")
144                }
145            }
146        }
147    }
148}