Skip to main content

sz_orm_core/
partial_model.rs

1//! Partial Models — 部分字段选择与聚合查询(P-F-3, v2.1.0)
2//!
3//! 提供 `select_only()` 进入部分选择模式,支持 `.column(C)` / `.column_as(Expr, alias)`
4//! / `.group_by(C)`,追平 SeaORM `select_only()` 性能优化能力。
5//!
6//! # 设计
7//!
8//! - `SelectMode` 枚举控制 `QueryBuilder` 的 SELECT 行为(All / Partial)
9//! - `ColumnTrait` trait 提供类型化列引用,编译期拒绝字符串
10//! - `Expr` 结构体表达聚合函数(COUNT / SUM / AVG / MAX / MIN)
11//!
12//! # 用法
13//!
14//! ```ignore
15//! use sz_orm_core::partial_model::{select_only, Expr, AggFunc};
16//!
17//! // SELECT id, name FROM users
18//! let sql = User::find()
19//!     .select_only()
20//!     .column("id")
21//!     .column("name")
22//!     .build_select();
23//!
24//! // SELECT COUNT(id) AS count FROM users
25//! let sql = User::find()
26//!     .select_only()
27//!     .column_as(Expr::count("id"), "count")
28//!     .build_select();
29//! ```
30
31/// 聚合函数类型
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum AggFunc {
34    /// COUNT(col)
35    Count,
36    /// SUM(col)
37    Sum,
38    /// AVG(col)
39    Avg,
40    /// MAX(col)
41    Max,
42    /// MIN(col)
43    Min,
44}
45
46impl AggFunc {
47    /// 转换为 SQL 函数名
48    pub fn as_sql(self) -> &'static str {
49        match self {
50            AggFunc::Count => "COUNT",
51            AggFunc::Sum => "SUM",
52            AggFunc::Avg => "AVG",
53            AggFunc::Max => "MAX",
54            AggFunc::Min => "MIN",
55        }
56    }
57}
58
59/// 聚合表达式
60///
61/// 表达 `FUNC(column)` 形式的聚合表达式,可通过 `column_as` 添加别名。
62#[derive(Debug, Clone)]
63pub struct Expr {
64    func: AggFunc,
65    column: String,
66}
67
68impl Expr {
69    /// 创建 COUNT(col) 表达式
70    pub fn count(column: impl Into<String>) -> Self {
71        Self {
72            func: AggFunc::Count,
73            column: column.into(),
74        }
75    }
76
77    /// 创建 SUM(col) 表达式
78    pub fn sum(column: impl Into<String>) -> Self {
79        Self {
80            func: AggFunc::Sum,
81            column: column.into(),
82        }
83    }
84
85    /// 创建 AVG(col) 表达式
86    pub fn avg(column: impl Into<String>) -> Self {
87        Self {
88            func: AggFunc::Avg,
89            column: column.into(),
90        }
91    }
92
93    /// 创建 MAX(col) 表达式
94    pub fn max(column: impl Into<String>) -> Self {
95        Self {
96            func: AggFunc::Max,
97            column: column.into(),
98        }
99    }
100
101    /// 创建 MIN(col) 表达式
102    pub fn min(column: impl Into<String>) -> Self {
103        Self {
104            func: AggFunc::Min,
105            column: column.into(),
106        }
107    }
108
109    /// 渲染为 SQL 片段(不含别名)
110    pub fn render(&self) -> String {
111        format!("{}({})", self.func.as_sql(), self.column)
112    }
113
114    /// 渲染为带别名的 SQL 片段
115    pub fn render_as(&self, alias: &str) -> String {
116        format!("{}({}) AS {}", self.func.as_sql(), self.column, alias)
117    }
118}
119
120/// SELECT 模式
121#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
122pub enum SelectMode {
123    /// SELECT *(默认)
124    #[default]
125    All,
126    /// SELECT col1, col2, ...(部分选择)
127    Partial,
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn test_agg_func_as_sql() {
136        assert_eq!(AggFunc::Count.as_sql(), "COUNT");
137        assert_eq!(AggFunc::Sum.as_sql(), "SUM");
138        assert_eq!(AggFunc::Avg.as_sql(), "AVG");
139        assert_eq!(AggFunc::Max.as_sql(), "MAX");
140        assert_eq!(AggFunc::Min.as_sql(), "MIN");
141    }
142
143    #[test]
144    fn test_expr_count() {
145        let expr = Expr::count("id");
146        assert_eq!(expr.render(), "COUNT(id)");
147        assert_eq!(expr.render_as("total"), "COUNT(id) AS total");
148    }
149
150    #[test]
151    fn test_expr_sum() {
152        let expr = Expr::sum("amount");
153        assert_eq!(expr.render(), "SUM(amount)");
154        assert_eq!(
155            expr.render_as("total_amount"),
156            "SUM(amount) AS total_amount"
157        );
158    }
159
160    #[test]
161    fn test_expr_avg() {
162        let expr = Expr::avg("score");
163        assert_eq!(expr.render(), "AVG(score)");
164    }
165
166    #[test]
167    fn test_expr_max() {
168        let expr = Expr::max("price");
169        assert_eq!(expr.render(), "MAX(price)");
170    }
171
172    #[test]
173    fn test_expr_min() {
174        let expr = Expr::min("price");
175        assert_eq!(expr.render(), "MIN(price)");
176    }
177
178    #[test]
179    fn test_select_mode_default() {
180        assert_eq!(SelectMode::default(), SelectMode::All);
181    }
182}