Support join on multiple columns
This commit is contained in:
parent
69b0ae1177
commit
e76928f1cc
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(" ")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {}
|
||||||
|
76
src/tests_cfg/cake_filling_price.rs
Normal file
76
src/tests_cfg/cake_filling_price.rs
Normal 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 {}
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user