zino_orm/
aggregate.rs

1use self::Aggregation::*;
2use super::{Entity, query::QueryExt};
3use zino_core::model::Query;
4
5/// SQL aggregate expressions.
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::new()
14///     .aggregate(Aggregation::Count(TaskColumn::Id, false), "num_tasks")
15///     .aggregate(Aggregation::Sum(TaskColumn::Manhours), "total_manhours")
16///     .aggregate(Aggregation::Avg(TaskColumn::Manhours), "average_manhours")
17///     .and_eq(TaskColumn::Status, "Completed")
18///     .group_by(TaskColumn::ProjectId, "project_id")
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 the SQL expression.
50    pub(super) fn expr(&self) -> String {
51        match self {
52            Count(col, distinct) => {
53                let col_name = E::format_column(col);
54                let field = Query::format_field(&col_name);
55                if *distinct {
56                    format!("count(distinct {field})")
57                } else {
58                    format!("count({field})")
59                }
60            }
61            Sum(col) => {
62                let col_name = E::format_column(col);
63                let field = Query::format_field(&col_name);
64                format!("sum({field})")
65            }
66            Avg(col) => {
67                let col_name = E::format_column(col);
68                let field = Query::format_field(&col_name);
69                format!("avg({field})")
70            }
71            Min(col) => {
72                let col_name = E::format_column(col);
73                let field = Query::format_field(&col_name);
74                format!("min({field})")
75            }
76            Max(col) => {
77                let col_name = E::format_column(col);
78                let field = Query::format_field(&col_name);
79                format!("max({field})")
80            }
81            Stddev(col) => {
82                let col_name = E::format_column(col);
83                let field = Query::format_field(&col_name);
84                format!("stddev({field})")
85            }
86            Variance(col) => {
87                let col_name = E::format_column(col);
88                let field = Query::format_field(&col_name);
89                format!("variance({field})")
90            }
91            JsonArrayagg(col) => {
92                let col_name = E::format_column(col);
93                let field = Query::format_field(&col_name);
94                if cfg!(any(
95                    feature = "orm-mariadb",
96                    feature = "orm-mysql",
97                    feature = "orm-tidb"
98                )) {
99                    format!("json_arrayagg({field})")
100                } else if cfg!(feature = "orm-postgres") {
101                    format!("jsonb_agg({field})")
102                } else {
103                    format!("json_group_array({field})")
104                }
105            }
106            JsonObjectagg(key_col, val_col) => {
107                let key_col_name = E::format_column(key_col);
108                let val_col_name = E::format_column(val_col);
109                let key_field = Query::format_field(&key_col_name);
110                let val_field = Query::format_field(&val_col_name);
111                if cfg!(any(
112                    feature = "orm-mariadb",
113                    feature = "orm-mysql",
114                    feature = "orm-tidb"
115                )) {
116                    format!("json_objectagg({key_field}, {val_field})")
117                } else if cfg!(feature = "orm-postgres") {
118                    format!("jsonb_object_agg({key_field}, {val_field})")
119                } else {
120                    format!("json_group_object({key_field}, {val_field})")
121                }
122            }
123        }
124    }
125}