From 8e4271da79ccd90d2d15faa2912da5c55a88ba1d Mon Sep 17 00:00:00 2001 From: Chris Tsang Date: Sat, 8 May 2021 16:22:28 +0800 Subject: [PATCH] Select filters --- src/entity/base.rs | 18 +++++++++- src/entity/column.rs | 69 +++++++++++++++++++++++++++++++++++-- src/entity/identity.rs | 8 +++++ src/lib.rs | 3 +- src/query/select.rs | 78 ++++++++++++++++++++++++++++++++++++++---- src/tests_cfg/cake.rs | 12 ++++--- 6 files changed, 172 insertions(+), 16 deletions(-) diff --git a/src/entity/base.rs b/src/entity/base.rs index 05863723..37c3ec35 100644 --- a/src/entity/base.rs +++ b/src/entity/base.rs @@ -1,6 +1,6 @@ use super::{ColumnTrait, Identity, ModelTrait, RelationTrait}; use crate::Select; -use sea_query::Iden; +use sea_query::{Expr, Iden, Value}; use std::fmt::Debug; pub use strum::IntoEnumIterator as Iterable; @@ -20,4 +20,20 @@ pub trait EntityTrait: Iden + Default + Debug { fn find<'s>() -> Select<'s, Self> { Select::new(Self::default()) } + + fn find_one<'s>() -> Select<'s, Self> { + let mut select = Self::find(); + select.query().limit(1); + select + } + + fn find_one_by<'s, V>(v: V) -> Select<'s, Self> + where + V: Into, + { + let select = Self::find_one(); + let select = + select.filter(Expr::tbl(Self::default(), Self::primary_key().into_iden()).eq(v)); + select + } } diff --git a/src/entity/column.rs b/src/entity/column.rs index 71f2b701..d7758665 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -1,6 +1,69 @@ +use crate::EntityTrait; pub use sea_query::ColumnType; -use sea_query::Iden; +use sea_query::{Expr, Iden, SimpleExpr, Value}; +use std::rc::Rc; -pub trait ColumnTrait: Iden { - fn col_type(&self) -> ColumnType; +macro_rules! bind_oper { + ( $op: ident ) => { + fn $op(&'static self, v: V) -> SimpleExpr + where + V: Into, + { + Expr::tbl(self.entity_iden(), *self).$op(v) + } + }; +} + +pub trait ColumnTrait: Iden + Copy { + type Entity: EntityTrait; + + fn col_type(&self) -> ColumnType; + + fn entity_iden(&'static self) -> Rc { + Rc::new(Self::Entity::default()) as Rc + } + + bind_oper!(eq); + bind_oper!(ne); + bind_oper!(gt); + bind_oper!(gte); + bind_oper!(lt); + bind_oper!(lte); + + fn between(&'static self, a: V, b: V) -> SimpleExpr + where + V: Into, + { + Expr::tbl(self.entity_iden(), *self).between(a, b) + } + + fn not_between(&'static self, a: V, b: V) -> SimpleExpr + where + V: Into, + { + Expr::tbl(self.entity_iden(), *self).not_between(a, b) + } + + fn like(&'static self, s: &str) -> SimpleExpr { + Expr::tbl(self.entity_iden(), *self).like(s) + } + + fn not_like(&'static self, s: &str) -> SimpleExpr { + Expr::tbl(self.entity_iden(), *self).not_like(s) + } + + fn starts_with(&'static self, s: &str) -> SimpleExpr { + let pattern = format!("{}%", s); + Expr::tbl(self.entity_iden(), *self).like(&pattern) + } + + fn ends_with(&'static self, s: &str) -> SimpleExpr { + let pattern = format!("%{}", s); + Expr::tbl(self.entity_iden(), *self).like(&pattern) + } + + fn contains(&'static self, s: &str) -> SimpleExpr { + let pattern = format!("%{}%", s); + Expr::tbl(self.entity_iden(), *self).like(&pattern) + } } diff --git a/src/entity/identity.rs b/src/entity/identity.rs index 72848cd9..303956aa 100644 --- a/src/entity/identity.rs +++ b/src/entity/identity.rs @@ -12,6 +12,14 @@ pub trait IntoIdentity { fn into_identity(self) -> Identity; } +impl Identity { + pub fn into_iden(self) -> Rc { + match self { + Self::Unary(iden) => iden, + } + } +} + impl IntoIdentity for T where T: IntoIden, diff --git a/src/lib.rs b/src/lib.rs index 88d3999c..38e2f423 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,5 +12,6 @@ pub use driver::*; pub use entity::*; pub use query::*; +pub use sea_query; pub use sea_query::Iden; -pub use strum::EnumIter; \ No newline at end of file +pub use strum::EnumIter; diff --git a/src/query/select.rs b/src/query/select.rs index 9c71a734..f08b684b 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -2,7 +2,7 @@ use crate::{EntityTrait, Identity, Iterable, RelationDef, Statement}; use core::fmt::Debug; use core::marker::PhantomData; pub use sea_query::JoinType; -use sea_query::{Expr, Iden, IntoIden, QueryBuilder, SelectStatement}; +use sea_query::{Expr, Iden, IntoIden, QueryBuilder, SelectStatement, SimpleExpr}; use std::rc::Rc; #[derive(Debug)] @@ -54,6 +54,11 @@ where self } + pub fn filter(mut self, expr: SimpleExpr) -> Self { + self.select.and_where(expr); + self + } + pub fn left_join(self, relation: RelationDef) -> Self { self.prepare_join(JoinType::LeftJoin, relation) } @@ -89,16 +94,77 @@ where #[cfg(test)] mod tests { use crate::tests_cfg::cake; - use crate::EntityTrait; - use sea_query::MySqlQueryBuilder; + use crate::{ColumnTrait, EntityTrait}; + use sea_query::MysqlQueryBuilder; #[test] fn test_1() { assert_eq!( - cake::Entity::find() - .build(MySqlQueryBuilder::default()) - .to_string(), + cake::Entity::find().build(MysqlQueryBuilder).to_string(), "SELECT `cake`.`id`, `cake`.`name` FROM `cake`" ); } + + #[test] + fn test_2() { + assert_eq!( + cake::Entity::find() + .filter(cake::Column::Id.eq(5)) + .build(MysqlQueryBuilder) + .to_string(), + "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` = 5" + ); + } + + #[test] + fn test_3() { + assert_eq!( + cake::Entity::find() + .filter(cake::Column::Name.like("cheese")) + .build(MysqlQueryBuilder) + .to_string(), + "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE 'cheese'" + ); + assert_eq!( + cake::Entity::find() + .filter(cake::Column::Name.starts_with("cheese")) + .build(MysqlQueryBuilder) + .to_string(), + "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE 'cheese%'" + ); + assert_eq!( + cake::Entity::find() + .filter(cake::Column::Name.ends_with("cheese")) + .build(MysqlQueryBuilder) + .to_string(), + "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE '%cheese'" + ); + assert_eq!( + cake::Entity::find() + .filter(cake::Column::Name.contains("cheese")) + .build(MysqlQueryBuilder) + .to_string(), + "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE '%cheese%'" + ); + } + + #[test] + fn test_4() { + assert_eq!( + cake::Entity::find_one() + .build(MysqlQueryBuilder) + .to_string(), + "SELECT `cake`.`id`, `cake`.`name` FROM `cake` LIMIT 1" + ); + } + + #[test] + fn test_5() { + assert_eq!( + cake::Entity::find_one_by(11) + .build(MysqlQueryBuilder) + .to_string(), + "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` = 11 LIMIT 1" + ); + } } diff --git a/src/tests_cfg/cake.rs b/src/tests_cfg/cake.rs index 74d1a6b2..41b58116 100644 --- a/src/tests_cfg/cake.rs +++ b/src/tests_cfg/cake.rs @@ -1,9 +1,9 @@ use crate::{ - ColumnTrait, ColumnType, EntityTrait, Identity, IntoIdentity, ModelTrait, QueryResult, RelationDef, - RelationTrait, TypeErr, EnumIter, Iden + ColumnTrait, ColumnType, EntityTrait, EnumIter, Iden, Identity, IntoIdentity, ModelTrait, + QueryResult, RelationDef, RelationTrait, TypeErr, }; -#[derive(Iden, Default, Debug)] +#[derive(Default, Debug, Iden)] #[iden = "cake"] pub struct Entity; @@ -13,13 +13,13 @@ pub struct Model { pub name: String, } -#[derive(Iden, EnumIter)] +#[derive(Copy, Clone, Iden, EnumIter)] pub enum Column { Id, Name, } -#[derive(EnumIter)] +#[derive(Copy, Clone, EnumIter)] pub enum Relation {} impl EntityTrait for Entity { @@ -44,6 +44,8 @@ impl ModelTrait for Model { } impl ColumnTrait for Column { + type Entity = Entity; + fn col_type(&self) -> ColumnType { match self { Self::Id => ColumnType::Integer(None),