Support join on multiple columns

This commit is contained in:
Billy Chan 2021-08-13 18:40:20 +08:00 committed by Chris Tsang
parent 69b0ae1177
commit e76928f1cc
7 changed files with 171 additions and 8 deletions

View File

@ -1,17 +1,24 @@
use crate::IdenStatic; use crate::{ColumnTrait, EntityTrait, IdenStatic};
use sea_query::{DynIden, IntoIden}; use sea_query::{DynIden, IntoIden};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Identity { pub enum Identity {
Unary(DynIden), Unary(DynIden),
Binary(DynIden, DynIden), Binary(DynIden, DynIden),
// Ternary(DynIden, DynIden, DynIden), Ternary(DynIden, DynIden, DynIden),
} }
pub trait IntoIdentity { pub trait IntoIdentity {
fn into_identity(self) -> Identity; fn into_identity(self) -> Identity;
} }
pub trait IdentityOf<E>
where
E: EntityTrait,
{
fn identity_of(self) -> Identity;
}
impl<T> IntoIdentity for T impl<T> IntoIdentity for T
where where
T: IdenStatic, T: IdenStatic,
@ -30,3 +37,44 @@ where
Identity::Binary(self.0.into_iden(), self.1.into_iden()) Identity::Binary(self.0.into_iden(), self.1.into_iden())
} }
} }
impl<T, C, R> IntoIdentity for (T, C, R)
where
T: IdenStatic,
C: IdenStatic,
R: IdenStatic,
{
fn into_identity(self) -> Identity {
Identity::Ternary(self.0.into_iden(), self.1.into_iden(), self.2.into_iden())
}
}
impl<E, C> IdentityOf<E> for C
where
E: EntityTrait<Column = C>,
C: ColumnTrait,
{
fn identity_of(self) -> Identity {
self.into_identity()
}
}
impl<E, C> IdentityOf<E> for (C, C)
where
E: EntityTrait<Column = C>,
C: ColumnTrait,
{
fn identity_of(self) -> Identity {
self.into_identity()
}
}
impl<E, C> IdentityOf<E> for (C, C, C)
where
E: EntityTrait<Column = C>,
C: ColumnTrait,
{
fn identity_of(self) -> Identity {
self.into_identity()
}
}

View File

@ -1,4 +1,4 @@
use crate::{EntityTrait, Identity, IntoIdentity, Iterable, QuerySelect, Select}; use crate::{EntityTrait, Identity, IdentityOf, Iterable, QuerySelect, Select};
use core::marker::PhantomData; use core::marker::PhantomData;
use sea_query::{DynIden, IntoIden, JoinType}; use sea_query::{DynIden, IntoIden, JoinType};
use std::fmt::Debug; use std::fmt::Debug;
@ -89,13 +89,19 @@ where
} }
} }
pub fn from(mut self, identifier: E::Column) -> Self { pub fn from<T>(mut self, identifier: T) -> Self
self.from_col = Some(identifier.into_identity()); where
T: IdentityOf<E>,
{
self.from_col = Some(identifier.identity_of());
self self
} }
pub fn to(mut self, identifier: R::Column) -> Self { pub fn to<T>(mut self, identifier: T) -> Self
self.to_col = Some(identifier.into_identity()); where
T: IdentityOf<R>,
{
self.to_col = Some(identifier.identity_of());
self self
} }
} }

View File

@ -283,6 +283,12 @@ fn join_condition(rel: RelationDef) -> SimpleExpr {
.equals(SeaRc::clone(&to_tbl), f1) .equals(SeaRc::clone(&to_tbl), f1)
.and(Expr::tbl(SeaRc::clone(&from_tbl), o2).equals(SeaRc::clone(&to_tbl), f2)) .and(Expr::tbl(SeaRc::clone(&from_tbl), o2).equals(SeaRc::clone(&to_tbl), f2))
} }
(Identity::Ternary(o1, o2, o3), Identity::Ternary(f1, f2, f3)) => {
Expr::tbl(SeaRc::clone(&from_tbl), o1)
.equals(SeaRc::clone(&to_tbl), f1)
.and(Expr::tbl(SeaRc::clone(&from_tbl), o2).equals(SeaRc::clone(&to_tbl), f2))
.and(Expr::tbl(SeaRc::clone(&from_tbl), o3).equals(SeaRc::clone(&to_tbl), f3))
}
_ => panic!("Owner key and foreign key mismatch"), _ => panic!("Owner key and foreign key mismatch"),
} }
} }

