Join APIs

This commit is contained in:
Chris Tsang 2021-05-12 13:45:41 +08:00
parent 634b5aca7a
commit 1bec83ff64
3 changed files with 63 additions and 44 deletions

View File

@ -105,7 +105,7 @@ async fn count_fruits_by_cake(db: &Database) -> Result<(), QueryErr> {
print!("count fruits by cake: "); print!("count fruits by cake: ");
let select = cake::Entity::find() let select = cake::Entity::find()
.left_join(cake::Relation::Fruit) .left_join(fruit::Entity)
.select_only() .select_only()
.column(cake::Column::Name) .column(cake::Column::Name)
.column_as(fruit::Column::Id.count(), "num_of_fruits") .column_as(fruit::Column::Id.count(), "num_of_fruits")

View File

@ -1,5 +1,5 @@
use crate::{EntityTrait, Identity, IntoIdentity, Select}; use crate::{EntityTrait, Identity, IntoIdentity, Select};
use sea_query::{Iden, IntoIden}; use sea_query::{Iden, IntoIden, JoinType};
use std::fmt::Debug; use std::fmt::Debug;
use std::rc::Rc; use std::rc::Rc;
@ -24,7 +24,7 @@ where
} }
fn find_related() -> Select<R> { fn find_related() -> Select<R> {
Select::<R>::new().prepare_reverse_join(Self::to()) Select::<R>::new().join_rev(JoinType::InnerJoin, Self::to())
} }
} }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
ColumnTrait, EntityTrait, Identity, Iterable, ModelTrait, PrimaryKeyOfModel, Related, ColumnTrait, EntityTrait, Identity, Iterable, ModelTrait, PrimaryKeyOfModel, Related,
RelationDef, RelationTrait, Statement, RelationDef, Statement,
}; };
use core::fmt::Debug; use core::fmt::Debug;
use core::marker::PhantomData; use core::marker::PhantomData;
@ -67,35 +67,6 @@ where
self 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();
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(&own_tbl), o1).equals(Rc::clone(&to_tbl), f1)
} // _ => panic!("Owner key and foreign key mismatch"),
};
self.query.join(join, Rc::clone(&to_tbl), condition);
self
}
pub(crate) fn prepare_reverse_join(mut self, rel: RelationDef) -> Self {
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
}
pub fn select_only(mut self) -> Self { pub fn select_only(mut self) -> Self {
self.query.clear_selects(); self.query.clear_selects();
self self
@ -243,23 +214,71 @@ where
self self
} }
pub fn left_join(self, rel: E::Relation) -> Self { /// Join via [`RelationDef`].
self.prepare_join(JoinType::LeftJoin, E::Relation::def(&rel)) pub fn join(mut self, join: JoinType, rel: RelationDef) -> Self {
let own_tbl = E::default().into_iden();
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(&own_tbl), o1).equals(Rc::clone(&to_tbl), f1)
} // _ => panic!("Owner key and foreign key mismatch"),
};
self.query.join(join, Rc::clone(&to_tbl), condition);
self
} }
pub fn right_join(self, rel: E::Relation) -> Self { /// Join via [`RelationDef`] but in reverse direction.
self.prepare_join(JoinType::RightJoin, E::Relation::def(&rel)) /// Assume when there exist a relation A -> B.
/// You can reverse join B <- A.
pub fn join_rev(mut self, join: JoinType, rel: RelationDef) -> Self {
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(join, Rc::clone(&from_tbl), condition);
self
} }
pub fn inner_join(self, rel: E::Relation) -> Self { /// Left Join with a Related Entity.
self.prepare_join(JoinType::InnerJoin, E::Relation::def(&rel)) pub fn left_join<R>(self, _: R) -> Self
where
R: EntityTrait,
E: Related<R>,
{
self.join(JoinType::LeftJoin, E::to())
} }
pub fn reverse_join<R>(self) -> Self /// Right Join with a Related Entity.
pub fn right_join<R>(self, _: R) -> Self
where
R: EntityTrait,
E: Related<R>,
{
self.join(JoinType::RightJoin, E::to())
}
/// Inner Join with a Related Entity.
pub fn inner_join<R>(self, _: R) -> Self
where
R: EntityTrait,
E: Related<R>,
{
self.join(JoinType::InnerJoin, E::to())
}
/// Join with an Entity Related to me.
pub fn reverse_join<R>(self, _: R) -> Self
where where
R: EntityTrait + Related<E>, R: EntityTrait + Related<E>,
{ {
self.prepare_reverse_join(R::to()) self.join_rev(JoinType::InnerJoin, R::to())
} }
/// Get a mutable ref to the query builder /// Get a mutable ref to the query builder
@ -296,7 +315,7 @@ mod tests {
fn join_1() { fn join_1() {
assert_eq!( assert_eq!(
cake::Entity::find() cake::Entity::find()
.left_join(cake::Relation::Fruit) .left_join(fruit::Entity)
.build(MysqlQueryBuilder) .build(MysqlQueryBuilder)
.to_string(), .to_string(),
[ [
@ -311,7 +330,7 @@ mod tests {
fn join_2() { fn join_2() {
assert_eq!( assert_eq!(
cake::Entity::find() cake::Entity::find()
.inner_join(cake::Relation::Fruit) .inner_join(fruit::Entity)
.filter(fruit::Column::Name.contains("cherry")) .filter(fruit::Column::Name.contains("cherry"))
.build(MysqlQueryBuilder) .build(MysqlQueryBuilder)
.to_string(), .to_string(),
@ -328,7 +347,7 @@ mod tests {
fn join_3() { fn join_3() {
assert_eq!( assert_eq!(
fruit::Entity::find() fruit::Entity::find()
.reverse_join::<cake::Entity>() .reverse_join(cake::Entity)
.build(MysqlQueryBuilder) .build(MysqlQueryBuilder)
.to_string(), .to_string(),
[ [