1use crate::expr::{Expr, TableRef};
2use crate::join::Join;
3use crate::order::SortDirection;
4use crate::pagination::Pagination;
5use crate::predicate::Predicate;
6use sql_orm_core::{Entity, EntityColumn};
7
8#[derive(Debug, Clone, PartialEq)]
9pub enum AggregateExpr {
10 GroupKey(Expr),
11 CountAll,
12 Count(Expr),
13 Sum(Expr),
14 Avg(Expr),
15 Min(Expr),
16 Max(Expr),
17}
18
19impl AggregateExpr {
20 pub fn group_key(expr: impl Into<Expr>) -> Self {
21 Self::GroupKey(expr.into())
22 }
23
24 pub const fn count_all() -> Self {
25 Self::CountAll
26 }
27
28 pub fn count(expr: impl Into<Expr>) -> Self {
29 Self::Count(expr.into())
30 }
31
32 pub fn sum(expr: impl Into<Expr>) -> Self {
33 Self::Sum(expr.into())
34 }
35
36 pub fn avg(expr: impl Into<Expr>) -> Self {
37 Self::Avg(expr.into())
38 }
39
40 pub fn min(expr: impl Into<Expr>) -> Self {
41 Self::Min(expr.into())
42 }
43
44 pub fn max(expr: impl Into<Expr>) -> Self {
45 Self::Max(expr.into())
46 }
47}
48
49#[derive(Debug, Clone, PartialEq)]
50pub struct AggregateProjection {
51 pub expr: AggregateExpr,
52 pub alias: &'static str,
53}
54
55impl AggregateProjection {
56 pub fn group_key<E: Entity>(column: EntityColumn<E>) -> Self {
57 let alias = column.column_name();
58 Self {
59 expr: AggregateExpr::GroupKey(Expr::from(column)),
60 alias,
61 }
62 }
63
64 pub fn group_key_as(expr: impl Into<Expr>, alias: &'static str) -> Self {
65 Self {
66 expr: AggregateExpr::GroupKey(expr.into()),
67 alias,
68 }
69 }
70
71 pub const fn expr_as(expr: AggregateExpr, alias: &'static str) -> Self {
72 Self { expr, alias }
73 }
74
75 pub fn count_as(alias: &'static str) -> Self {
76 Self::expr_as(AggregateExpr::CountAll, alias)
77 }
78
79 pub fn sum_as(expr: impl Into<Expr>, alias: &'static str) -> Self {
80 Self::expr_as(AggregateExpr::sum(expr), alias)
81 }
82
83 pub fn avg_as(expr: impl Into<Expr>, alias: &'static str) -> Self {
84 Self::expr_as(AggregateExpr::avg(expr), alias)
85 }
86
87 pub fn min_as(expr: impl Into<Expr>, alias: &'static str) -> Self {
88 Self::expr_as(AggregateExpr::min(expr), alias)
89 }
90
91 pub fn max_as(expr: impl Into<Expr>, alias: &'static str) -> Self {
92 Self::expr_as(AggregateExpr::max(expr), alias)
93 }
94}
95
96#[derive(Debug, Clone, PartialEq)]
97pub enum AggregatePredicate {
98 Eq(AggregateExpr, Expr),
99 Ne(AggregateExpr, Expr),
100 Gt(AggregateExpr, Expr),
101 Gte(AggregateExpr, Expr),
102 Lt(AggregateExpr, Expr),
103 Lte(AggregateExpr, Expr),
104 And(Vec<AggregatePredicate>),
105 Or(Vec<AggregatePredicate>),
106 Not(Box<AggregatePredicate>),
107}
108
109impl AggregatePredicate {
110 pub fn eq(left: AggregateExpr, right: impl Into<Expr>) -> Self {
111 Self::Eq(left, right.into())
112 }
113
114 pub fn ne(left: AggregateExpr, right: impl Into<Expr>) -> Self {
115 Self::Ne(left, right.into())
116 }
117
118 pub fn gt(left: AggregateExpr, right: impl Into<Expr>) -> Self {
119 Self::Gt(left, right.into())
120 }
121
122 pub fn gte(left: AggregateExpr, right: impl Into<Expr>) -> Self {
123 Self::Gte(left, right.into())
124 }
125
126 pub fn lt(left: AggregateExpr, right: impl Into<Expr>) -> Self {
127 Self::Lt(left, right.into())
128 }
129
130 pub fn lte(left: AggregateExpr, right: impl Into<Expr>) -> Self {
131 Self::Lte(left, right.into())
132 }
133
134 pub fn and(predicates: Vec<AggregatePredicate>) -> Self {
135 Self::And(predicates)
136 }
137
138 pub fn or(predicates: Vec<AggregatePredicate>) -> Self {
139 Self::Or(predicates)
140 }
141
142 pub fn negate(predicate: AggregatePredicate) -> Self {
143 Self::Not(Box::new(predicate))
144 }
145}
146
147#[derive(Debug, Clone, PartialEq)]
148pub struct AggregateOrderBy {
149 pub expr: AggregateExpr,
150 pub direction: SortDirection,
151}
152
153impl AggregateOrderBy {
154 pub fn new(expr: AggregateExpr, direction: SortDirection) -> Self {
155 Self { expr, direction }
156 }
157
158 pub fn asc(expr: AggregateExpr) -> Self {
159 Self::new(expr, SortDirection::Asc)
160 }
161
162 pub fn desc(expr: AggregateExpr) -> Self {
163 Self::new(expr, SortDirection::Desc)
164 }
165}
166
167#[derive(Debug, Clone, PartialEq)]
168pub struct AggregateQuery {
169 pub from: TableRef,
170 pub joins: Vec<Join>,
171 pub projection: Vec<AggregateProjection>,
172 pub predicate: Option<Predicate>,
173 pub group_by: Vec<Expr>,
174 pub having: Option<AggregatePredicate>,
175 pub order_by: Vec<AggregateOrderBy>,
176 pub pagination: Option<Pagination>,
177}
178
179impl AggregateQuery {
180 pub fn from_entity<E: Entity>() -> Self {
181 Self {
182 from: TableRef::for_entity::<E>(),
183 joins: Vec::new(),
184 projection: Vec::new(),
185 predicate: None,
186 group_by: Vec::new(),
187 having: None,
188 order_by: Vec::new(),
189 pagination: None,
190 }
191 }
192
193 pub fn from_entity_as<E: Entity>(alias: &'static str) -> Self {
194 Self {
195 from: TableRef::for_entity_as::<E>(alias),
196 joins: Vec::new(),
197 projection: Vec::new(),
198 predicate: None,
199 group_by: Vec::new(),
200 having: None,
201 order_by: Vec::new(),
202 pagination: None,
203 }
204 }
205
206 pub fn project<P, I>(mut self, projection: I) -> Self
207 where
208 P: Into<AggregateProjection>,
209 I: IntoIterator<Item = P>,
210 {
211 self.projection = projection.into_iter().map(Into::into).collect();
212 self
213 }
214
215 pub fn group_by<G, I>(mut self, group_by: I) -> Self
216 where
217 G: Into<Expr>,
218 I: IntoIterator<Item = G>,
219 {
220 self.group_by = group_by.into_iter().map(Into::into).collect();
221 self
222 }
223
224 pub fn filter(mut self, predicate: Predicate) -> Self {
225 self.predicate = Some(match self.predicate.take() {
226 Some(existing) => Predicate::and(vec![existing, predicate]),
227 None => predicate,
228 });
229 self
230 }
231
232 pub fn join(mut self, join: Join) -> Self {
233 self.joins.push(join);
234 self
235 }
236
237 pub fn inner_join<E: Entity>(self, on: Predicate) -> Self {
238 self.join(Join::inner_entity::<E>(on))
239 }
240
241 pub fn left_join<E: Entity>(self, on: Predicate) -> Self {
242 self.join(Join::left_entity::<E>(on))
243 }
244
245 pub fn inner_join_as<E: Entity>(self, alias: &'static str, on: Predicate) -> Self {
246 self.join(Join::inner_entity_as::<E>(alias, on))
247 }
248
249 pub fn left_join_as<E: Entity>(self, alias: &'static str, on: Predicate) -> Self {
250 self.join(Join::left_entity_as::<E>(alias, on))
251 }
252
253 pub fn having(mut self, predicate: AggregatePredicate) -> Self {
254 self.having = Some(match self.having.take() {
255 Some(existing) => AggregatePredicate::and(vec![existing, predicate]),
256 None => predicate,
257 });
258 self
259 }
260
261 pub fn order_by(mut self, order: AggregateOrderBy) -> Self {
262 self.order_by.push(order);
263 self
264 }
265
266 pub fn paginate(mut self, pagination: Pagination) -> Self {
267 self.pagination = Some(pagination);
268 self
269 }
270}