Select Both
This commit is contained in:
parent
9dc7c818d7
commit
abc11753a8
@ -104,7 +104,7 @@ impl ModelTrait for Model {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_query_result(row: QueryResult, pre: &str) -> Result<Self, TypeErr> {
|
||||
fn from_query_result(row: &QueryResult, pre: &str) -> Result<Self, TypeErr> {
|
||||
Ok(Self {
|
||||
id: row.try_get(pre, Column::Id.as_str())?,
|
||||
name: row.try_get(pre, Column::Name.as_str())?,
|
||||
|
@ -90,7 +90,7 @@ impl ModelTrait for Model {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_query_result(row: QueryResult, pre: &str) -> Result<Self, TypeErr> {
|
||||
fn from_query_result(row: &QueryResult, pre: &str) -> Result<Self, TypeErr> {
|
||||
Ok(Self {
|
||||
id: row.try_get(pre, Column::Id.as_str())?,
|
||||
name: row.try_get(pre, Column::Name.as_str())?,
|
||||
|
@ -14,13 +14,22 @@ async fn main() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
println!("{:?}", db);
|
||||
println!();
|
||||
println!("{:?}\n", db);
|
||||
|
||||
println!("===== =====\n");
|
||||
|
||||
find_all(&db).await.unwrap();
|
||||
|
||||
println!("===== =====\n");
|
||||
|
||||
find_together(&db).await.unwrap();
|
||||
|
||||
println!("===== =====\n");
|
||||
|
||||
find_one(&db).await.unwrap();
|
||||
|
||||
println!("===== =====\n");
|
||||
|
||||
count_fruits_by_cake(&db).await.unwrap();
|
||||
}
|
||||
|
||||
@ -31,8 +40,7 @@ async fn find_all(db: &Database) -> Result<(), QueryErr> {
|
||||
|
||||
println!();
|
||||
for cc in cakes.iter() {
|
||||
println!("{:?}", cc);
|
||||
println!();
|
||||
println!("{:?}\n", cc);
|
||||
}
|
||||
|
||||
print!("find all fruits: ");
|
||||
@ -41,8 +49,20 @@ async fn find_all(db: &Database) -> Result<(), QueryErr> {
|
||||
|
||||
println!();
|
||||
for ff in fruits.iter() {
|
||||
println!("{:?}", ff);
|
||||
println!();
|
||||
println!("{:?}\n", ff);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn find_together(db: &Database) -> Result<(), QueryErr> {
|
||||
print!("find cakes and fruits: ");
|
||||
|
||||
let both = cake::Entity::find().left_join_and_select(fruit::Entity).all(db).await?;
|
||||
|
||||
println!();
|
||||
for bb in both.iter() {
|
||||
println!("{:?}\n", bb);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -74,8 +94,7 @@ async fn find_one(db: &Database) -> Result<(), QueryErr> {
|
||||
|
||||
println!();
|
||||
for ff in fruits.iter() {
|
||||
println!("{:?}", ff);
|
||||
println!();
|
||||
println!("{:?}\n", ff);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -93,7 +112,7 @@ async fn count_fruits_by_cake(db: &Database) -> Result<(), QueryErr> {
|
||||
use sea_orm::{FromQueryResult, QueryResult, TypeErr};
|
||||
|
||||
impl FromQueryResult for SelectResult {
|
||||
fn from_query_result(row: QueryResult, pre: &str) -> Result<Self, TypeErr> {
|
||||
fn from_query_result(row: &QueryResult, pre: &str) -> Result<Self, TypeErr> {
|
||||
Ok(Self {
|
||||
name: row.try_get(pre, "name")?,
|
||||
num_of_fruits: row.try_get(pre, "num_of_fruits")?,
|
||||
@ -115,8 +134,7 @@ async fn count_fruits_by_cake(db: &Database) -> Result<(), QueryErr> {
|
||||
|
||||
println!();
|
||||
for rr in results.iter() {
|
||||
println!("{:?}", rr);
|
||||
println!();
|
||||
println!("{:?}\n", rr);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::{Connection, Database, EntityTrait, FromQueryResult, QueryErr, Select, Statement};
|
||||
use crate::query::combine;
|
||||
use crate::{Connection, Database, EntityTrait, FromQueryResult, QueryErr, Select, SelectTwo, Statement};
|
||||
use sea_query::{QueryBuilder, SelectStatement};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
@ -21,7 +22,7 @@ where
|
||||
model: PhantomData<(M, N)>,
|
||||
}
|
||||
|
||||
impl<E: 'static> Select<E>
|
||||
impl<E> Select<E>
|
||||
where
|
||||
E: EntityTrait,
|
||||
{
|
||||
@ -44,6 +45,31 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, F> SelectTwo<E, F>
|
||||
where
|
||||
E: EntityTrait,
|
||||
F: EntityTrait,
|
||||
{
|
||||
fn into_model<M, N>(self) -> SelectTwoModel<M, N>
|
||||
where
|
||||
M: FromQueryResult,
|
||||
N: FromQueryResult,
|
||||
{
|
||||
SelectTwoModel {
|
||||
query: self.query,
|
||||
model: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn one(self, db: &Database) -> Result<(E::Model, F::Model), QueryErr> {
|
||||
self.into_model::<E::Model, F::Model>().one(db).await
|
||||
}
|
||||
|
||||
pub async fn all(self, db: &Database) -> Result<Vec<(E::Model, F::Model)>, QueryErr> {
|
||||
self.into_model::<E::Model, F::Model>().all(db).await
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> SelectModel<M>
|
||||
where
|
||||
M: FromQueryResult,
|
||||
@ -59,7 +85,7 @@ where
|
||||
let builder = db.get_query_builder_backend();
|
||||
self.query.limit(1);
|
||||
let row = db.get_connection().query_one(self.build(builder)).await?;
|
||||
Ok(M::from_query_result(row, "")?)
|
||||
Ok(M::from_query_result(&row, "")?)
|
||||
}
|
||||
|
||||
pub async fn all(self, db: &Database) -> Result<Vec<M>, QueryErr> {
|
||||
@ -67,7 +93,37 @@ where
|
||||
let rows = db.get_connection().query_all(self.build(builder)).await?;
|
||||
let mut models = Vec::new();
|
||||
for row in rows.into_iter() {
|
||||
models.push(M::from_query_result(row, "")?);
|
||||
models.push(M::from_query_result(&row, "")?);
|
||||
}
|
||||
Ok(models)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, N> SelectTwoModel<M, N>
|
||||
where
|
||||
M: FromQueryResult,
|
||||
N: FromQueryResult,
|
||||
{
|
||||
pub fn build<B>(&self, builder: B) -> Statement
|
||||
where
|
||||
B: QueryBuilder,
|
||||
{
|
||||
self.query.build(builder).into()
|
||||
}
|
||||
|
||||
pub async fn one(mut self, db: &Database) -> Result<(M, N), QueryErr> {
|
||||
let builder = db.get_query_builder_backend();
|
||||
self.query.limit(1);
|
||||
let row = db.get_connection().query_one(self.build(builder)).await?;
|
||||
Ok((M::from_query_result(&row, combine::SELECT_A)?, N::from_query_result(&row, combine::SELECT_B)?))
|
||||
}
|
||||
|
||||
pub async fn all(self, db: &Database) -> Result<Vec<(M, N)>, QueryErr> {
|
||||
let builder = db.get_query_builder_backend();
|
||||
let rows = db.get_connection().query_all(self.build(builder)).await?;
|
||||
let mut models = Vec::new();
|
||||
for row in rows.into_iter() {
|
||||
models.push((M::from_query_result(&row, combine::SELECT_A)?, N::from_query_result(&row, combine::SELECT_B)?));
|
||||
}
|
||||
Ok(models)
|
||||
}
|
||||
|
@ -9,13 +9,13 @@ pub trait ModelTrait: Clone + Debug + Default {
|
||||
|
||||
fn set(&mut self, c: Self::Column, v: Value);
|
||||
|
||||
fn from_query_result(row: QueryResult, pre: &str) -> Result<Self, TypeErr>
|
||||
fn from_query_result(row: &QueryResult, pre: &str) -> Result<Self, TypeErr>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
pub trait FromQueryResult {
|
||||
fn from_query_result(row: QueryResult, pre: &str) -> Result<Self, TypeErr>
|
||||
fn from_query_result(row: &QueryResult, pre: &str) -> Result<Self, TypeErr>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
@ -24,7 +24,7 @@ impl<M> FromQueryResult for M
|
||||
where
|
||||
M: ModelTrait + Sized,
|
||||
{
|
||||
fn from_query_result(row: QueryResult, pre: &str) -> Result<M, TypeErr> {
|
||||
fn from_query_result(row: &QueryResult, pre: &str) -> Result<M, TypeErr> {
|
||||
<Self as ModelTrait>::from_query_result(row, pre)
|
||||
}
|
||||
}
|
||||
|
123
src/query/combine.rs
Normal file
123
src/query/combine.rs
Normal file
@ -0,0 +1,123 @@
|
||||
use crate::{EntityTrait, IntoSimpleExpr, Iterable, Select, SelectTwo};
|
||||
use core::marker::PhantomData;
|
||||
pub use sea_query::JoinType;
|
||||
use sea_query::{Alias, ColumnRef, Iden, SelectExpr, SelectStatement, SimpleExpr};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub const SELECT_A: &str = "A_";
|
||||
pub const SELECT_B: &str = "B_";
|
||||
|
||||
impl<E> Select<E>
|
||||
where
|
||||
E: EntityTrait,
|
||||
{
|
||||
fn apply_alias(mut self, pre: &str) -> Self {
|
||||
self.query().exprs_mut_for_each(|sel| {
|
||||
match &sel.alias {
|
||||
Some(alias) => {
|
||||
let alias = format!("{}{}", pre, alias.to_string().as_str());
|
||||
sel.alias = Some(Rc::new(Alias::new(&alias)));
|
||||
}
|
||||
None => {
|
||||
let col = match &sel.expr {
|
||||
SimpleExpr::Column(col_ref) => match &col_ref {
|
||||
ColumnRef::Column(col) => col,
|
||||
ColumnRef::TableColumn(_, col) => col,
|
||||
},
|
||||
_ => panic!("cannot apply alias for expr other than Column"),
|
||||
};
|
||||
let alias = format!("{}{}", pre, col.to_string().as_str());
|
||||
sel.alias = Some(Rc::new(Alias::new(&alias)));
|
||||
}
|
||||
};
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn select_also<F>(mut self, _: F) -> SelectTwo<E, F>
|
||||
where
|
||||
F: EntityTrait,
|
||||
{
|
||||
self = self.apply_alias(SELECT_A);
|
||||
SelectTwo::new(self.into_query())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, F> SelectTwo<E, F>
|
||||
where
|
||||
E: EntityTrait,
|
||||
F: EntityTrait,
|
||||
{
|
||||
pub(crate) fn new(query: SelectStatement) -> Self {
|
||||
let myself = Self {
|
||||
query,
|
||||
entity: PhantomData,
|
||||
};
|
||||
myself.prepare_select()
|
||||
}
|
||||
|
||||
fn prepare_select(mut self) -> Self {
|
||||
for col in <F::Column as Iterable>::iter() {
|
||||
let alias = format!("{}{}", SELECT_B, col.to_string().as_str());
|
||||
self.query.expr(SelectExpr {
|
||||
expr: col.into_simple_expr(),
|
||||
alias: Some(Rc::new(Alias::new(&alias))),
|
||||
});
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests_cfg::{cake, fruit};
|
||||
use crate::{EntityTrait, ColumnTrait, QueryHelper};
|
||||
use sea_query::MysqlQueryBuilder;
|
||||
|
||||
#[test]
|
||||
fn alias_1() {
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.column_as(cake::Column::Id, "B")
|
||||
.apply_alias("A_")
|
||||
.build(MysqlQueryBuilder)
|
||||
.to_string(),
|
||||
"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`, `cake`.`id` AS `A_B` FROM `cake`",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_also_1() {
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.left_join(fruit::Entity)
|
||||
.select_also(fruit::Entity)
|
||||
.build(MysqlQueryBuilder)
|
||||
.to_string(),
|
||||
[
|
||||
"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,",
|
||||
"`fruit`.`id` AS `B_id`, `fruit`.`name` AS `B_name`, `fruit`.`cake_id` AS `B_cake_id`",
|
||||
"FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
|
||||
].join(" ")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_also_2() {
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.left_join(fruit::Entity)
|
||||
.select_also(fruit::Entity)
|
||||
.filter(cake::Column::Id.eq(1))
|
||||
.filter(fruit::Column::Id.eq(2))
|
||||
.build(MysqlQueryBuilder)
|
||||
.to_string(),
|
||||
[
|
||||
"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,",
|
||||
"`fruit`.`id` AS `B_id`, `fruit`.`name` AS `B_name`, `fruit`.`cake_id` AS `B_cake_id`",
|
||||
"FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
|
||||
"WHERE `cake`.`id` = 1 AND `fruit`.`id` = 2",
|
||||
].join(" ")
|
||||
);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
ColumnTrait, EntityTrait, Identity, Iterable, ModelTrait, PrimaryKeyOfModel, QueryHelper,
|
||||
Related, RelationDef, Select,
|
||||
Related, RelationDef, Select, SelectTwo
|
||||
};
|
||||
|
||||
pub use sea_query::JoinType;
|
||||
@ -91,6 +91,15 @@ where
|
||||
{
|
||||
self.join_rev(JoinType::InnerJoin, R::to())
|
||||
}
|
||||
|
||||
/// Left Join with a Related Entity and select both Entity.
|
||||
pub fn left_join_and_select<R>(self, r: R) -> SelectTwo<E, R>
|
||||
where
|
||||
R: EntityTrait,
|
||||
E: Related<R>,
|
||||
{
|
||||
self.left_join(r).select_also(r)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -182,16 +191,4 @@ mod tests {
|
||||
.join(" ")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_1() {
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.column_as(cake::Column::Id, "B")
|
||||
.apply_alias("A_")
|
||||
.build(MysqlQueryBuilder)
|
||||
.to_string(),
|
||||
"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`, `cake`.`id` AS `A_B` FROM `cake`",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
pub(crate) mod combine;
|
||||
mod helper;
|
||||
mod join;
|
||||
mod result;
|
||||
mod select;
|
||||
|
||||
pub use combine::*;
|
||||
pub use helper::*;
|
||||
pub use join::*;
|
||||
pub use result::*;
|
||||
|
@ -2,9 +2,7 @@ use crate::{ColumnTrait, EntityTrait, Iterable, QueryHelper, Statement};
|
||||
use core::fmt::Debug;
|
||||
use core::marker::PhantomData;
|
||||
pub use sea_query::JoinType;
|
||||
use sea_query::{
|
||||
Alias, ColumnRef, Iden, IntoColumnRef, IntoIden, QueryBuilder, SelectStatement, SimpleExpr,
|
||||
};
|
||||
use sea_query::{Iden, IntoColumnRef, IntoIden, QueryBuilder, SelectStatement, SimpleExpr};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -91,30 +89,12 @@ where
|
||||
self.query.from(E::default().into_iden());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn apply_alias(mut self, pre: &str) -> Self {
|
||||
self.query().exprs_mut_for_each(|sel| {
|
||||
match &sel.alias {
|
||||
Some(alias) => {
|
||||
let alias = format!("{}{}", pre, alias.to_string().as_str());
|
||||
sel.alias = Some(Rc::new(Alias::new(&alias)));
|
||||
}
|
||||
None => {
|
||||
let col = match &sel.expr {
|
||||
SimpleExpr::Column(col_ref) => match &col_ref {
|
||||
ColumnRef::Column(col) => col,
|
||||
ColumnRef::TableColumn(_, col) => col,
|
||||
},
|
||||
_ => panic!("cannot apply alias for expr other than Column"),
|
||||
};
|
||||
let alias = format!("{}{}", pre, col.to_string().as_str());
|
||||
sel.alias = Some(Rc::new(Alias::new(&alias)));
|
||||
}
|
||||
};
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
impl<E> Select<E>
|
||||
where
|
||||
E: EntityTrait,
|
||||
{
|
||||
/// Get a mutable ref to the query builder
|
||||
pub fn query(&mut self) -> &mut SelectStatement {
|
||||
&mut self.query
|
||||
@ -139,21 +119,31 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests_cfg::cake;
|
||||
use crate::{EntityTrait, QueryHelper};
|
||||
use sea_query::MysqlQueryBuilder;
|
||||
impl<E, F> SelectTwo<E, F>
|
||||
where
|
||||
E: EntityTrait,
|
||||
F: EntityTrait,
|
||||
{
|
||||
/// Get a mutable ref to the query builder
|
||||
pub fn query(&mut self) -> &mut SelectStatement {
|
||||
&mut self.query
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_1() {
|
||||
assert_eq!(
|
||||
cake::Entity::find()
|
||||
.column_as(cake::Column::Id, "B")
|
||||
.apply_alias("A_")
|
||||
.build(MysqlQueryBuilder)
|
||||
.to_string(),
|
||||
"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`, `cake`.`id` AS `A_B` FROM `cake`",
|
||||
);
|
||||
/// Get an immutable ref to the query builder
|
||||
pub fn as_query(&self) -> &SelectStatement {
|
||||
&self.query
|
||||
}
|
||||
|
||||
/// Take ownership of the query builder
|
||||
pub fn into_query(self) -> SelectStatement {
|
||||
self.query
|
||||
}
|
||||
|
||||
/// Build the query as [`Statement`]
|
||||
pub fn build<B>(&self, builder: B) -> Statement
|
||||
where
|
||||
B: QueryBuilder,
|
||||
{
|
||||
self.as_query().build(builder).into()
|
||||
}
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ impl ModelTrait for Model {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_query_result(row: QueryResult, pre: &str) -> Result<Self, TypeErr> {
|
||||
fn from_query_result(row: &QueryResult, pre: &str) -> Result<Self, TypeErr> {
|
||||
Ok(Self {
|
||||
id: row.try_get(pre, Column::Id.as_str())?,
|
||||
name: row.try_get(pre, Column::Name.as_str())?,
|
||||
|
@ -90,7 +90,7 @@ impl ModelTrait for Model {
|
||||
}
|
||||
}
|
||||
|
||||
fn from_query_result(row: QueryResult, pre: &str) -> Result<Self, TypeErr> {
|
||||
fn from_query_result(row: &QueryResult, pre: &str) -> Result<Self, TypeErr> {
|
||||
Ok(Self {
|
||||
id: row.try_get(pre, Column::Id.as_str())?,
|
||||
name: row.try_get(pre, Column::Name.as_str())?,
|
||||
|
Loading…
x
Reference in New Issue
Block a user