diff --git a/examples/sqlx-mysql/src/main.rs b/examples/sqlx-mysql/src/main.rs index 8e1d859c..f6c72d73 100644 --- a/examples/sqlx-mysql/src/main.rs +++ b/examples/sqlx-mysql/src/main.rs @@ -104,22 +104,12 @@ async fn count_fruits_by_cake(db: &Database) -> Result<(), QueryErr> { print!("count fruits by cake: "); - let mut select = cake::Entity::find().left_join(cake::Relation::Fruit); - { - use sea_orm::sea_query::*; - type Cake = cake::Column; - type Fruit = fruit::Column; - - select - .query() - .clear_selects() - .column((cake::Entity, Cake::Name)) - .expr_as( - Expr::tbl(fruit::Entity, Fruit::Id).count(), - Alias::new("num_of_fruits"), - ) - .group_by_col((cake::Entity, Cake::Name)); - } + let select = cake::Entity::find() + .left_join(cake::Relation::Fruit) + .clear_selects() + .column(cake::Column::Name) + .expr_as(fruit::Column::Id.count(), "num_of_fruits") + .group_by(cake::Column::Name); let results = select.into_model::().all(db).await?; diff --git a/src/entity/column.rs b/src/entity/column.rs index 93171cb4..6cdbbd4f 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -15,6 +15,27 @@ macro_rules! bind_oper { }; } +macro_rules! bind_agg_func { + ( $func: ident ) => { + fn $func(&self) -> SimpleExpr { + Expr::tbl(self.entity_name(), *self).$func() + } + }; +} + +macro_rules! bind_vec_func { + ( $func: ident ) => { + #[allow(clippy::wrong_self_convention)] + fn $func(&self, v: I) -> SimpleExpr + where + V: Into, + I: IntoIterator, + { + Expr::tbl(self.entity_name(), *self).$func(v) + } + }; +} + pub trait ColumnTrait: IdenStatic { type EntityName: EntityName; @@ -24,6 +45,10 @@ pub trait ColumnTrait: IdenStatic { Rc::new(Self::EntityName::default()) as Rc } + fn as_column_ref(&self) -> (Rc, Rc) { + (self.entity_name(), Rc::new(*self) as Rc) + } + bind_oper!(eq); bind_oper!(ne); bind_oper!(gt); @@ -144,4 +169,19 @@ pub trait ColumnTrait: IdenStatic { let pattern = format!("%{}%", s); Expr::tbl(self.entity_name(), *self).like(&pattern) } -} + + bind_agg_func!(max); + bind_agg_func!(min); + bind_agg_func!(sum); + bind_agg_func!(count); + + fn if_null(&self, v: V) -> SimpleExpr + where + V: Into, + { + Expr::tbl(self.entity_name(), *self).if_null(v) + } + + bind_vec_func!(is_in); + bind_vec_func!(is_not_in); +} \ No newline at end of file diff --git a/src/query/select.rs b/src/query/select.rs index 4dd0439b..0f5d6019 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -1,11 +1,13 @@ use crate::{ - EntityTrait, Identity, Iterable, PrimaryKeyOfModel, Related, RelationDef, RelationTrait, - Statement, + ColumnTrait, EntityTrait, Identity, Iterable, ModelTrait, PrimaryKeyOfModel, Related, + RelationDef, RelationTrait, Statement, }; use core::fmt::Debug; use core::marker::PhantomData; pub use sea_query::JoinType; -use sea_query::{Expr, Iden, IntoIden, Order, QueryBuilder, SelectStatement, SimpleExpr}; +use sea_query::{ + Alias, Expr, Iden, IntoIden, Order, QueryBuilder, SelectExpr, SelectStatement, SimpleExpr, +}; use std::rc::Rc; #[derive(Clone, Debug)] @@ -74,6 +76,72 @@ where self } + pub fn clear_selects(mut self) -> Self { + self.query.clear_selects(); + self + } + + /// ``` + /// use sea_orm::{ColumnTrait, EntityTrait, tests_cfg::cake, sea_query::PostgresQueryBuilder}; + /// + /// assert_eq!( + /// cake::Entity::find() + /// .clear_selects() + /// .column(cake::Column::Name) + /// .build(PostgresQueryBuilder) + /// .to_string(), + /// r#"SELECT "cake"."name" FROM "cake""# + /// ); + /// ``` + pub fn column(mut self, col: C) -> Self + where + C: ColumnTrait, + { + self.query.column(col.as_column_ref()); + self + } + + /// ``` + /// use sea_orm::{ColumnTrait, EntityTrait, tests_cfg::cake, sea_query::PostgresQueryBuilder}; + /// + /// assert_eq!( + /// cake::Entity::find() + /// .clear_selects() + /// .column(cake::Column::Name) + /// .group_by(cake::Column::Name) + /// .build(PostgresQueryBuilder) + /// .to_string(), + /// r#"SELECT "cake"."name" FROM "cake" GROUP BY "cake"."name""# + /// ); + /// ``` + pub fn group_by(mut self, col: C) -> Self + where + C: ColumnTrait, + { + self.query.group_by_col(col.as_column_ref()); + self + } + + /// ``` + /// use sea_orm::{ColumnTrait, EntityTrait, tests_cfg::cake, sea_query::PostgresQueryBuilder}; + /// + /// assert_eq!( + /// cake::Entity::find() + /// .clear_selects() + /// .expr_as(cake::Column::Id.count(), "count") + /// .build(PostgresQueryBuilder) + /// .to_string(), + /// r#"SELECT COUNT("cake"."id") AS "count" FROM "cake""# + /// ); + /// ``` + pub fn expr_as(mut self, expr: SimpleExpr, alias: &str) -> Self { + self.query.expr(SelectExpr { + expr, + alias: Some(Rc::new(Alias::new(alias))), + }); + self + } + /// ``` /// use sea_orm::{ColumnTrait, EntityTrait, tests_cfg::cake, sea_query::MysqlQueryBuilder}; /// @@ -95,8 +163,6 @@ where R: EntityTrait + Related, R::PrimaryKey: PrimaryKeyOfModel, { - use crate::{ColumnTrait, ModelTrait}; - if let Some(key) = R::PrimaryKey::iter().next() { // TODO: supporting composite primary key let col = key.into_column();