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 from: Option<syn::Lit>,
|
||||||
pub to: Option<syn::Lit>,
|
pub to: Option<syn::Lit>,
|
||||||
pub fk_name: 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) };
|
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 = quote! { #result.into() };
|
||||||
|
|
||||||
Result::<_, syn::Error>::Ok(result)
|
Result::<_, syn::Error>::Ok(result)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::{unpack_table_ref, EntityTrait, Identity, IdentityOf, Iterable, QuerySelect, Select};
|
use crate::{unpack_table_ref, EntityTrait, Identity, IdentityOf, Iterable, QuerySelect, Select};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use sea_query::{
|
use sea_query::{
|
||||||
Alias, Condition, DynIden, ForeignKeyCreateStatement, JoinType, SeaRc, TableForeignKey,
|
Alias, Condition, ConditionType, DynIden, ForeignKeyCreateStatement, JoinType, SeaRc,
|
||||||
TableRef,
|
TableForeignKey, TableRef,
|
||||||
};
|
};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
@ -68,6 +68,8 @@ pub struct RelationDef {
|
|||||||
pub on_condition: Option<Box<dyn Fn(DynIden, DynIden) -> Condition + Send + Sync>>,
|
pub on_condition: Option<Box<dyn Fn(DynIden, DynIden) -> Condition + Send + Sync>>,
|
||||||
/// The name of foreign key constraint
|
/// The name of foreign key constraint
|
||||||
pub fk_name: Option<String>,
|
pub fk_name: Option<String>,
|
||||||
|
/// Condition type of join on expression
|
||||||
|
pub condition_type: ConditionType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for RelationDef {
|
impl std::fmt::Debug for RelationDef {
|
||||||
@ -123,6 +125,7 @@ where
|
|||||||
on_update: Option<ForeignKeyAction>,
|
on_update: Option<ForeignKeyAction>,
|
||||||
on_condition: Option<Box<dyn Fn(DynIden, DynIden) -> Condition + Send + Sync>>,
|
on_condition: Option<Box<dyn Fn(DynIden, DynIden) -> Condition + Send + Sync>>,
|
||||||
fk_name: Option<String>,
|
fk_name: Option<String>,
|
||||||
|
condition_type: ConditionType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, R> std::fmt::Debug for RelationBuilder<E, R>
|
impl<E, R> std::fmt::Debug for RelationBuilder<E, R>
|
||||||
@ -160,6 +163,7 @@ impl RelationDef {
|
|||||||
on_update: self.on_update,
|
on_update: self.on_update,
|
||||||
on_condition: self.on_condition,
|
on_condition: self.on_condition,
|
||||||
fk_name: None,
|
fk_name: None,
|
||||||
|
condition_type: self.condition_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,6 +209,42 @@ impl RelationDef {
|
|||||||
self.on_condition = Some(Box::new(f));
|
self.on_condition = Some(Box::new(f));
|
||||||
self
|
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>
|
impl<E, R> RelationBuilder<E, R>
|
||||||
@ -225,6 +265,7 @@ where
|
|||||||
on_update: None,
|
on_update: None,
|
||||||
on_condition: None,
|
on_condition: None,
|
||||||
fk_name: None,
|
fk_name: None,
|
||||||
|
condition_type: ConditionType::All,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,6 +282,7 @@ where
|
|||||||
on_update: None,
|
on_update: None,
|
||||||
on_condition: None,
|
on_condition: None,
|
||||||
fk_name: None,
|
fk_name: None,
|
||||||
|
condition_type: ConditionType::All,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,6 +333,12 @@ where
|
|||||||
self.fk_name = Some(fk_name.to_owned());
|
self.fk_name = Some(fk_name.to_owned());
|
||||||
self
|
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
|
impl<E, R> From<RelationBuilder<E, R>> for RelationDef
|
||||||
@ -310,6 +358,7 @@ where
|
|||||||
on_update: b.on_update,
|
on_update: b.on_update,
|
||||||
on_condition: b.on_condition,
|
on_condition: b.on_condition,
|
||||||
fk_name: b.fk_name,
|
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.
|
/// 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};
|
/// use sea_orm::{EnumIter, Iden, Identity, PrimaryKeyTrait, RelationDef, RelationTrait, RelationType};
|
||||||
///
|
///
|
||||||
/// let relation = RelationDef {
|
/// let relation = RelationDef {
|
||||||
@ -385,6 +434,7 @@ impl From<RelationDef> for ForeignKeyCreateStatement {
|
|||||||
/// on_update: None,
|
/// on_update: None,
|
||||||
/// on_condition: None,
|
/// on_condition: None,
|
||||||
/// fk_name: Some("foo-bar".to_string()),
|
/// fk_name: Some("foo-bar".to_string()),
|
||||||
|
/// condition_type: ConditionType::All,
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// let mut alter_table = TableAlterStatement::new()
|
/// let mut alter_table = TableAlterStatement::new()
|
||||||
|
@ -3,8 +3,8 @@ use crate::{
|
|||||||
PrimaryKeyToColumn, RelationDef,
|
PrimaryKeyToColumn, RelationDef,
|
||||||
};
|
};
|
||||||
use sea_query::{
|
use sea_query::{
|
||||||
Alias, Expr, Iden, IntoCondition, IntoIden, LockType, SeaRc, SelectExpr, SelectStatement,
|
Alias, ConditionType, Expr, Iden, IntoCondition, IntoIden, LockType, SeaRc, SelectExpr,
|
||||||
SimpleExpr, TableRef,
|
SelectStatement, SimpleExpr, TableRef,
|
||||||
};
|
};
|
||||||
pub use sea_query::{Condition, ConditionalStatement, DynIden, JoinType, Order, OrderedStatement};
|
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 owner_keys = rel.from_col;
|
||||||
let foreign_keys = rel.to_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(&from_tbl),
|
||||||
SeaRc::clone(&to_tbl),
|
SeaRc::clone(&to_tbl),
|
||||||
owner_keys,
|
owner_keys,
|
||||||
|
@ -117,7 +117,7 @@ mod tests {
|
|||||||
RelationTrait,
|
RelationTrait,
|
||||||
};
|
};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use sea_query::{Alias, Expr, IntoCondition, JoinType};
|
use sea_query::{Alias, ConditionType, Expr, IntoCondition, JoinType};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn join_1() {
|
fn join_1() {
|
||||||
@ -559,4 +559,36 @@ mod tests {
|
|||||||
.join(" ")
|
.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%")"#
|
on_condition = r#"super::fruit::Column::Name.like("%tropical%")"#
|
||||||
)]
|
)]
|
||||||
TropicalFruit,
|
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 {
|
impl Related<super::fruit::Entity> for Entity {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user