rust_query/value/
aggregate.rs

1use std::{
2    marker::PhantomData,
3    ops::{Deref, DerefMut},
4    rc::Rc,
5};
6
7use ref_cast::RefCast;
8use sea_query::{Func, SelectStatement, SimpleExpr};
9
10use crate::{
11    Expr, Table,
12    alias::MyAlias,
13    rows::Rows,
14    value::{EqTyp, IntoExpr, MyTyp, NumTyp, Typed, ValueBuilder},
15};
16
17use super::DynTypedExpr;
18
19/// This is the argument type used for [aggregate].
20pub struct Aggregate<'outer, 'inner, S> {
21    pub(crate) query: Rows<'inner, S>,
22    _p: PhantomData<&'inner &'outer ()>,
23}
24
25impl<'inner, S> Deref for Aggregate<'_, 'inner, S> {
26    type Target = Rows<'inner, S>;
27
28    fn deref(&self) -> &Self::Target {
29        &self.query
30    }
31}
32
33impl<S> DerefMut for Aggregate<'_, '_, S> {
34    fn deref_mut(&mut self) -> &mut Self::Target {
35        &mut self.query
36    }
37}
38
39impl<'outer, 'inner, S: 'static> Aggregate<'outer, 'inner, S> {
40    fn select<T>(
41        &self,
42        expr: impl 'static + Fn(&mut ValueBuilder) -> SimpleExpr,
43    ) -> Aggr<S, Option<T>> {
44        let expr = DynTypedExpr(Rc::new(expr));
45        let mut builder = self.query.ast.clone().full();
46        let (select, mut fields) = builder.build_select(true, vec![expr]);
47
48        let conds = builder.forwarded.into_iter().map(|x| x.1.1).collect();
49
50        Aggr {
51            _p2: PhantomData,
52            select: Rc::new(select),
53            field: {
54                debug_assert_eq!(fields.len(), 1);
55                fields.swap_remove(0)
56            },
57            conds,
58        }
59    }
60
61    /// Filter the rows of this sub-query based on a value from the outer query.
62    #[deprecated = "Please use `Rows::filter` instead"]
63    pub fn filter_on<T: EqTyp + 'static>(
64        &mut self,
65        val: impl IntoExpr<'inner, S, Typ = T>,
66        on: impl IntoExpr<'outer, S, Typ = T>,
67    ) {
68        let on = on.into_expr();
69        self.filter(val.into_expr().eq(on))
70    }
71
72    /// Return the average value in a column, this is [None] if there are zero rows.
73    pub fn avg(&self, val: impl IntoExpr<'inner, S, Typ = f64>) -> Expr<'outer, S, Option<f64>> {
74        let val = val.into_expr().inner;
75        Expr::new(self.select(move |b| Func::avg(val.build_expr(b)).into()))
76    }
77
78    /// Return the maximum value in a column, this is [None] if there are zero rows.
79    pub fn max<T>(&self, val: impl IntoExpr<'inner, S, Typ = T>) -> Expr<'outer, S, Option<T>>
80    where
81        T: NumTyp,
82    {
83        let val = val.into_expr().inner;
84        Expr::new(self.select(move |b| Func::max(val.build_expr(b)).into()))
85    }
86
87    /// Return the minimum value in a column, this is [None] if there are zero rows.
88    pub fn min<T>(&self, val: impl IntoExpr<'inner, S, Typ = T>) -> Expr<'outer, S, Option<T>>
89    where
90        T: NumTyp,
91    {
92        let val = val.into_expr().inner;
93        Expr::new(self.select(move |b| Func::min(val.build_expr(b)).into()))
94    }
95
96    /// Return the sum of a column.
97    pub fn sum<T>(&self, val: impl IntoExpr<'inner, S, Typ = T>) -> Expr<'outer, S, T>
98    where
99        T: NumTyp,
100    {
101        let val = val.into_expr().inner;
102        let val = self.select::<T>(move |b| Func::sum(val.build_expr(b)).into());
103
104        Expr::adhoc(move |b| {
105            sea_query::Expr::expr(val.build_expr(b))
106                .if_null(SimpleExpr::Constant(T::ZERO.into_sea_value()))
107        })
108    }
109
110    /// Return the number of distinct values in a column.
111    pub fn count_distinct<T: EqTyp + 'static>(
112        &self,
113        val: impl IntoExpr<'inner, S, Typ = T>,
114    ) -> Expr<'outer, S, i64> {
115        let val = val.into_expr().inner;
116        let val = self.select::<i64>(move |b| Func::count_distinct(val.build_expr(b)).into());
117        Expr::adhoc(move |b| {
118            sea_query::Expr::expr(val.build_expr(b))
119                .if_null(SimpleExpr::Constant(0i64.into_sea_value()))
120        })
121    }
122
123    /// Return whether there are any rows.
124    pub fn exists(&self) -> Expr<'outer, S, bool> {
125        let val = self.select::<i64>(|_| SimpleExpr::Constant(1.into_sea_value()));
126        Expr::adhoc(move |b| sea_query::Expr::expr(val.build_expr(b)).is_not_null())
127    }
128}
129
130pub struct Aggr<S, T> {
131    pub(crate) _p2: PhantomData<(S, T)>,
132    pub(crate) select: Rc<SelectStatement>,
133    pub(crate) conds: Vec<DynTypedExpr>,
134    pub(crate) field: MyAlias,
135}
136
137impl<S, T> Clone for Aggr<S, T> {
138    fn clone(&self) -> Self {
139        Self {
140            _p2: PhantomData,
141            select: self.select.clone(),
142            conds: self.conds.clone(),
143            field: self.field,
144        }
145    }
146}
147
148impl<S, T: MyTyp> Typed for Aggr<S, T> {
149    type Typ = T;
150    fn build_expr(&self, b: &mut ValueBuilder) -> SimpleExpr {
151        sea_query::Expr::col((self.build_table(b), self.field)).into()
152    }
153}
154
155impl<S, T> Aggr<S, T> {
156    fn build_table(&self, b: &mut ValueBuilder) -> MyAlias {
157        let conds = self.conds.iter().map(|expr| (expr.0)(b)).collect();
158        b.get_aggr(self.select.clone(), conds)
159    }
160}
161
162impl<S, T: Table> Deref for Aggr<S, T> {
163    type Target = T::Ext<Self>;
164
165    fn deref(&self) -> &Self::Target {
166        RefCast::ref_cast(self)
167    }
168}
169
170/// Perform an aggregate that returns a single result for each of the current rows.
171///
172/// You can filter the rows in the aggregate based on values from the outer query.
173/// That is the only way to get a different aggregate for each outer row.
174///
175/// ```
176/// # use rust_query::aggregate;
177/// # use rust_query::private::doctest::*;
178/// # let mut client = get_client();
179/// # let txn = get_txn(&mut client);
180/// let res = txn.query_one(aggregate(|rows| {
181///     let user = rows.join(User);
182///     rows.count_distinct(user)
183/// }));
184/// assert_eq!(res, 1, "there is one user in the database");
185/// ```
186pub fn aggregate<'outer, S, F, R>(f: F) -> R
187where
188    F: for<'inner> FnOnce(&mut Aggregate<'outer, 'inner, S>) -> R,
189{
190    let inner = Rows {
191        phantom: PhantomData,
192        ast: Default::default(),
193        _p: PhantomData,
194    };
195    let mut group = Aggregate {
196        query: inner,
197        _p: PhantomData,
198    };
199    f(&mut group)
200}