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: ");
let select = cake::Entity::find()
.left_join(cake::Relation::Fruit)
.left_join(fruit::Entity)
.select_only()
.column(cake::Column::Name)
.column_as(fruit::Column::Id.count(), "num_of_fruits")

View File

@ -1,5 +1,5 @@
use crate::{EntityTrait, Identity, IntoIdentity, Select};
use sea_query::{Iden, IntoIden};
use sea_query::{Iden, IntoIden, JoinType};
use std::fmt::Debug;
use std::rc::Rc;
@ -24,7 +24,7 @@ where
}
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::{
ColumnTrait, EntityTrait, Identity, Iterable, ModelTrait, PrimaryKeyOfModel, Related,
RelationDef, RelationTrait, Statement,
RelationDef, Statement,
};
use core::fmt::Debug;
use core::marker::PhantomData;
@ -67,35 +67,6 @@ where
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 {
self.query.clear_selects();
self
@ -243,23 +214,71 @@ where
self
}
pub fn left_join(self, rel: E::Relation) -> Self {
self.prepare_join(JoinType::LeftJoin, E::Relation::def(&rel))
/// Join via [`RelationDef`].
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 {
self.prepare_join(JoinType::RightJoin, E::Relation::def(&rel))
/// Join via [`RelationDef`] but in reverse direction.
/// 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 {
self.prepare_join(JoinType::InnerJoin, E::Relation::def(&rel))
/// Left Join with a Related Entity.
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
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
@ -296,7 +315,7 @@ mod tests {
fn join_1() {
assert_eq!(
cake::Entity::find()
.left_join(cake::Relation::Fruit)
.left_join(fruit::Entity)
.build(MysqlQueryBuilder)
.to_string(),
[
@ -311,7 +330,7 @@ mod tests {
fn join_2() {
assert_eq!(
cake::Entity::find()
.inner_join(cake::Relation::Fruit)
.inner_join(fruit::Entity)
.filter(fruit::Column::Name.contains("cherry"))
.build(MysqlQueryBuilder)
.to_string(),
@ -328,7 +347,7 @@ mod tests {
fn join_3() {
assert_eq!(
fruit::Entity::find()
.reverse_join::<cake::Entity>()
.reverse_join(cake::Entity)
.build(MysqlQueryBuilder)
.to_string(),
[