Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relation join on AND / OR condition #1433

Merged
merged 1 commit into from
Apr 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sea-orm-macros/src/derives/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 @@ -690,7 +690,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