diff --git a/src/query/helper.rs b/src/query/helper.rs index 109912e5f..6ab31d53b 100644 --- a/src/query/helper.rs +++ b/src/query/helper.rs @@ -3,8 +3,8 @@ use crate::{ PrimaryKeyToColumn, RelationDef, }; use sea_query::{ - Alias, Expr, Iden, IntoCondition, LockType, SeaRc, SelectExpr, SelectStatement, SimpleExpr, - TableRef, + Alias, Expr, Iden, IntoCondition, IntoIden, LockType, SeaRc, SelectExpr, SelectStatement, + SimpleExpr, TableRef, }; pub use sea_query::{Condition, ConditionalStatement, DynIden, JoinType, Order, OrderedStatement}; @@ -182,6 +182,32 @@ pub trait QuerySelect: Sized { self } + /// Join via [`RelationDef`] with table alias. + fn join_as(mut self, join: JoinType, mut rel: RelationDef, alias: I) -> Self + where + I: IntoIden, + { + let alias = alias.into_iden(); + rel.to_tbl = rel.to_tbl.alias(SeaRc::clone(&alias)); + self.query() + .join(join, rel.to_tbl.clone(), join_condition(rel)); + self + } + + /// Join via [`RelationDef`] with table alias but in reverse direction. + /// Assume when there exist a relation A to B. + /// You can reverse join B from A. + fn join_as_rev(mut self, join: JoinType, mut rel: RelationDef, alias: I) -> Self + where + I: IntoIden, + { + let alias = alias.into_iden(); + rel.from_tbl = rel.from_tbl.alias(SeaRc::clone(&alias)); + self.query() + .join(join, rel.from_tbl.clone(), join_condition(rel)); + self + } + /// Select lock fn lock(mut self, lock_type: LockType) -> Self { self.query().lock(lock_type); @@ -431,8 +457,15 @@ pub trait QueryFilter: Sized { } pub(crate) fn join_condition(mut rel: RelationDef) -> Condition { - let from_tbl = unpack_table_ref(&rel.from_tbl); - let to_tbl = unpack_table_ref(&rel.to_tbl); + // Use table alias (if any) to construct the join condition + let from_tbl = match unpack_table_alias(&rel.from_tbl) { + Some(alias) => alias, + None => unpack_table_ref(&rel.from_tbl), + }; + let to_tbl = match unpack_table_alias(&rel.to_tbl) { + Some(alias) => alias, + None => unpack_table_ref(&rel.to_tbl), + }; let owner_keys = rel.from_col; let foreign_keys = rel.to_col; @@ -486,3 +519,16 @@ pub(crate) fn unpack_table_ref(table_ref: &TableRef) -> DynIden { | TableRef::ValuesList(_, tbl) => SeaRc::clone(tbl), } } + +pub(crate) fn unpack_table_alias(table_ref: &TableRef) -> Option { + match table_ref { + TableRef::Table(_) + | TableRef::SchemaTable(_, _) + | TableRef::DatabaseSchemaTable(_, _, _) + | TableRef::SubQuery(_, _) + | TableRef::ValuesList(_, _) => None, + TableRef::TableAlias(_, alias) + | TableRef::SchemaTableAlias(_, _, alias) + | TableRef::DatabaseSchemaTableAlias(_, _, _, alias) => Some(SeaRc::clone(alias)), + } +} diff --git a/src/query/join.rs b/src/query/join.rs index f936e546a..d23c0333c 100644 --- a/src/query/join.rs +++ b/src/query/join.rs @@ -124,7 +124,7 @@ mod tests { RelationTrait, }; use pretty_assertions::assert_eq; - use sea_query::{Expr, IntoCondition, JoinType}; + use sea_query::{Alias, Expr, IntoCondition, JoinType}; #[test] fn join_1() { @@ -506,4 +506,64 @@ mod tests { .join(" ") ); } + + #[test] + fn join_20() { + assert_eq!( + cake::Entity::find() + .column_as( + Expr::tbl(Alias::new("fruit_alias"), fruit::Column::Name).into_simple_expr(), + "fruit_name" + ) + .join_as( + JoinType::LeftJoin, + cake::Relation::Fruit + .def() + .on_condition(|_left, right| { + Expr::tbl(right, fruit::Column::Name) + .like("%tropical%") + .into_condition() + }), + Alias::new("fruit_alias") + ) + .build(DbBackend::MySql) + .to_string(), + [ + "SELECT `cake`.`id`, `cake`.`name`, `fruit_alias`.`name` AS `fruit_name` FROM `cake`", + "LEFT JOIN `fruit` AS `fruit_alias` ON `cake`.`id` = `fruit_alias`.`cake_id` AND `fruit_alias`.`name` LIKE '%tropical%'", + ] + .join(" ") + ); + } + + #[test] + fn join_21() { + assert_eq!( + cake::Entity::find() + .column_as( + Expr::tbl(Alias::new("cake_filling_alias"), cake_filling::Column::CakeId).into_simple_expr(), + "cake_filling_cake_id" + ) + .join(JoinType::LeftJoin, cake::Relation::TropicalFruit.def()) + .join_as_rev( + JoinType::LeftJoin, + cake_filling::Relation::Cake + .def() + .on_condition(|left, _right| { + Expr::tbl(left, cake_filling::Column::CakeId) + .gt(10) + .into_condition() + }), + Alias::new("cake_filling_alias") + ) + .build(DbBackend::MySql) + .to_string(), + [ + "SELECT `cake`.`id`, `cake`.`name`, `cake_filling_alias`.`cake_id` AS `cake_filling_cake_id` FROM `cake`", + "LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` AND `fruit`.`name` LIKE '%tropical%'", + "LEFT JOIN `cake_filling` AS `cake_filling_alias` ON `cake_filling_alias`.`cake_id` = `cake`.`id` AND `cake_filling_alias`.`cake_id` > 10", + ] + .join(" ") + ); + } }