From 9a5201bda82521ecce664be12165d6c8b4176393 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 1 Apr 2025 21:26:01 +0800 Subject: [PATCH 1/2] Select up to 10 --- src/executor/cursor.rs | 124 +++++++------- src/executor/paginator.rs | 53 +++--- src/executor/select.rs | 345 +++++++++++++++----------------------- src/query/combine.rs | 108 ++++++------ src/query/join.rs | 58 ++++--- src/query/mod.rs | 4 +- src/query/select.rs | 153 ++++++----------- 7 files changed, 369 insertions(+), 476 deletions(-) diff --git a/src/executor/cursor.rs b/src/executor/cursor.rs index 0c2c10e927..b49a27dead 100644 --- a/src/executor/cursor.rs +++ b/src/executor/cursor.rs @@ -1,7 +1,7 @@ use crate::{ ConnectionTrait, DbErr, EntityTrait, FromQueryResult, Identity, IdentityOf, IntoIdentity, - PartialModelTrait, PrimaryKeyToColumn, QueryOrder, QuerySelect, Select, SelectModel, - SelectThree, SelectThreeModel, SelectTwo, SelectTwoModel, SelectorTrait, + PartialModelTrait, PrimaryKeyToColumn, QueryOrder, QuerySelect, Select, SelectModel, SelectTwo, + SelectTwoModel, SelectorTrait, }; use sea_query::{ Condition, DynIden, Expr, IntoValueTuple, Order, SeaRc, SelectStatement, SimpleExpr, Value, @@ -398,27 +398,27 @@ where } } -impl CursorTrait for SelectTwo -where - E: EntityTrait, - F: EntityTrait, - M: FromQueryResult + Sized + Send + Sync, - N: FromQueryResult + Sized + Send + Sync, -{ - type Selector = SelectTwoModel; +macro_rules! impl_cursor_trait { + ( $select_struct:ident <$($select_generics:ident),+>, $model_struct:ident <$($model_generics:ident),+> ) => { + impl<$($select_generics),*, $($model_generics),*> CursorTrait for crate::$select_struct<$($select_generics),*> + where + $($select_generics: EntityTrait,)* + $($model_generics: FromQueryResult + Sized + Send + Sync,)* + { + type Selector = crate::$model_struct<$($model_generics,)*>; + } + } } -impl CursorTrait for SelectThree -where - E: EntityTrait, - F: EntityTrait, - G: EntityTrait, - M: FromQueryResult + Sized + Send + Sync, - N: FromQueryResult + Sized + Send + Sync, - O: FromQueryResult + Sized + Send + Sync, -{ - type Selector = SelectThreeModel; -} +impl_cursor_trait!(SelectTwo, SelectTwoModel); +impl_cursor_trait!(SelectThree, SelectThreeModel); +impl_cursor_trait!(SelectFour, SelectFourModel); +impl_cursor_trait!(SelectFive, SelectFiveModel); +impl_cursor_trait!(SelectSix, SelectSixModel); +impl_cursor_trait!(SelectSeven, SelectSevenModel); +impl_cursor_trait!(SelectEight, SelectEightModel); +impl_cursor_trait!(SelectNine, SelectNineModel); +impl_cursor_trait!(SelectTen, SelectTenModel); impl SelectTwo where @@ -472,51 +472,53 @@ where } } -impl SelectThree -where - E: EntityTrait, - F: EntityTrait, - G: EntityTrait, - M: FromQueryResult + Sized + Send + Sync, - N: FromQueryResult + Sized + Send + Sync, - O: FromQueryResult + Sized + Send + Sync, -{ - /// Convert into a cursor using column of first entity - pub fn cursor_by(self, order_columns: C) -> Cursor> - where - C: IdentityOf, - { - let mut cursor = Cursor::new( - self.query, - SeaRc::new(E::default()), - order_columns.identity_of(), - ); - { - let primary_keys: Vec<(DynIden, Identity)> = ::iter() - .map(|pk| { - ( - SeaRc::new(F::default()), - Identity::Unary(SeaRc::new(pk.into_column())), - ) - }) - .collect(); - cursor.set_secondary_order_by(primary_keys); - } +macro_rules! impl_cursor_by { + ( $select_struct:ident <$first_select_generics:ident, $($select_generics:ident),+>, $model_struct:ident <$first_model_generics:ident, $($model_generics:ident),+> ) => { + impl<$first_select_generics, $($select_generics),*, $first_model_generics, $($model_generics),*> crate::$select_struct<$first_select_generics, $($select_generics),*> + where + $first_select_generics: EntityTrait, + $($select_generics: EntityTrait,)* + $first_model_generics: FromQueryResult + Sized + Send + Sync, + $($model_generics: FromQueryResult + Sized + Send + Sync,)* { - let primary_keys: Vec<(DynIden, Identity)> = ::iter() - .map(|pk| { - ( - SeaRc::new(G::default()), - Identity::Unary(SeaRc::new(pk.into_column())), - ) - }) - .collect(); - cursor.set_secondary_order_by(primary_keys); + #[doc ="Convert into a cursor using column of first entity"] + pub fn cursor_by(self, order_columns: C) -> Cursor> + where + C: IdentityOf<$first_select_generics>, + { + let mut cursor = Cursor::new( + self.query, + SeaRc::new($first_select_generics::default()), + order_columns.identity_of(), + ); + $( + { + let primary_keys: Vec<(DynIden, Identity)> = <$select_generics::PrimaryKey as Iterable>::iter() + .map(|pk| { + ( + SeaRc::new($select_generics::default()), + Identity::Unary(SeaRc::new(pk.into_column())), + ) + }) + .collect(); + cursor.set_secondary_order_by(primary_keys); + } + )* + cursor + } } - cursor } } +impl_cursor_by!(SelectThree, SelectThreeModel); +impl_cursor_by!(SelectFour, SelectFourModel); +impl_cursor_by!(SelectFive, SelectFiveModel); +impl_cursor_by!(SelectSix, SelectSixModel); +impl_cursor_by!(SelectSeven, SelectSevenModel); +impl_cursor_by!(SelectEight, SelectEightModel); +impl_cursor_by!(SelectNine, SelectNineModel); +impl_cursor_by!(SelectTen, SelectTenModel); + #[cfg(test)] #[cfg(feature = "mock")] mod tests { diff --git a/src/executor/paginator.rs b/src/executor/paginator.rs index 5b910fbb83..7b933f7e1e 100644 --- a/src/executor/paginator.rs +++ b/src/executor/paginator.rs @@ -1,6 +1,6 @@ use crate::{ error::*, ConnectionTrait, DbBackend, EntityTrait, FromQueryResult, Select, SelectModel, - SelectThree, SelectThreeModel, SelectTwo, SelectTwoModel, Selector, SelectorRaw, SelectorTrait, + Selector, SelectorRaw, SelectorTrait, }; use async_stream::stream; use futures_util::Stream; @@ -285,37 +285,32 @@ where } } -impl<'db, C, M, N, E, F> PaginatorTrait<'db, C> for SelectTwo -where - C: ConnectionTrait, - E: EntityTrait, - F: EntityTrait, - M: FromQueryResult + Sized + Send + Sync + 'db, - N: FromQueryResult + Sized + Send + Sync + 'db, -{ - type Selector = SelectTwoModel; - - fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector> { - self.into_model().paginate(db, page_size) +macro_rules! impl_paginator_trait { + ( $select_struct:ident <$($select_generics:ident),+>, $model_struct:ident <$($model_generics:ident),+> ) => { + impl<'db, C, $($select_generics),*, $($model_generics),*> PaginatorTrait<'db, C> for crate::$select_struct<$($select_generics),*> + where + C: ConnectionTrait, + $($select_generics: EntityTrait,)* + $($model_generics: FromQueryResult + Sized + Send + Sync + 'db,)* + { + type Selector = crate::$model_struct<$($model_generics),*>; + + fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector> { + self.into_model().paginate(db, page_size) + } + } } } -impl<'db, C, M, N, O, E, F, G> PaginatorTrait<'db, C> for SelectThree -where - C: ConnectionTrait, - E: EntityTrait, - F: EntityTrait, - G: EntityTrait, - M: FromQueryResult + Sized + Send + Sync + 'db, - N: FromQueryResult + Sized + Send + Sync + 'db, - O: FromQueryResult + Sized + Send + Sync + 'db, -{ - type Selector = SelectThreeModel; - - fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector> { - self.into_model().paginate(db, page_size) - } -} +impl_paginator_trait!(SelectTwo, SelectTwoModel); +impl_paginator_trait!(SelectThree, SelectThreeModel); +impl_paginator_trait!(SelectFour, SelectFourModel); +impl_paginator_trait!(SelectFive, SelectFiveModel); +impl_paginator_trait!(SelectSix, SelectSixModel); +impl_paginator_trait!(SelectSeven, SelectSevenModel); +impl_paginator_trait!(SelectEight, SelectEightModel); +impl_paginator_trait!(SelectNine, SelectNineModel); +impl_paginator_trait!(SelectTen, SelectTenModel); #[cfg(test)] #[cfg(feature = "mock")] diff --git a/src/executor/select.rs b/src/executor/select.rs index fa149eed8e..0ae202942e 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -1,8 +1,7 @@ use crate::{ error::*, ConnectionTrait, DbBackend, EntityTrait, FromQueryResult, IdenStatic, Iterable, ModelTrait, PartialModelTrait, PrimaryKeyArity, PrimaryKeyToColumn, PrimaryKeyTrait, - QueryResult, QuerySelect, Select, SelectA, SelectB, SelectC, SelectThree, SelectTwo, - SelectTwoMany, Statement, StreamTrait, TryGetableMany, + QueryResult, QuerySelect, Select, SelectTwoMany, Statement, StreamTrait, TryGetableMany, }; use futures_util::{Stream, TryStreamExt}; use sea_query::{SelectStatement, Value}; @@ -71,26 +70,28 @@ where model: PhantomData, } -/// Helper class to handle query result for 2 Models -#[derive(Clone, Debug)] -pub struct SelectTwoModel -where - M: FromQueryResult, - N: FromQueryResult, -{ - model: PhantomData<(M, N)>, +macro_rules! def_model_struct { + ( $struct:ident <$($generics:ident),+>, $num:ident ) => { + #[doc = concat!("Helper class to handle query result for ", stringify!($num), " Models")] + #[derive(Clone, Debug)] + pub struct $struct<$($generics),*> + where + $($generics: FromQueryResult),* + { + model: PhantomData<($($generics),*)>, + } + } } -/// Helper class to handle query result for 3 Models -#[derive(Clone, Debug)] -pub struct SelectThreeModel -where - M: FromQueryResult, - N: FromQueryResult, - O: FromQueryResult, -{ - model: PhantomData<(M, N, O)>, -} +def_model_struct!(SelectTwoModel, two); +def_model_struct!(SelectThreeModel, three); +def_model_struct!(SelectFourModel, four); +def_model_struct!(SelectFiveModel, five); +def_model_struct!(SelectSixModel, six); +def_model_struct!(SelectSevenModel, seven); +def_model_struct!(SelectEightModel, eight); +def_model_struct!(SelectNineModel, nine); +def_model_struct!(SelectTenModel, ten); impl SelectorTrait for SelectGetableValue where @@ -127,37 +128,34 @@ where } } -impl SelectorTrait for SelectTwoModel -where - M: FromQueryResult + Sized, - N: FromQueryResult + Sized, -{ - type Item = (M, Option); - - fn from_raw_query_result(res: QueryResult) -> Result { - Ok(( - M::from_query_result(&res, SelectA.as_str())?, - N::from_query_result_optional(&res, SelectB.as_str())?, - )) +macro_rules! impl_selector_trait { + ( $model_struct:ident <$first_model_generics:ident => $first_prefix:ident, $($model_generics:ident => $prefix:ident),+> ) => { + impl<$first_model_generics, $($model_generics),*> SelectorTrait for $model_struct<$first_model_generics, $($model_generics),*> + where + $first_model_generics: FromQueryResult + Sized, + $($model_generics: FromQueryResult + Sized),* + { + type Item = ($first_model_generics, $(Option<$model_generics>),*); + + fn from_raw_query_result(res: QueryResult) -> Result { + Ok(( + $first_model_generics::from_query_result(&res, crate::$first_prefix.as_str())?, + $($model_generics::from_query_result_optional(&res, crate::$prefix.as_str())?,)* + )) + } + } } } -impl SelectorTrait for SelectThreeModel -where - M: FromQueryResult + Sized, - N: FromQueryResult + Sized, - O: FromQueryResult + Sized, -{ - type Item = (M, Option, Option); - - fn from_raw_query_result(res: QueryResult) -> Result { - Ok(( - M::from_query_result(&res, SelectA.as_str())?, - N::from_query_result_optional(&res, SelectB.as_str())?, - O::from_query_result_optional(&res, SelectC.as_str())?, - )) - } -} +impl_selector_trait!(SelectTwoModelSelectA, P=>SelectB>); +impl_selector_trait!(SelectThreeModelSelectA, P=>SelectB, Q=>SelectC>); +impl_selector_trait!(SelectFourModelSelectA, P=>SelectB, Q=>SelectC, R=>SelectD>); +impl_selector_trait!(SelectFiveModelSelectA, P=>SelectB, Q=>SelectC, R=>SelectD, S=>SelectE>); +impl_selector_trait!(SelectSixModelSelectA, P=>SelectB, Q=>SelectC, R=>SelectD, S=>SelectE, T=>SelectF>); +impl_selector_trait!(SelectSevenModelSelectA, P=>SelectB, Q=>SelectC, R=>SelectD, S=>SelectE, T=>SelectF, U=>SelectG>); +impl_selector_trait!(SelectEightModelSelectA, P=>SelectB, Q=>SelectC, R=>SelectD, S=>SelectE, T=>SelectF, U=>SelectG, V=>SelectH>); +impl_selector_trait!(SelectNineModelSelectA, P=>SelectB, Q=>SelectC, R=>SelectD, S=>SelectE, T=>SelectF, U=>SelectG, V=>SelectH, W=>SelectI>); +impl_selector_trait!(SelectTenModelSelectA, P=>SelectB, Q=>SelectC, R=>SelectD, S=>SelectE, T=>SelectF, U=>SelectG, V=>SelectH, W=>SelectI, X=>SelectJ>); impl Select where @@ -479,85 +477,104 @@ where } } -impl SelectTwo -where - E: EntityTrait, - F: EntityTrait, -{ - /// Perform a conversion into a [SelectTwoModel] - pub fn into_model(self) -> Selector> - where - M: FromQueryResult, - N: FromQueryResult, - { - Selector { - query: self.query, - selector: SelectTwoModel { model: PhantomData }, - } - } +macro_rules! json_value_type { + ( $i:ident ) => { + JsonValue + }; +} - /// Perform a conversion into a [SelectTwoModel] with [PartialModel](PartialModelTrait) - pub fn into_partial_model(self) -> Selector> - where - M: PartialModelTrait, - N: PartialModelTrait, - { - let select = QuerySelect::select_only(self); - let select = M::select_cols(select); - let select = N::select_cols(select); - select.into_model::() - } +macro_rules! impl_model_struct { + ( $select_struct:ident <$first_select_generics:ident, $($select_generics:ident),+>, $model_struct:ident <$first_model_generics:ident, $($model_generics:ident),+> ) => { + impl<$first_select_generics, $($select_generics),*> crate::$select_struct<$first_select_generics, $($select_generics),*> + where + $first_select_generics: EntityTrait, + $($select_generics: EntityTrait),* + { + #[doc = concat!("Perform a conversion into a [", stringify!($model_struct), "]")] + pub fn into_model<$first_model_generics, $($model_generics),*>(self) -> Selector<$model_struct<$first_model_generics, $($model_generics),*>> + where + $first_model_generics: FromQueryResult, + $($model_generics: FromQueryResult),* + { + Selector { + query: self.query, + selector: crate::$model_struct { model: PhantomData }, + } + } - /// Convert the Models into JsonValue - #[cfg(feature = "with-json")] - pub fn into_json(self) -> Selector> { - Selector { - query: self.query, - selector: SelectTwoModel { model: PhantomData }, - } - } + #[doc = concat!("Perform a conversion into a [", stringify!($model_struct), "] with [PartialModel](PartialModelTrait)")] + pub fn into_partial_model<$first_model_generics, $($model_generics),*>(self) -> Selector> + where + $first_model_generics: PartialModelTrait, + $($model_generics: PartialModelTrait),* + { + let select = QuerySelect::select_only(self); + $(let select = $model_generics::select_cols(select);)* + select.into_model::<$first_model_generics, $($model_generics),*>() + } - /// Get one Model from the Select query - pub async fn one(self, db: &C) -> Result)>, DbErr> - where - C: ConnectionTrait, - { - self.into_model().one(db).await - } + #[doc = "Convert the Models into JsonValue"] + #[cfg(feature = "with-json")] + pub fn into_json(self) -> Selector> { + Selector { + query: self.query, + selector: crate::$model_struct { model: PhantomData }, + } + } - /// Get all Models from the Select query - pub async fn all(self, db: &C) -> Result)>, DbErr> - where - C: ConnectionTrait, - { - self.into_model().all(db).await - } + #[doc = "Get one Model from the Select query"] + pub async fn one(self, db: &C) -> Result),*)>, DbErr> + where + C: ConnectionTrait, + { + self.into_model().one(db).await + } - /// Stream the results of a Select operation on a Model - pub async fn stream<'a: 'b, 'b, C>( - self, - db: &'a C, - ) -> Result), DbErr>> + 'b, DbErr> - where - C: ConnectionTrait + StreamTrait + Send, - { - self.into_model().stream(db).await - } + #[doc = "Get all Models from the Select query"] + pub async fn all(self, db: &C) -> Result),*)>, DbErr> + where + C: ConnectionTrait, + { + self.into_model().all(db).await + } - /// Stream the result of the operation with PartialModel - pub async fn stream_partial_model<'a: 'b, 'b, C, M, N>( - self, - db: &'a C, - ) -> Result), DbErr>> + 'b + Send, DbErr> - where - C: ConnectionTrait + StreamTrait + Send, - M: PartialModelTrait + Send + 'b, - N: PartialModelTrait + Send + 'b, - { - self.into_partial_model().stream(db).await + #[doc = "Stream the results of a Select operation on a Model"] + pub async fn stream<'a: 'b, 'b, C>( + self, + db: &'a C, + ) -> Result),*), DbErr>> + 'b, DbErr> + where + C: ConnectionTrait + StreamTrait + Send, + { + self.into_model().stream(db).await + } + + #[doc = "Stream the result of the operation with PartialModel"] + pub async fn stream_partial_model<'a: 'b, 'b, C, $first_model_generics, $($model_generics),*>( + self, + db: &'a C, + ) -> Result),*), DbErr>> + 'b + Send, DbErr> + where + C: ConnectionTrait + StreamTrait + Send, + $first_model_generics: PartialModelTrait + Send + 'b, + $($model_generics: PartialModelTrait + Send + 'b),* + { + self.into_partial_model().stream(db).await + } + } } } +impl_model_struct!(SelectTwo, SelectTwoModel); +impl_model_struct!(SelectThree, SelectThreeModel); +impl_model_struct!(SelectFour, SelectFourModel); +impl_model_struct!(SelectFive, SelectFiveModel); +impl_model_struct!(SelectSix, SelectSixModel); +impl_model_struct!(SelectSeven, SelectSevenModel); +impl_model_struct!(SelectEight, SelectEightModel); +impl_model_struct!(SelectNine, SelectNineModel); +impl_model_struct!(SelectTen, SelectTenModel); + impl SelectTwoMany where E: EntityTrait, @@ -646,98 +663,6 @@ where // we should only count the number of items of the parent model } -impl SelectThree -where - E: EntityTrait, - F: EntityTrait, - G: EntityTrait, -{ - /// Perform a conversion into a [SelectThreeModel] - pub fn into_model(self) -> Selector> - where - M: FromQueryResult, - N: FromQueryResult, - O: FromQueryResult, - { - Selector { - query: self.query, - selector: SelectThreeModel { model: PhantomData }, - } - } - - /// Perform a conversion into a [SelectThreeModel] with [PartialModel](PartialModelTrait) - pub fn into_partial_model(self) -> Selector> - where - M: PartialModelTrait, - N: PartialModelTrait, - O: PartialModelTrait, - { - let select = QuerySelect::select_only(self); - let select = M::select_cols(select); - let select = N::select_cols(select); - select.into_model::() - } - - /// Convert the Models into JsonValue - #[cfg(feature = "with-json")] - pub fn into_json(self) -> Selector> { - Selector { - query: self.query, - selector: SelectThreeModel { model: PhantomData }, - } - } - - /// Get one Model from the Select query - pub async fn one( - self, - db: &C, - ) -> Result, Option)>, DbErr> - where - C: ConnectionTrait, - { - self.into_model().one(db).await - } - - /// Get all Models from the Select query - pub async fn all( - self, - db: &C, - ) -> Result, Option)>, DbErr> - where - C: ConnectionTrait, - { - self.into_model().all(db).await - } - - /// Stream the results of a Select operation on a Model - pub async fn stream<'a: 'b, 'b, C>( - self, - db: &'a C, - ) -> Result< - impl Stream, Option), DbErr>> + 'b, - DbErr, - > - where - C: ConnectionTrait + StreamTrait + Send, - { - self.into_model().stream(db).await - } - - /// Stream the result of the operation with PartialModel - pub async fn stream_partial_model<'a: 'b, 'b, C, M, N, O>( - self, - db: &'a C, - ) -> Result, Option), DbErr>> + 'b + Send, DbErr> - where - C: ConnectionTrait + StreamTrait + Send, - M: PartialModelTrait + Send + 'b, - N: PartialModelTrait + Send + 'b, - O: PartialModelTrait + Send + 'b, - { - self.into_partial_model().stream(db).await - } -} - impl Selector where S: SelectorTrait, diff --git a/src/query/combine.rs b/src/query/combine.rs index 917ec24ebf..0eb347339c 100644 --- a/src/query/combine.rs +++ b/src/query/combine.rs @@ -1,6 +1,5 @@ use crate::{ - ColumnTrait, EntityTrait, IdenStatic, Iterable, QueryTrait, Select, SelectThree, SelectTwo, - SelectTwoMany, + ColumnTrait, EntityTrait, IdenStatic, Iterable, QueryTrait, Select, SelectTwo, SelectTwoMany, }; use core::marker::PhantomData; use sea_query::{Alias, ColumnRef, Iden, Order, SeaRc, SelectExpr, SelectStatement, SimpleExpr}; @@ -28,6 +27,13 @@ macro_rules! select_def { select_def!(SelectA, "A_"); select_def!(SelectB, "B_"); select_def!(SelectC, "C_"); +select_def!(SelectD, "D_"); +select_def!(SelectE, "E_"); +select_def!(SelectF, "F_"); +select_def!(SelectG, "G_"); +select_def!(SelectH, "H_"); +select_def!(SelectI, "I_"); +select_def!(SelectJ, "J_"); impl Select where @@ -92,36 +98,67 @@ where } } -impl SelectTwo -where - E: EntityTrait, - F: EntityTrait, -{ - /// Selects extra Entity and returns it together with the Entities from `Self` - pub fn select_also(self, _: G) -> SelectThree - where - G: EntityTrait, - { - SelectThree::new(self.into_query()) - } +macro_rules! impl_prepare_select_also { + ( $struct:ident <$($generics:ident),+>, $last:ident, $col_prefix:ident ) => { + impl<$($generics),*> crate::$struct<$($generics),*> + where + $($generics: EntityTrait),* + { + pub(crate) fn new(query: SelectStatement) -> Self { + Self::new_without_prepare(query).prepare_select() + } - pub(crate) fn new(query: SelectStatement) -> Self { - Self::new_without_prepare(query).prepare_select() - } + pub(crate) fn new_without_prepare(query: SelectStatement) -> Self { + Self { + query, + entity: PhantomData, + } + } - pub(crate) fn new_without_prepare(query: SelectStatement) -> Self { - Self { - query, - entity: PhantomData, + fn prepare_select(mut self) -> Self { + prepare_select_col::<$last, _, _>(&mut self, $col_prefix); + self + } } } +} - fn prepare_select(mut self) -> Self { - prepare_select_col::(&mut self, SelectB); - self +impl_prepare_select_also!(SelectTwo, F, SelectB); +impl_prepare_select_also!(SelectThree, G, SelectC); +impl_prepare_select_also!(SelectFour, H, SelectD); +impl_prepare_select_also!(SelectFive, I, SelectE); +impl_prepare_select_also!(SelectSix, J, SelectF); +impl_prepare_select_also!(SelectSeven, K, SelectG); +impl_prepare_select_also!(SelectEight, L, SelectH); +impl_prepare_select_also!(SelectNine, M, SelectI); +impl_prepare_select_also!(SelectTen, N, SelectJ); + +macro_rules! impl_select_also { + ( $struct:ident <$($generics:ident),+>, $next_struct:ident ) => { + impl<$($generics),*> crate::$struct<$($generics),*> + where + $($generics: EntityTrait),* + { + #[doc = "Selects extra Entity and returns it together with the Entities from `Self`"] + pub fn select_also(self, _: R) -> crate::$next_struct<$($generics),*, R> + where + R: EntityTrait, + { + crate::$next_struct::new(self.into_query()) + } + } } } +impl_select_also!(SelectTwo, SelectThree); +impl_select_also!(SelectThree, SelectFour); +impl_select_also!(SelectFour, SelectFive); +impl_select_also!(SelectFive, SelectSix); +impl_select_also!(SelectSix, SelectSeven); +impl_select_also!(SelectSeven, SelectEight); +impl_select_also!(SelectEight, SelectNine); +impl_select_also!(SelectNine, SelectTen); + impl SelectTwoMany where E: EntityTrait, @@ -153,29 +190,6 @@ where } } -impl SelectThree -where - E: EntityTrait, - F: EntityTrait, - G: EntityTrait, -{ - pub(crate) fn new(query: SelectStatement) -> Self { - Self::new_without_prepare(query).prepare_select() - } - - pub(crate) fn new_without_prepare(query: SelectStatement) -> Self { - Self { - query, - entity: PhantomData, - } - } - - fn prepare_select(mut self) -> Self { - prepare_select_col::(&mut self, SelectC); - self - } -} - fn prepare_select_col(selector: &mut S, alias: A) where F: EntityTrait, diff --git a/src/query/join.rs b/src/query/join.rs index ebccef162a..6a9b212e9b 100644 --- a/src/query/join.rs +++ b/src/query/join.rs @@ -1,6 +1,6 @@ use crate::{ join_tbl_on_condition, unpack_table_ref, ColumnTrait, EntityTrait, IdenStatic, Iterable, - Linked, QuerySelect, Related, Select, SelectA, SelectB, SelectThree, SelectTwo, SelectTwoMany, + Linked, QuerySelect, Related, Select, SelectA, SelectB, SelectTwo, SelectTwoMany, }; pub use sea_query::JoinType; use sea_query::{Alias, Condition, Expr, IntoIden, SeaRc, SelectExpr}; @@ -155,32 +155,44 @@ where } } -impl SelectTwo -where - E: EntityTrait, - F: EntityTrait, -{ - /// Left Join with an Entity Related to the first Entity - pub fn find_also_related(self, r: R) -> SelectThree - where - R: EntityTrait, - E: Related, - { - self.join_join(JoinType::LeftJoin, E::to(), E::via()) - .select_also(r) - } +macro_rules! impl_find_related { + ( $struct:ident <$($generics:ident),+>, $second_last:ident, $last:ident, $next_struct:ident ) => { + impl<$($generics),*> crate::$struct<$($generics),*> + where + $($generics: EntityTrait),* + { + #[doc = "Left Join with an Entity Related to the second last Entity"] + pub fn find_also_related(self, r: R) -> crate::$next_struct<$($generics),*, R> + where + R: EntityTrait, + $second_last: Related, + { + self.join_join(JoinType::LeftJoin, $second_last::to(), $second_last::via()) + .select_also(r) + } - /// Left Join with an Entity Related to the second Entity - pub fn and_also_related(self, r: R) -> SelectThree - where - R: EntityTrait, - F: Related, - { - self.join_join(JoinType::LeftJoin, F::to(), F::via()) - .select_also(r) + #[doc = "Left Join with an Entity Related to the last Entity"] + pub fn and_also_related(self, r: R) -> crate::$next_struct<$($generics),*, R> + where + R: EntityTrait, + $last: Related, + { + self.join_join(JoinType::LeftJoin, $last::to(), $last::via()) + .select_also(r) + } + } } } +impl_find_related!(SelectTwo, E, F, SelectThree); +impl_find_related!(SelectThree, F, G, SelectFour); +impl_find_related!(SelectFour, G, H, SelectFive); +impl_find_related!(SelectFive, H, I, SelectSix); +impl_find_related!(SelectSix, I, J, SelectSeven); +impl_find_related!(SelectSeven, J, K, SelectEight); +impl_find_related!(SelectEight, K, L, SelectNine); +impl_find_related!(SelectNine, L, M, SelectTen); + #[cfg(test)] mod tests { use crate::tests_cfg::{cake, cake_filling, cake_filling_price, entity_linked, filling, fruit}; diff --git a/src/query/mod.rs b/src/query/mod.rs index bb429764f6..932d7f71c5 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -11,7 +11,9 @@ mod traits; mod update; mod util; -pub use combine::{SelectA, SelectB, SelectC}; +pub use combine::{ + SelectA, SelectB, SelectC, SelectD, SelectE, SelectF, SelectG, SelectH, SelectI, SelectJ, +}; pub use delete::*; pub use helper::*; pub use insert::*; diff --git a/src/query/select.rs b/src/query/select.rs index 625a06e2d7..598faeea5d 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -13,39 +13,30 @@ where pub(crate) entity: PhantomData, } -/// Defines a structure to perform a SELECT operation on two Models -#[derive(Clone, Debug)] -pub struct SelectTwo -where - E: EntityTrait, - F: EntityTrait, -{ - pub(crate) query: SelectStatement, - pub(crate) entity: PhantomData<(E, F)>, +macro_rules! def_select_struct { + ( $struct:ident <$($generics:ident),+>, $num:ident ) => { + #[doc = concat!("Defines a structure to perform a SELECT operation on ", stringify!($num), " Models")] + #[derive(Clone, Debug)] + pub struct $struct<$($generics),*> + where + $($generics: EntityTrait),* + { + pub(crate) query: SelectStatement, + pub(crate) entity: PhantomData<($($generics),*)>, + } + } } -/// Defines a structure to perform a SELECT operation on many Models -#[derive(Clone, Debug)] -pub struct SelectTwoMany -where - E: EntityTrait, - F: EntityTrait, -{ - pub(crate) query: SelectStatement, - pub(crate) entity: PhantomData<(E, F)>, -} - -/// Defines a structure to perform a SELECT operation on two Models -#[derive(Clone, Debug)] -pub struct SelectThree -where - E: EntityTrait, - F: EntityTrait, - G: EntityTrait, -{ - pub(crate) query: SelectStatement, - pub(crate) entity: PhantomData<(E, F, G)>, -} +def_select_struct!(SelectTwo, two); +def_select_struct!(SelectTwoMany, many); +def_select_struct!(SelectThree, three); +def_select_struct!(SelectFour, four); +def_select_struct!(SelectFive, five); +def_select_struct!(SelectSix, six); +def_select_struct!(SelectSeven, seven); +def_select_struct!(SelectEight, eight); +def_select_struct!(SelectNine, nine); +def_select_struct!(SelectTen, ten); /// Performs a conversion to [SimpleExpr] pub trait IntoSimpleExpr { @@ -54,10 +45,10 @@ pub trait IntoSimpleExpr { } macro_rules! impl_query_trait { - ( $trait: ident ) => { - impl $trait for Select + ( $struct:ident <$($generics:ident),+> ) => { + impl<$($generics),*> QuerySelect for $struct<$($generics),*> where - E: EntityTrait, + $($generics: EntityTrait),* { type QueryStatement = SelectStatement; @@ -66,10 +57,9 @@ macro_rules! impl_query_trait { } } - impl $trait for SelectTwo + impl<$($generics),*> QueryFilter for $struct<$($generics),*> where - E: EntityTrait, - F: EntityTrait, + $($generics: EntityTrait),* { type QueryStatement = SelectStatement; @@ -78,10 +68,9 @@ macro_rules! impl_query_trait { } } - impl $trait for SelectTwoMany + impl<$($generics),*> QueryOrder for $struct<$($generics),*> where - E: EntityTrait, - F: EntityTrait, + $($generics: EntityTrait),* { type QueryStatement = SelectStatement; @@ -90,24 +79,36 @@ macro_rules! impl_query_trait { } } - impl $trait for SelectThree + impl<$($generics),*> QueryTrait for $struct<$($generics),*> where - E: EntityTrait, - F: EntityTrait, - G: EntityTrait, + $($generics: EntityTrait),* { type QueryStatement = SelectStatement; fn query(&mut self) -> &mut SelectStatement { &mut self.query } + fn as_query(&self) -> &SelectStatement { + &self.query + } + fn into_query(self) -> SelectStatement { + self.query + } } }; } -impl_query_trait!(QuerySelect); -impl_query_trait!(QueryFilter); -impl_query_trait!(QueryOrder); +impl_query_trait!(Select); +impl_query_trait!(SelectTwo); +impl_query_trait!(SelectTwoMany); +impl_query_trait!(SelectThree); +impl_query_trait!(SelectFour); +impl_query_trait!(SelectFive); +impl_query_trait!(SelectSix); +impl_query_trait!(SelectSeven); +impl_query_trait!(SelectEight); +impl_query_trait!(SelectNine); +impl_query_trait!(SelectTen); impl IntoSimpleExpr for C where @@ -159,61 +160,3 @@ where self } } - -impl QueryTrait for Select -where - E: EntityTrait, -{ - type QueryStatement = SelectStatement; - fn query(&mut self) -> &mut SelectStatement { - &mut self.query - } - fn as_query(&self) -> &SelectStatement { - &self.query - } - fn into_query(self) -> SelectStatement { - self.query - } -} - -macro_rules! select_two { - ( $selector: ident ) => { - impl QueryTrait for $selector - where - E: EntityTrait, - F: EntityTrait, - { - type QueryStatement = SelectStatement; - fn query(&mut self) -> &mut SelectStatement { - &mut self.query - } - fn as_query(&self) -> &SelectStatement { - &self.query - } - fn into_query(self) -> SelectStatement { - self.query - } - } - }; -} - -select_two!(SelectTwo); -select_two!(SelectTwoMany); - -impl QueryTrait for SelectThree -where - E: EntityTrait, - F: EntityTrait, - G: EntityTrait, -{ - type QueryStatement = SelectStatement; - fn query(&mut self) -> &mut SelectStatement { - &mut self.query - } - fn as_query(&self) -> &SelectStatement { - &self.query - } - fn into_query(self) -> SelectStatement { - self.query - } -} From 1022ba7ed23ca60bc4e07556085d4305aea1fa4e Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 1 Apr 2025 21:47:20 +0800 Subject: [PATCH 2/2] one_also_related --- src/query/join.rs | 11 ++++++ tests/relational_tests.rs | 78 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/src/query/join.rs b/src/query/join.rs index 6a9b212e9b..fa62072b61 100644 --- a/src/query/join.rs +++ b/src/query/join.rs @@ -180,6 +180,17 @@ macro_rules! impl_find_related { self.join_join(JoinType::LeftJoin, $last::to(), $last::via()) .select_also(r) } + + #[doc = "Left Join with an Entity Related to one of the Entity"] + pub fn one_also_related(self, r: R) -> crate::$next_struct<$($generics),*, R> + where + R: EntityTrait, + Z: EntityTrait, + Z: Related, + { + self.join_join(JoinType::LeftJoin, Z::to(), Z::via()) + .select_also(r) + } } } } diff --git a/tests/relational_tests.rs b/tests/relational_tests.rs index 778580201e..77a79d0cd7 100644 --- a/tests/relational_tests.rs +++ b/tests/relational_tests.rs @@ -1184,3 +1184,81 @@ pub async fn select_three() -> Result<(), DbErr> { Ok(()) } + +#[sea_orm_macros::test] +pub async fn select_four() -> Result<(), DbErr> { + use common::bakery_chain::*; + + let ctx = TestContext::new("test_select_four").await; + create_tables(&ctx.db).await?; + + seed_data::init_1(&ctx, true).await; + + let order = order::Model { + id: 101, + total: Decimal::from(10), + bakery_id: 42, + customer_id: 11, + placed_at: DateTime::UNIX_EPOCH, + }; + + let customer = customer::Model { + id: 11, + name: "Bob".to_owned(), + notes: Some("Sweet tooth".to_owned()), + }; + + let line_1 = lineitem::Model { + id: 1, + price: 2.into(), + quantity: 2, + order_id: 101, + cake_id: 13, + }; + + let line_2 = lineitem::Model { + id: 2, + price: 3.into(), + quantity: 2, + order_id: 101, + cake_id: 15, + }; + + let bakery = bakery::Model { + id: 42, + name: "cool little bakery".to_string(), + profit_margin: 4.1, + }; + + let items: Vec<( + order::Model, + Option, + Option, + Option, + )> = order::Entity::find() + .find_also_related(customer::Entity) + .find_also_related(lineitem::Entity) + .one_also_related::(bakery::Entity) + .order_by_asc(order::Column::Id) + .order_by_asc(lineitem::Column::Id) + .order_by_asc(bakery::Column::Id) + .all(&ctx.db) + .await + .unwrap(); + + assert_eq!(items.len(), 2); + assert_eq!(items[0].0, order); + assert_eq!(items[0].1.as_ref().unwrap(), &customer); + assert_eq!(items[1].1.as_ref().unwrap(), &customer); + + assert_eq!(items[1].0, order); + assert_eq!(items[0].2.as_ref().unwrap(), &line_1); + assert_eq!(items[1].2.as_ref().unwrap(), &line_2); + + assert_eq!(items[0].3.as_ref().unwrap(), &bakery); + assert_eq!(items[1].3.as_ref().unwrap(), &bakery); + + ctx.delete().await; + + Ok(()) +}