icydb_core/db/query/builder/aggregate/
expr.rs1use crate::db::query::plan::{AggregateKind, AggregateShape, expr::Expr};
2
3#[derive(Clone, Debug, Eq, PartialEq)]
13pub struct AggregateExpr {
14 shape: AggregateShape,
15}
16
17impl AggregateExpr {
18 const fn terminal(kind: AggregateKind) -> Self {
20 Self {
21 shape: AggregateShape::terminal(kind),
22 }
23 }
24
25 fn field_target(kind: AggregateKind, field: impl Into<String>) -> Self {
27 Self {
28 shape: AggregateShape::field_target(kind, field.into()),
29 }
30 }
31
32 pub(in crate::db) fn from_expression_input(kind: AggregateKind, input_expr: Expr) -> Self {
34 Self {
35 shape: AggregateShape::from_expression_input(kind, input_expr),
36 }
37 }
38
39 #[must_use]
41 pub(in crate::db) const fn from_shape(shape: AggregateShape) -> Self {
42 Self { shape }
43 }
44
45 #[must_use]
47 pub(in crate::db) const fn shape(&self) -> &AggregateShape {
48 &self.shape
49 }
50
51 #[must_use]
53 pub(in crate::db) fn with_filter_expr(mut self, filter_expr: Expr) -> Self {
54 self.shape = self.shape.with_filter_expr(filter_expr);
55 self
56 }
57
58 #[must_use]
60 pub const fn distinct(mut self) -> Self {
61 self.shape.set_raw_distinct(true);
62 self
63 }
64
65 #[must_use]
67 pub(in crate::db) const fn kind(&self) -> AggregateKind {
68 self.shape.kind()
69 }
70
71 #[must_use]
73 pub(in crate::db) fn input_expr(&self) -> Option<&Expr> {
74 self.shape.input_expr()
75 }
76
77 #[must_use]
79 pub(in crate::db) fn filter_expr(&self) -> Option<&Expr> {
80 self.shape.filter_expr()
81 }
82
83 #[must_use]
85 pub(in crate::db) fn target_field(&self) -> Option<&str> {
86 match self.input_expr() {
87 Some(Expr::Field(field)) => Some(field.as_str()),
88 _ => None,
89 }
90 }
91
92 #[must_use]
94 pub(in crate::db) const fn is_distinct(&self) -> bool {
95 self.shape.raw_distinct()
96 }
97
98 #[cfg(test)]
100 #[must_use]
101 pub(in crate::db) fn terminal_for_kind(kind: AggregateKind) -> Self {
102 match kind {
103 AggregateKind::Count => count(),
104 AggregateKind::Exists => exists(),
105 AggregateKind::Min => min(),
106 AggregateKind::Max => max(),
107 AggregateKind::First => first(),
108 AggregateKind::Last => last(),
109 AggregateKind::Sum | AggregateKind::Avg => unreachable!(
110 "AggregateExpr::terminal_for_kind does not support SUM/AVG field-target kinds"
111 ),
112 }
113 }
114}
115
116#[must_use]
118pub const fn count() -> AggregateExpr {
119 AggregateExpr::terminal(AggregateKind::Count)
120}
121
122#[must_use]
124pub fn count_by(field: impl AsRef<str>) -> AggregateExpr {
125 AggregateExpr::field_target(AggregateKind::Count, field.as_ref().to_string())
126}
127
128#[must_use]
130pub fn sum(field: impl AsRef<str>) -> AggregateExpr {
131 AggregateExpr::field_target(AggregateKind::Sum, field.as_ref().to_string())
132}
133
134#[must_use]
136pub fn avg(field: impl AsRef<str>) -> AggregateExpr {
137 AggregateExpr::field_target(AggregateKind::Avg, field.as_ref().to_string())
138}
139
140#[must_use]
142pub const fn exists() -> AggregateExpr {
143 AggregateExpr::terminal(AggregateKind::Exists)
144}
145
146#[must_use]
148pub const fn first() -> AggregateExpr {
149 AggregateExpr::terminal(AggregateKind::First)
150}
151
152#[must_use]
154pub const fn last() -> AggregateExpr {
155 AggregateExpr::terminal(AggregateKind::Last)
156}
157
158#[must_use]
160pub const fn min() -> AggregateExpr {
161 AggregateExpr::terminal(AggregateKind::Min)
162}
163
164#[must_use]
166pub fn min_by(field: impl AsRef<str>) -> AggregateExpr {
167 AggregateExpr::field_target(AggregateKind::Min, field.as_ref().to_string())
168}
169
170#[must_use]
172pub const fn max() -> AggregateExpr {
173 AggregateExpr::terminal(AggregateKind::Max)
174}
175
176#[must_use]
178pub fn max_by(field: impl AsRef<str>) -> AggregateExpr {
179 AggregateExpr::field_target(AggregateKind::Max, field.as_ref().to_string())
180}