Skip to content

Commit

Permalink
Relation join on AND / OR condition
Browse files Browse the repository at this point in the history
  • Loading branch information
billy1624 committed Jan 30, 2023
1 parent 51e6ad7 commit 25e6e06
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 7 deletions.
1 change: 1 addition & 0 deletions sea-orm-macros/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ pub mod field_attr {
pub from: Option<syn::Lit>,
pub to: Option<syn::Lit>,
pub fk_name: Option<syn::Lit>,
pub condition_type: Option<syn::Lit>,
}
}
22 changes: 22 additions & 0 deletions sea-orm-macros/src/derives/relation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,28 @@ impl DeriveRelation {
result = quote! { #result.fk_name(#fk_name) };
}

if attr.condition_type.is_some() {
let condition_type = attr
.condition_type
.as_ref()
.map(|lit| {
match lit {
syn::Lit::Str(lit_str) => {
match lit_str.value().to_ascii_lowercase().as_str() {
"all" => Ok(quote!( sea_orm::sea_query::ConditionType::All )),
"any" => Ok(quote!( sea_orm::sea_query::ConditionType::Any )),
_ => Err(syn::Error::new_spanned(lit, "Condition type must be one of `all` or `any`")),
}
},
_ => Err(syn::Error::new_spanned(lit, "attribute must be a string")),
}
})
.ok_or_else(|| {
syn::Error::new_spanned(variant, "Missing value for 'condition_type'")
})??;
result = quote! { #result.condition_type(#condition_type) };
}

result = quote! { #result.into() };

Result::<_, syn::Error>::Ok(result)
Expand Down
56 changes: 53 additions & 3 deletions src/entity/relation.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{unpack_table_ref, EntityTrait, Identity, IdentityOf, Iterable, QuerySelect, Select};
use core::marker::PhantomData;
use sea_query::{
Alias, Condition, DynIden, ForeignKeyCreateStatement, JoinType, SeaRc, TableForeignKey,
TableRef,
Alias, Condition, ConditionType, DynIden, ForeignKeyCreateStatement, JoinType, SeaRc,
TableForeignKey, TableRef,
};
use std::fmt::Debug;

Expand Down Expand Up @@ -68,6 +68,8 @@ pub struct RelationDef {
pub on_condition: Option<Box<dyn Fn(DynIden, DynIden) -> Condition + Send + Sync>>,
/// The name of foreign key constraint
pub fk_name: Option<String>,
/// Condition type of join on expression
pub condition_type: ConditionType,
}

impl std::fmt::Debug for RelationDef {
Expand Down Expand Up @@ -123,6 +125,7 @@ where
on_update: Option<ForeignKeyAction>,
on_condition: Option<Box<dyn Fn(DynIden, DynIden) -> Condition + Send + Sync>>,
fk_name: Option<String>,
condition_type: ConditionType,
}

impl<E, R> std::fmt::Debug for RelationBuilder<E, R>
Expand Down Expand Up @@ -160,6 +163,7 @@ impl RelationDef {
on_update: self.on_update,
on_condition: self.on_condition,
fk_name: None,
condition_type: self.condition_type,
}
}

Expand Down Expand Up @@ -205,6 +209,42 @@ impl RelationDef {
self.on_condition = Some(Box::new(f));
self
}

/// Set the condition type of join on expression
///
/// # Examples
///
/// ```
/// use sea_orm::{entity::*, query::*, DbBackend, tests_cfg::{cake, cake_filling}};
/// use sea_query::{Expr, IntoCondition, ConditionType};
///
/// assert_eq!(
/// cake::Entity::find()
/// .join(
/// JoinType::LeftJoin,
/// cake_filling::Relation::Cake
/// .def()
/// .rev()
/// .condition_type(ConditionType::Any)
/// .on_condition(|_left, right| {
/// Expr::col((right, cake_filling::Column::CakeId))
/// .gt(10i32)
/// .into_condition()
/// })
/// )
/// .build(DbBackend::MySql)
/// .to_string(),
/// [
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
/// "LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id` OR `cake_filling`.`cake_id` > 10",
/// ]
/// .join(" ")
/// );
/// ```
pub fn condition_type(mut self, condition_type: ConditionType) -> Self {
self.condition_type = condition_type;
self
}
}

impl<E, R> RelationBuilder<E, R>
Expand All @@ -225,6 +265,7 @@ where
on_update: None,
on_condition: None,
fk_name: None,
condition_type: ConditionType::All,
}
}

Expand All @@ -241,6 +282,7 @@ where
on_update: None,
on_condition: None,
fk_name: None,
condition_type: ConditionType::All,
}
}

