Reverse join
This commit is contained in:
parent
97ba9d6c8d
commit
89af820f2c
@ -1,10 +1,10 @@
|
|||||||
use super::{ColumnTrait, Identity, ModelTrait, RelationTrait};
|
use super::{ColumnTrait, Identity, ModelTrait, RelationBuilder, RelationTrait, RelationType};
|
||||||
use crate::Select;
|
use crate::Select;
|
||||||
use sea_query::{Expr, Iden, Value};
|
use sea_query::{Expr, Iden, IntoIden, Value};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
pub use strum::IntoEnumIterator as Iterable;
|
pub use strum::IntoEnumIterator as Iterable;
|
||||||
|
|
||||||
pub trait EntityTrait: Iden + Default + Debug {
|
pub trait EntityTrait: Iden + Default + Debug + 'static {
|
||||||
type Model: ModelTrait;
|
type Model: ModelTrait;
|
||||||
|
|
||||||
type Column: ColumnTrait + Iterable;
|
type Column: ColumnTrait + Iterable;
|
||||||
@ -17,6 +17,27 @@ pub trait EntityTrait: Iden + Default + Debug {
|
|||||||
true
|
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};
|
/// use sea_orm::{ColumnTrait, EntityTrait, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
||||||
///
|
///
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
use crate::EntityTrait;
|
use crate::EntityTrait;
|
||||||
pub use sea_query::ColumnType;
|
pub use sea_query::ColumnType;
|
||||||
use sea_query::{Expr, Iden, SimpleExpr, Value};
|
use sea_query::{Expr, Iden, SimpleExpr, Value};
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
macro_rules! bind_oper {
|
macro_rules! bind_oper {
|
||||||
( $op: ident ) => {
|
( $op: ident ) => {
|
||||||
fn $op<V>(&'static self, v: V) -> SimpleExpr
|
fn $op<V>(&self, v: V) -> SimpleExpr
|
||||||
where
|
where
|
||||||
V: Into<Value>,
|
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;
|
type Entity: EntityTrait;
|
||||||
|
|
||||||
fn col_type(&self) -> ColumnType;
|
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>
|
Rc::new(Self::Entity::default()) as Rc<dyn Iden>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,14 +31,14 @@ pub trait ColumnTrait: Iden + Copy {
|
|||||||
bind_oper!(lt);
|
bind_oper!(lt);
|
||||||
bind_oper!(lte);
|
bind_oper!(lte);
|
||||||
|
|
||||||
fn between<V>(&'static self, a: V, b: V) -> SimpleExpr
|
fn between<V>(&self, a: V, b: V) -> SimpleExpr
|
||||||
where
|
where
|
||||||
V: Into<Value>,
|
V: Into<Value>,
|
||||||
{
|
{
|
||||||
Expr::tbl(self.entity_iden(), *self).between(a, b)
|
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
|
where
|
||||||
V: Into<Value>,
|
V: Into<Value>,
|
||||||
{
|
{
|
||||||
@ -55,7 +56,7 @@ pub trait ColumnTrait: Iden + Copy {
|
|||||||
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE 'cheese'"
|
/// "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)
|
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'"
|
/// "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)
|
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%'"
|
/// "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);
|
let pattern = format!("{}%", s);
|
||||||
Expr::tbl(self.entity_iden(), *self).like(&pattern)
|
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'"
|
/// "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);
|
let pattern = format!("%{}", s);
|
||||||
Expr::tbl(self.entity_iden(), *self).like(&pattern)
|
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%'"
|
/// "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);
|
let pattern = format!("%{}%", s);
|
||||||
Expr::tbl(self.entity_iden(), *self).like(&pattern)
|
Expr::tbl(self.entity_iden(), *self).like(&pattern)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::{Identity, IntoIdentity};
|
use super::{Identity, IntoIdentity};
|
||||||
use crate::EntityTrait;
|
|
||||||
use sea_query::{Iden, IntoIden};
|
use sea_query::{Iden, IntoIden};
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -10,12 +10,13 @@ pub enum RelationType {
|
|||||||
BelongsTo,
|
BelongsTo,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RelationTrait {
|
pub trait RelationTrait: Debug + 'static {
|
||||||
fn rel_def(&self) -> RelationDef;
|
fn rel_def(&self) -> RelationDef;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RelationDef {
|
pub struct RelationDef {
|
||||||
pub rel_type: RelationType,
|
pub rel_type: RelationType,
|
||||||
|
pub from_tbl: Rc<dyn Iden>,
|
||||||
pub to_tbl: Rc<dyn Iden>,
|
pub to_tbl: Rc<dyn Iden>,
|
||||||
pub from_col: Identity,
|
pub from_col: Identity,
|
||||||
pub to_col: Identity,
|
pub to_col: Identity,
|
||||||
@ -23,40 +24,22 @@ pub struct RelationDef {
|
|||||||
|
|
||||||
pub struct RelationBuilder {
|
pub struct RelationBuilder {
|
||||||
rel_type: RelationType,
|
rel_type: RelationType,
|
||||||
|
from_tbl: Rc<dyn Iden>,
|
||||||
to_tbl: Rc<dyn Iden>,
|
to_tbl: Rc<dyn Iden>,
|
||||||
from_col: Option<Identity>,
|
from_col: Option<Identity>,
|
||||||
to_col: Option<Identity>,
|
to_col: Option<Identity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RelationBuilder {
|
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
|
where
|
||||||
E: EntityTrait,
|
E: IntoIden,
|
||||||
{
|
T: IntoIden,
|
||||||
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,
|
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
rel_type,
|
rel_type,
|
||||||
to_tbl: entity.into_iden(),
|
from_tbl: from.into_iden(),
|
||||||
|
to_tbl: to.into_iden(),
|
||||||
from_col: None,
|
from_col: None,
|
||||||
to_col: None,
|
to_col: None,
|
||||||
}
|
}
|
||||||
@ -83,6 +66,7 @@ impl From<RelationBuilder> for RelationDef {
|
|||||||
fn from(b: RelationBuilder) -> Self {
|
fn from(b: RelationBuilder) -> Self {
|
||||||
RelationDef {
|
RelationDef {
|
||||||
rel_type: b.rel_type,
|
rel_type: b.rel_type,
|
||||||
|
from_tbl: b.from_tbl,
|
||||||
to_tbl: b.to_tbl,
|
to_tbl: b.to_tbl,
|
||||||
from_col: b.from_col.unwrap(),
|
from_col: b.from_col.unwrap(),
|
||||||
to_col: b.to_col.unwrap(),
|
to_col: b.to_col.unwrap(),
|
||||||
|
@ -42,25 +42,9 @@ where
|
|||||||
self
|
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 own_tbl = E::default().into_iden();
|
||||||
let to_tbl = rel.to_tbl.clone();
|
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 owner_keys = rel.from_col;
|
||||||
let foreign_keys = rel.to_col;
|
let foreign_keys = rel.to_col;
|
||||||
let condition = match (owner_keys, foreign_keys) {
|
let condition = match (owner_keys, foreign_keys) {
|
||||||
@ -72,6 +56,25 @@ where
|
|||||||
self
|
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};
|
/// 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))
|
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
|
/// Get a mutable ref to the query builder
|
||||||
pub fn query(&mut self) -> &mut SelectStatement {
|
pub fn query(&mut self) -> &mut SelectStatement {
|
||||||
&mut self.query
|
&mut self.query
|
||||||
@ -176,7 +172,11 @@ mod tests {
|
|||||||
.left_join(cake::Relation::Fruit)
|
.left_join(cake::Relation::Fruit)
|
||||||
.build(MysqlQueryBuilder)
|
.build(MysqlQueryBuilder)
|
||||||
.to_string(),
|
.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(),
|
.to_string(),
|
||||||
[
|
[
|
||||||
"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
|
"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(" ")
|
.join(" ")
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ColumnTrait, ColumnType, EntityTrait, EnumIter, Iden, Identity, IntoIdentity, ModelTrait,
|
ColumnTrait, ColumnType, EntityTrait, EnumIter, Iden, Identity, IntoIdentity, ModelTrait,
|
||||||
QueryResult, RelationBuilder, RelationDef, RelationTrait, TypeErr,
|
QueryResult, RelationDef, RelationTrait, TypeErr,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default, Debug, Iden)]
|
#[derive(Default, Debug, Iden)]
|
||||||
@ -13,13 +13,13 @@ pub struct Model {
|
|||||||
pub name: String,
|
pub name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Iden, EnumIter)]
|
#[derive(Copy, Clone, Debug, Iden, EnumIter)]
|
||||||
pub enum Column {
|
pub enum Column {
|
||||||
Id,
|
Id,
|
||||||
Name,
|
Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, EnumIter)]
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
pub enum Relation {
|
pub enum Relation {
|
||||||
Fruit,
|
Fruit,
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ impl ColumnTrait for Column {
|
|||||||
impl RelationTrait for Relation {
|
impl RelationTrait for Relation {
|
||||||
fn rel_def(&self) -> RelationDef {
|
fn rel_def(&self) -> RelationDef {
|
||||||
match self {
|
match self {
|
||||||
Self::Fruit => RelationBuilder::has_many(super::fruit::Entity)
|
Self::Fruit => Entity::has_many(super::fruit::Entity)
|
||||||
.from(Column::Id)
|
.from(Column::Id)
|
||||||
.to(super::fruit::Column::CakeId)
|
.to(super::fruit::Column::CakeId)
|
||||||
.into(),
|
.into(),
|
||||||
|
@ -14,14 +14,14 @@ pub struct Model {
|
|||||||
pub cake_id: Option<i32>,
|
pub cake_id: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Iden, EnumIter)]
|
#[derive(Copy, Clone, Debug, Iden, EnumIter)]
|
||||||
pub enum Column {
|
pub enum Column {
|
||||||
Id,
|
Id,
|
||||||
Name,
|
Name,
|
||||||
CakeId,
|
CakeId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, EnumIter)]
|
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||||
pub enum Relation {}
|
pub enum Relation {}
|
||||||
|
|
||||||
impl EntityTrait for Entity {
|
impl EntityTrait for Entity {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user