icydb_core/db/query/builder/aggregate/
expr.rs1use crate::db::query::plan::{
2 AggregateKind,
3 expr::{Expr, FieldId, canonicalize_aggregate_input_expr},
4};
5
6#[derive(Clone, Debug, Eq, PartialEq)]
16pub struct AggregateExpr {
17 kind: AggregateKind,
18 input_expr: Option<Box<Expr>>,
19 filter_expr: Option<Box<Expr>>,
20 distinct: bool,
21}
22
23impl AggregateExpr {
24 const fn terminal(kind: AggregateKind) -> Self {
26 Self {
27 kind,
28 input_expr: None,
29 filter_expr: None,
30 distinct: false,
31 }
32 }
33
34 fn field_target(kind: AggregateKind, field: impl Into<String>) -> Self {
36 Self {
37 kind,
38 input_expr: Some(Box::new(Expr::Field(FieldId::new(field.into())))),
39 filter_expr: None,
40 distinct: false,
41 }
42 }
43
44 pub(in crate::db) fn from_expression_input(kind: AggregateKind, input_expr: Expr) -> Self {
46 Self {
47 kind,
48 input_expr: Some(Box::new(canonicalize_aggregate_input_expr(
49 kind, input_expr,
50 ))),
51 filter_expr: None,
52 distinct: false,
53 }
54 }
55
56 #[must_use]
58 pub(in crate::db) fn with_filter_expr(mut self, filter_expr: Expr) -> Self {
59 self.filter_expr = Some(Box::new(filter_expr));
60 self
61 }
62
63 #[must_use]
65 pub const fn distinct(mut self) -> Self {
66 self.distinct = true;
67 self
68 }
69
70 #[must_use]
72 pub(in crate::db) const fn kind(&self) -> AggregateKind {
73 self.kind
74 }
75
76 #[must_use]
78 pub(in crate::db) fn input_expr(&self) -> Option<&Expr> {
79 self.input_expr.as_deref()
80 }
81
82 #[must_use]
84 pub(in crate::db) fn filter_expr(&self) -> Option<&Expr> {
85 self.filter_expr.as_deref()
86 }
87
88 #[must_use]
90 pub(in crate::db) fn target_field(&self) -> Option<&str> {
91 match self.input_expr() {
92 Some(Expr::Field(field)) => Some(field.as_str()),
93 _ => None,
94 }
95 }
96
97 #[must_use]
99 pub(in crate::db) const fn is_distinct(&self) -> bool {
100 self.distinct
101 }
102
103 pub(in crate::db::query) fn from_semantic_parts(
105 kind: AggregateKind,
106 target_field: Option<String>,
107 distinct: bool,
108 ) -> Self {
109 Self {
110 kind,
111 input_expr: target_field.map(|field| Box::new(Expr::Field(FieldId::new(field)))),
112 filter_expr: None,
113 distinct,
114 }
115 }
116
117 #[cfg(test)]
119 #[must_use]
120 pub(in crate::db) fn terminal_for_kind(kind: AggregateKind) -> Self {
121 match kind {
122 AggregateKind::Count => count(),
123 AggregateKind::Exists => exists(),
124 AggregateKind::Min => min(),
125 AggregateKind::Max => max(),
126 AggregateKind::First => first(),
127 AggregateKind::Last => last(),
128 AggregateKind::Sum | AggregateKind::Avg => unreachable!(
129 "AggregateExpr::terminal_for_kind does not support SUM/AVG field-target kinds"
130 ),
131 }
132 }
133}
134
135#[must_use]
137pub const fn count() -> AggregateExpr {
138 AggregateExpr::terminal(AggregateKind::Count)
139}
140
141#[must_use]
143pub fn count_by(field: impl AsRef<str>) -> AggregateExpr {
144 AggregateExpr::field_target(AggregateKind::Count, field.as_ref().to_string())
145}
146
147#[must_use]
149pub fn sum(field: impl AsRef<str>) -> AggregateExpr {
150 AggregateExpr::field_target(AggregateKind::Sum, field.as_ref().to_string())
151}
152
153#[must_use]
155pub fn avg(field: impl AsRef<str>) -> AggregateExpr {
156 AggregateExpr::field_target(AggregateKind::Avg, field.as_ref().to_string())
157}
158
159#[must_use]
161pub const fn exists() -> AggregateExpr {
162 AggregateExpr::terminal(AggregateKind::Exists)
163}
164
165#[must_use]
167pub const fn first() -> AggregateExpr {
168 AggregateExpr::terminal(AggregateKind::First)
169}
170
171#[must_use]
173pub const fn last() -> AggregateExpr {
174 AggregateExpr::terminal(AggregateKind::Last)
175}
176
177#[must_use]
179pub const fn min() -> AggregateExpr {
180 AggregateExpr::terminal(AggregateKind::Min)
181}
182
183#[must_use]
185pub fn min_by(field: impl AsRef<str>) -> AggregateExpr {
186 AggregateExpr::field_target(AggregateKind::Min, field.as_ref().to_string())
187}
188
189#[must_use]
191pub const fn max() -> AggregateExpr {
192 AggregateExpr::terminal(AggregateKind::Max)
193}
194
195#[must_use]
197pub fn max_by(field: impl AsRef<str>) -> AggregateExpr {
198 AggregateExpr::field_target(AggregateKind::Max, field.as_ref().to_string())
199}