Relation join on AND
/ OR
condition (#1433)
This commit is contained in:
parent
ef78b6d3ff
commit
fbb16c73fa
@ -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>,
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
@ -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 {
|
||||
@ -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>
|
||||
@ -160,6 +163,7 @@ impl RelationDef {
|
||||
on_update: self.on_update,
|
||||
on_condition: self.on_condition,
|
||||
fk_name: None,
|
||||
condition_type: self.condition_type,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
@ -225,6 +265,7 @@ where
|
||||
on_update: None,
|
||||
on_condition: None,
|
||||
fk_name: None,
|
||||
condition_type: ConditionType::All,
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,6 +282,7 @@ where
|
||||
on_update: None,
|
||||
on_condition: None,
|
||||
fk_name: None,
|
||||
condition_type: ConditionType::All,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
@ -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()
|
||||
|
@ -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};
|
||||
|
||||
@ -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,
|
||||
|
@ -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() {
|
||||
@ -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(" ")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user