Reverse join

This commit is contained in:
Chris Tsang 2021-05-09 00:28:10 +08:00
parent 97ba9d6c8d
commit 89af820f2c
6 changed files with 77 additions and 71 deletions

View File

@ -1,10 +1,10 @@
use super::{ColumnTrait, Identity, ModelTrait, RelationTrait};
use super::{ColumnTrait, Identity, ModelTrait, RelationBuilder, RelationTrait, RelationType};
use crate::Select;
use sea_query::{Expr, Iden, Value};
use sea_query::{Expr, Iden, IntoIden, Value};
use std::fmt::Debug;
pub use strum::IntoEnumIterator as Iterable;
pub trait EntityTrait: Iden + Default + Debug {
pub trait EntityTrait: Iden + Default + Debug + 'static {
type Model: ModelTrait;
type Column: ColumnTrait + Iterable;
@ -17,6 +17,27 @@ pub trait EntityTrait: Iden + Default + Debug {
true
}
fn has_one<E>(entity: E) -> RelationBuilder
where
E: IntoIden,
{
RelationBuilder::new(RelationType::HasOne, Self::default(), entity)
}
fn has_many<E>(entity: E) -> RelationBuilder
where
E: IntoIden,
{
RelationBuilder::new(RelationType::HasMany, Self::default(), entity)
}
fn belongs_to<E>(entity: E) -> RelationBuilder
where
E: IntoIden,
{
RelationBuilder::new(RelationType::BelongsTo, Self::default(), entity)
}
/// ```
/// use sea_orm::{ColumnTrait, EntityTrait, tests_cfg::cake, sea_query::MysqlQueryBuilder};
///

View File

@ -1,11 +1,12 @@
use crate::EntityTrait;
pub use sea_query::ColumnType;
use sea_query::{Expr, Iden, SimpleExpr, Value};
use std::fmt::Debug;
use std::rc::Rc;
macro_rules! bind_oper {
( $op: ident ) => {
fn $op<V>(&'static self, v: V) -> SimpleExpr
fn $op<V>(&self, v: V) -> SimpleExpr
where
V: Into<Value>,
{
@ -14,12 +15,12 @@ macro_rules! bind_oper {
};
}
pub trait ColumnTrait: Iden + Copy {
pub trait ColumnTrait: Iden + Copy + Debug + 'static {
type Entity: EntityTrait;
fn col_type(&self) -> ColumnType;
fn entity_iden(&'static self) -> Rc<dyn Iden> {
fn entity_iden(&self) -> Rc<dyn Iden> {
Rc::new(Self::Entity::default()) as Rc<dyn Iden>
}
@ -30,14 +31,14 @@ pub trait ColumnTrait: Iden + Copy {
bind_oper!(lt);
bind_oper!(lte);
fn between<V>(&'static self, a: V, b: V) -> SimpleExpr
fn between<V>(&self, a: V, b: V) -> SimpleExpr
where
V: Into<Value>,
{
Expr::tbl(self.entity_iden(), *self).between(a, b)
}
fn not_between<V>(&'static self, a: V, b: V) -> SimpleExpr
fn not_between<V>(&self, a: V, b: V) -> SimpleExpr
where
V: Into<Value>,
{
@ -55,7 +56,7 @@ pub trait ColumnTrait: Iden + Copy {
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE 'cheese'"
/// );
/// ```
fn like(&'static self, s: &str) -> SimpleExpr {
fn like(&self, s: &str) -> SimpleExpr {
Expr::tbl(self.entity_iden(), *self).like(s)
}
@ -70,7 +71,7 @@ pub trait ColumnTrait: Iden + Copy {
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` NOT LIKE 'cheese'"
/// );
/// ```
fn not_like(&'static self, s: &str) -> SimpleExpr {
fn not_like(&self, s: &str) -> SimpleExpr {
Expr::tbl(self.entity_iden(), *self).not_like(s)
}
@ -85,7 +86,7 @@ pub trait ColumnTrait: Iden + Copy {
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE 'cheese%'"
/// );
/// ```
fn starts_with(&'static self, s: &str) -> SimpleExpr {
fn starts_with(&self, s: &str) -> SimpleExpr {
let pattern = format!("{}%", s);
Expr::tbl(self.entity_iden(), *self).like(&pattern)
}
@ -101,7 +102,7 @@ pub trait ColumnTrait: Iden + Copy {
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE '%cheese'"
/// );
/// ```
fn ends_with(&'static self, s: &str) -> SimpleExpr {
fn ends_with(&self, s: &str) -> SimpleExpr {
let pattern = format!("%{}", s);
Expr::tbl(self.entity_iden(), *self).like(&pattern)
}
@ -117,7 +118,7 @@ pub trait ColumnTrait: Iden + Copy {
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE '%cheese%'"
/// );
/// ```
fn contains(&'static self, s: &str) -> SimpleExpr {
fn contains(&self, s: &str) -> SimpleExpr {
let pattern = format!("%{}%", s);
Expr::tbl(self.entity_iden(), *self).like(&pattern)
}

View File

@ -1,6 +1,6 @@
use super::{Identity, IntoIdentity};
use crate::EntityTrait;
use sea_query::{Iden, IntoIden};
use std::fmt::Debug;
use std::rc::Rc;
#[derive(Debug)]
@ -10,12 +10,13 @@ pub enum RelationType {
BelongsTo,
}
pub trait RelationTrait {
pub trait RelationTrait: Debug + 'static {
fn rel_def(&self) -> RelationDef;
}
pub struct RelationDef {
pub rel_type: RelationType,
pub from_tbl: Rc<dyn Iden>,
pub to_tbl: Rc<dyn Iden>,
pub from_col: Identity,
pub to_col: Identity,
@ -23,40 +24,22 @@ pub struct RelationDef {
pub struct RelationBuilder {
rel_type: RelationType,
from_tbl: Rc<dyn Iden>,
to_tbl: Rc<dyn Iden>,
from_col: Option<Identity>,
to_col: Option<Identity>,
}
impl RelationBuilder {
pub fn has_one<E: 'static>(entity: E) -> Self
pub(crate) fn new<E, T>(rel_type: RelationType, from: E, to: T) -> Self
where
E: EntityTrait,
{
Self::new(RelationType::HasOne, entity)
}
pub fn has_many<E: 'static>(entity: E) -> Self
where
E: EntityTrait,
{
Self::new(RelationType::HasMany, entity)
}
pub fn belongs_to<E: 'static>(entity: E) -> Self
where
E: EntityTrait,
{
Self::new(RelationType::BelongsTo, entity)
}
fn new<E: 'static>(rel_type: RelationType, entity: E) -> Self
where
E: EntityTrait,
E: IntoIden,
T: IntoIden,
{
Self {
rel_type,
to_tbl: entity.into_iden(),
from_tbl: from.into_iden(),
to_tbl: to.into_iden(),
from_col: None,
to_col: None,
}
@ -83,6 +66,7 @@ impl From<RelationBuilder> for RelationDef {
fn from(b: RelationBuilder) -> Self {
RelationDef {
rel_type: b.rel_type,
from_tbl: b.from_tbl,
to_tbl: b.to_tbl,
from_col: b.from_col.unwrap(),
to_col: b.to_col.unwrap(),

View File

@ -42,25 +42,9 @@ where
self
}
fn prepare_join(self, join: JoinType, rel: RelationDef) -> Self {
fn prepare_join(mut self, join: JoinType, rel: RelationDef) -> Self {
let own_tbl = E::default().into_iden();
let to_tbl = rel.to_tbl.clone();
self.prepare_join_impl(join, rel, own_tbl, to_tbl)
}
fn prepare_join_inverse(self, join: JoinType, rel: RelationDef) -> Self {
let own_tbl = E::default().into_iden();
let to_tbl = rel.to_tbl.clone();
self.prepare_join_impl(join, rel, to_tbl, own_tbl)
}
fn prepare_join_impl(
mut self,
join: JoinType,
rel: RelationDef,
own_tbl: Rc<dyn Iden>,
to_tbl: Rc<dyn Iden>,
) -> Self {
let owner_keys = rel.from_col;
let foreign_keys = rel.to_col;
let condition = match (owner_keys, foreign_keys) {
@ -72,6 +56,25 @@ where
self
}
pub fn reverse_join<R>(mut self, rel: R) -> Self
where
R: RelationTrait,
{
let rel = rel.rel_def();
let from_tbl = rel.from_tbl.clone();
let to_tbl = rel.to_tbl.clone();
let owner_keys = rel.from_col;
let foreign_keys = rel.to_col;
let condition = match (owner_keys, foreign_keys) {
(Identity::Unary(o1), Identity::Unary(f1)) => {
Expr::tbl(Rc::clone(&from_tbl), o1).equals(Rc::clone(&to_tbl), f1)
} // _ => panic!("Owner key and foreign key mismatch"),
};
self.query
.join(JoinType::InnerJoin, Rc::clone(&from_tbl), condition);
self
}
/// ```
/// use sea_orm::{ColumnTrait, EntityTrait, tests_cfg::cake, sea_query::MysqlQueryBuilder};
///
@ -132,13 +135,6 @@ where
self.prepare_join(JoinType::InnerJoin, E::Relation::rel_def(&rel))
}
pub fn reverse_join<R>(self, rel: R) -> Self
where
R: RelationTrait,
{
self
}
/// Get a mutable ref to the query builder
pub fn query(&mut self) -> &mut SelectStatement {
&mut self.query
@ -176,7 +172,11 @@ mod tests {
.left_join(cake::Relation::Fruit)
.build(MysqlQueryBuilder)
.to_string(),
"SELECT `cake`.`id`, `cake`.`name` FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`"
[
"SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
"LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
]
.join(" ")
);
}
@ -206,7 +206,7 @@ mod tests {
.to_string(),
[
"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
"INNER JOIN `cake` ON `fruit`.`cake_id` = `cake`.`id`",
"INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
]
.join(" ")
);

View File

@ -1,6 +1,6 @@
use crate::{
ColumnTrait, ColumnType, EntityTrait, EnumIter, Iden, Identity, IntoIdentity, ModelTrait,
QueryResult, RelationBuilder, RelationDef, RelationTrait, TypeErr,
QueryResult, RelationDef, RelationTrait, TypeErr,
};
#[derive(Default, Debug, Iden)]
@ -13,13 +13,13 @@ pub struct Model {
pub name: String,
}
#[derive(Copy, Clone, Iden, EnumIter)]
#[derive(Copy, Clone, Debug, Iden, EnumIter)]
pub enum Column {
Id,
Name,
}
#[derive(Copy, Clone, EnumIter)]
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Fruit,
}
@ -59,7 +59,7 @@ impl ColumnTrait for Column {
impl RelationTrait for Relation {
fn rel_def(&self) -> RelationDef {
match self {
Self::Fruit => RelationBuilder::has_many(super::fruit::Entity)
Self::Fruit => Entity::has_many(super::fruit::Entity)
.from(Column::Id)
.to(super::fruit::Column::CakeId)
.into(),

View File

@ -14,14 +14,14 @@ pub struct Model {
pub cake_id: Option<i32>,
}
#[derive(Copy, Clone, Iden, EnumIter)]
#[derive(Copy, Clone, Debug, Iden, EnumIter)]
pub enum Column {
Id,
Name,
CakeId,
}
#[derive(Copy, Clone, EnumIter)]
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {}
impl EntityTrait for Entity {