View File

@ -61,7 +61,7 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::tests_cfg::{cake, filling, fruit}; use crate::tests_cfg::{cake, cake_filling, cake_filling_price, filling, fruit};
use crate::{ColumnTrait, DbBackend, EntityTrait, ModelTrait, QueryFilter, QueryTrait}; use crate::{ColumnTrait, DbBackend, EntityTrait, ModelTrait, QueryFilter, QueryTrait};
#[test] #[test]
@ -182,4 +182,23 @@ mod tests {
.join(" ") .join(" ")
); );
} }
#[test]
fn join_8() {
use crate::{Related, Select};
let find_cake_filling_price: Select<cake_filling_price::Entity> =
cake_filling::Entity::find_related();
assert_eq!(
find_cake_filling_price.build(DbBackend::MySql).to_string(),
[
"SELECT `cake_filling_price`.`cake_id`, `cake_filling_price`.`filling_id`, `cake_filling_price`.`price`",
"FROM `cake_filling_price`",
"INNER JOIN `cake_filling` ON",
"(`cake_filling`.`cake_id` = `cake_filling_price`.`cake_id`) AND",
"(`cake_filling`.`filling_id` = `cake_filling_price`.`filling_id`)",
]
.join(" ")
);
}
} }

View File

@ -66,4 +66,10 @@ impl RelationTrait for Relation {
} }
} }
impl Related<super::cake_filling_price::Entity> for Entity {
fn to() -> RelationDef {
super::cake_filling_price::Relation::CakeFilling.def().rev()
}
}
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}

View File

@ -0,0 +1,76 @@
use crate as sea_orm;
use crate::entity::prelude::*;
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
pub struct Entity;
impl EntityName for Entity {
fn table_name(&self) -> &str {
"cake_filling_price"
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
pub struct Model {
pub cake_id: i32,
pub filling_id: i32,
pub price: Decimal,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
pub enum Column {
CakeId,
FillingId,
Price,
}
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
pub enum PrimaryKey {
CakeId,
FillingId,
}
impl PrimaryKeyTrait for PrimaryKey {
fn auto_increment() -> bool {
false
}
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
CakeFilling,
}
impl ColumnTrait for Column {
type EntityName = Entity;
fn def(&self) -> ColumnDef {
match self {
Self::CakeId => ColumnType::Integer.def(),
Self::FillingId => ColumnType::Integer.def(),
Self::Price => ColumnType::Decimal(None).def(),
}
}
}
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::CakeFilling => Entity::belongs_to(super::cake_filling::Entity)
.from((Column::CakeId, Column::FillingId))
.to((
super::cake_filling::Column::CakeId,
super::cake_filling::Column::FillingId,
))
.into(),
}
}
}
impl Related<super::cake_filling::Entity> for Entity {
fn to() -> RelationDef {
Relation::CakeFilling.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View File

@ -2,10 +2,12 @@
pub mod cake; pub mod cake;
pub mod cake_filling; pub mod cake_filling;
pub mod cake_filling_price;
pub mod filling; pub mod filling;
pub mod fruit; pub mod fruit;
pub use cake::Entity as Cake; pub use cake::Entity as Cake;
pub use cake_filling::Entity as CakeFilling; pub use cake_filling::Entity as CakeFilling;
pub use cake_filling_price::Entity as CakeFillingPrice;
pub use filling::Entity as Filling; pub use filling::Entity as Filling;
pub use fruit::Entity as Fruit; pub use fruit::Entity as Fruit;