Expand Down Expand Up @@ -291,6 +333,12 @@ where
self.fk_name = Some(fk_name.to_owned());
self
}

/// Set the condition type of join on expression
pub fn condition_type(mut self, condition_type: ConditionType) -> Self {
self.condition_type = condition_type;
self
}
}

impl<E, R> From<RelationBuilder<E, R>> for RelationDef
Expand All @@ -310,6 +358,7 @@ where
on_update: b.on_update,
on_condition: b.on_condition,
fk_name: b.fk_name,
condition_type: b.condition_type,
}
}
}
Expand Down Expand Up @@ -371,7 +420,7 @@ impl From<RelationDef> for ForeignKeyCreateStatement {

/// Creates a column definition for example to update a table.
/// ```
/// use sea_query::{Alias, IntoIden, MysqlQueryBuilder, TableAlterStatement, TableRef};
/// use sea_query::{Alias, IntoIden, MysqlQueryBuilder, TableAlterStatement, TableRef, ConditionType};
/// use sea_orm::{EnumIter, Iden, Identity, PrimaryKeyTrait, RelationDef, RelationTrait, RelationType};
///
/// let relation = RelationDef {
Expand All @@ -385,6 +434,7 @@ impl From<RelationDef> for ForeignKeyCreateStatement {
/// on_update: None,
/// on_condition: None,
/// fk_name: Some("foo-bar".to_string()),
/// condition_type: ConditionType::All,
/// };
///
/// let mut alter_table = TableAlterStatement::new()
Expand Down
11 changes: 8 additions & 3 deletions src/query/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::{
PrimaryKeyToColumn, RelationDef,
};
use sea_query::{
Alias, Expr, Iden, IntoCondition, IntoIden, LockType, SeaRc, SelectExpr, SelectStatement,
SimpleExpr, TableRef,
Alias, ConditionType, Expr, Iden, IntoCondition, IntoIden, LockType, SeaRc, SelectExpr,
SelectStatement, SimpleExpr, TableRef,
};
pub use sea_query::{Condition, ConditionalStatement, DynIden, JoinType, Order, OrderedStatement};

Expand Down Expand Up @@ -667,7 +667,12 @@ pub(crate) fn join_condition(mut rel: RelationDef) -> Condition {
let owner_keys = rel.from_col;
let foreign_keys = rel.to_col;

let mut condition = Condition::all().add(join_tbl_on_condition(
let mut condition = match rel.condition_type {
ConditionType::All => Condition::all(),
ConditionType::Any => Condition::any(),
};

condition = condition.add(join_tbl_on_condition(
SeaRc::clone(&from_tbl),
SeaRc::clone(&to_tbl),
owner_keys,
Expand Down
34 changes: 33 additions & 1 deletion src/query/join.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ mod tests {
RelationTrait,
};
use pretty_assertions::assert_eq;
use sea_query::{Alias, Expr, IntoCondition, JoinType};
use sea_query::{Alias, ConditionType, Expr, IntoCondition, JoinType};

#[test]
fn join_1() {
Expand Down Expand Up @@ -559,4 +559,36 @@ mod tests {
.join(" ")
);
}

#[test]
fn join_22() {
assert_eq!(
cake::Entity::find()
.column_as(
Expr::col((Alias::new("cake_filling_alias"), cake_filling::Column::CakeId)),
"cake_filling_cake_id"
)
.join(JoinType::LeftJoin, cake::Relation::OrTropicalFruit.def())
.join_as_rev(
JoinType::LeftJoin,
cake_filling::Relation::Cake
.def()
.condition_type(ConditionType::Any)
.on_condition(|left, _right| {
Expr::col((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` OR `fruit`.`name` LIKE '%tropical%'",
"LEFT JOIN `cake_filling` AS `cake_filling_alias` ON `cake_filling_alias`.`cake_id` = `cake`.`id` OR `cake_filling_alias`.`cake_id` > 10",
]
.join(" ")
);
}
}
6 changes: 6 additions & 0 deletions src/tests_cfg/cake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ pub enum Relation {
on_condition = r#"super::fruit::Column::Name.like("%tropical%")"#
)]
TropicalFruit,
#[sea_orm(
has_many = "super::fruit::Entity",
condition_type = "any",
on_condition = r#"super::fruit::Column::Name.like("%tropical%")"#
)]
OrTropicalFruit,
}

impl Related<super::fruit::Entity> for Entity {
Expand Down

0 comments on commit 25e6e06

Please sign in to comment.