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};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Identity {
|
||||
Unary(DynIden),
|
||||
Binary(DynIden, DynIden),
|
||||
// Ternary(DynIden, DynIden, DynIden),
|
||||
Ternary(DynIden, DynIden, DynIden),
|
||||
}
|
||||
|
||||
pub trait IntoIdentity {
|
||||
fn into_identity(self) -> Identity;
|
||||
}
|
||||
|
||||
pub trait IdentityOf<E>
|
||||
where
|
||||
E: EntityTrait,
|
||||
{
|
||||
fn identity_of(self) -> Identity;
|
||||
}
|
||||
|
||||
impl<T> IntoIdentity for T
|
||||
where
|
||||
T: IdenStatic,
|
||||
@ -30,3 +37,44 @@ where
|
||||
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 sea_query::{DynIden, IntoIden, JoinType};
|
||||
use std::fmt::Debug;
|
||||
@ -89,13 +89,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from(mut self, identifier: E::Column) -> Self {
|
||||
self.from_col = Some(identifier.into_identity());
|
||||
pub fn from<T>(mut self, identifier: T) -> Self
|
||||
where
|
||||
T: IdentityOf<E>,
|
||||
{
|
||||
self.from_col = Some(identifier.identity_of());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn to(mut self, identifier: R::Column) -> Self {
|
||||
self.to_col = Some(identifier.into_identity());
|
||||
pub fn to<T>(mut self, identifier: T) -> Self
|
||||
where
|
||||
T: IdentityOf<R>,
|
||||
{
|
||||
self.to_col = Some(identifier.identity_of());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -283,6 +283,12 @@ fn join_condition(rel: RelationDef) -> SimpleExpr {
|
||||
.equals(SeaRc::clone(&to_tbl), f1)
|
||||
.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"),
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
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};
|
||||
|
||||
#[test]
|
||||
@ -182,4 +182,23 @@ mod tests {
|
||||
.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 {}
|
||||
|
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_filling;
|
||||
pub mod cake_filling_price;
|
||||
pub mod filling;
|
||||
pub mod fruit;
|
||||
|
||||
pub use cake::Entity as Cake;
|
||||
pub use cake_filling::Entity as CakeFilling;
|
||||
pub use cake_filling_price::Entity as CakeFillingPrice;
|
||||
pub use filling::Entity as Filling;
|
||||
pub use fruit::Entity as Fruit;
|
||||
|
Loading…
x
Reference in New Issue
Block a user