Move code
This commit is contained in:
parent
2a2e5d6ba6
commit
9dc7c818d7
@ -1,4 +1,4 @@
|
|||||||
use sea_orm::{ColumnTrait, Database, EntityTrait, QueryErr, SelectQuery};
|
use sea_orm::{ColumnTrait, Database, EntityTrait, QueryErr, QueryHelper};
|
||||||
|
|
||||||
mod example_cake;
|
mod example_cake;
|
||||||
mod example_fruit;
|
mod example_fruit;
|
||||||
|
@ -3,7 +3,7 @@ use sea_query::{QueryBuilder, SelectStatement};
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SingleSelect<M>
|
pub struct SelectModel<M>
|
||||||
where
|
where
|
||||||
M: FromQueryResult,
|
M: FromQueryResult,
|
||||||
{
|
{
|
||||||
@ -12,7 +12,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DoubleSelect<M, N>
|
pub struct SelectTwoModel<M, N>
|
||||||
where
|
where
|
||||||
M: FromQueryResult,
|
M: FromQueryResult,
|
||||||
N: FromQueryResult,
|
N: FromQueryResult,
|
||||||
@ -25,11 +25,11 @@ impl<E: 'static> Select<E>
|
|||||||
where
|
where
|
||||||
E: EntityTrait,
|
E: EntityTrait,
|
||||||
{
|
{
|
||||||
pub fn into_model<M>(self) -> SingleSelect<M>
|
pub fn into_model<M>(self) -> SelectModel<M>
|
||||||
where
|
where
|
||||||
M: FromQueryResult,
|
M: FromQueryResult,
|
||||||
{
|
{
|
||||||
SingleSelect {
|
SelectModel {
|
||||||
query: self.query,
|
query: self.query,
|
||||||
model: PhantomData,
|
model: PhantomData,
|
||||||
}
|
}
|
||||||
@ -44,7 +44,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M> SingleSelect<M>
|
impl<M> SelectModel<M>
|
||||||
where
|
where
|
||||||
M: FromQueryResult,
|
M: FromQueryResult,
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ColumnTrait, ModelTrait, PrimaryKeyOfModel, PrimaryKeyTrait, RelationBuilder, RelationTrait,
|
ColumnTrait, ModelTrait, PrimaryKeyOfModel, PrimaryKeyTrait, QueryHelper, RelationBuilder,
|
||||||
RelationType, Select, SelectQuery,
|
RelationTrait, RelationType, Select,
|
||||||
};
|
};
|
||||||
use sea_query::{Iden, IntoIden, Value};
|
use sea_query::{Iden, IntoIden, Value};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
@ -57,7 +57,7 @@ pub trait ColumnTrait: IdenStatic {
|
|||||||
bind_oper!(lte);
|
bind_oper!(lte);
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{ColumnTrait, EntityTrait, SelectQuery, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
/// use sea_orm::{ColumnTrait, EntityTrait, QueryHelper, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake::Entity::find()
|
/// cake::Entity::find()
|
||||||
@ -75,7 +75,7 @@ pub trait ColumnTrait: IdenStatic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{ColumnTrait, EntityTrait, SelectQuery, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
/// use sea_orm::{ColumnTrait, EntityTrait, QueryHelper, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake::Entity::find()
|
/// cake::Entity::find()
|
||||||
@ -93,7 +93,7 @@ pub trait ColumnTrait: IdenStatic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{ColumnTrait, EntityTrait, SelectQuery, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
/// use sea_orm::{ColumnTrait, EntityTrait, QueryHelper, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake::Entity::find()
|
/// cake::Entity::find()
|
||||||
@ -108,7 +108,7 @@ pub trait ColumnTrait: IdenStatic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{ColumnTrait, EntityTrait, SelectQuery, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
/// use sea_orm::{ColumnTrait, EntityTrait, QueryHelper, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake::Entity::find()
|
/// cake::Entity::find()
|
||||||
@ -123,7 +123,7 @@ pub trait ColumnTrait: IdenStatic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{ColumnTrait, EntityTrait, SelectQuery, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
/// use sea_orm::{ColumnTrait, EntityTrait, QueryHelper, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake::Entity::find()
|
/// cake::Entity::find()
|
||||||
@ -139,7 +139,7 @@ pub trait ColumnTrait: IdenStatic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{ColumnTrait, EntityTrait, SelectQuery, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
/// use sea_orm::{ColumnTrait, EntityTrait, QueryHelper, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake::Entity::find()
|
/// cake::Entity::find()
|
||||||
@ -155,7 +155,7 @@ pub trait ColumnTrait: IdenStatic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use sea_orm::{ColumnTrait, EntityTrait, SelectQuery, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
/// use sea_orm::{ColumnTrait, EntityTrait, QueryHelper, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
||||||
///
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// cake::Entity::find()
|
/// cake::Entity::find()
|
||||||
|
140
src/query/helper.rs
Normal file
140
src/query/helper.rs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
use crate::{ColumnTrait, IntoSimpleExpr};
|
||||||
|
|
||||||
|
pub use sea_query::JoinType;
|
||||||
|
use sea_query::{Alias, Order, SelectExpr, SelectStatement, SimpleExpr};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
pub trait QueryHelper: Sized {
|
||||||
|
fn query(&mut self) -> &mut SelectStatement;
|
||||||
|
|
||||||
|
fn select_only(mut self) -> Self {
|
||||||
|
self.query().clear_selects();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a select column
|
||||||
|
/// ```
|
||||||
|
/// use sea_orm::{ColumnTrait, EntityTrait, QueryHelper, tests_cfg::cake, sea_query::PostgresQueryBuilder};
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// cake::Entity::find()
|
||||||
|
/// .select_only()
|
||||||
|
/// .column(cake::Column::Name)
|
||||||
|
/// .build(PostgresQueryBuilder)
|
||||||
|
/// .to_string(),
|
||||||
|
/// r#"SELECT "cake"."name" FROM "cake""#
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
fn column<C>(mut self, col: C) -> Self
|
||||||
|
where
|
||||||
|
C: ColumnTrait,
|
||||||
|
{
|
||||||
|
self.query().expr(col.into_simple_expr());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a select column with alias
|
||||||
|
/// ```
|
||||||
|
/// use sea_orm::{ColumnTrait, EntityTrait, QueryHelper, tests_cfg::cake, sea_query::PostgresQueryBuilder};
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// cake::Entity::find()
|
||||||
|
/// .select_only()
|
||||||
|
/// .column_as(cake::Column::Id.count(), "count")
|
||||||
|
/// .build(PostgresQueryBuilder)
|
||||||
|
/// .to_string(),
|
||||||
|
/// r#"SELECT COUNT("cake"."id") AS "count" FROM "cake""#
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
fn column_as<C>(mut self, col: C, alias: &str) -> Self
|
||||||
|
where
|
||||||
|
C: IntoSimpleExpr,
|
||||||
|
{
|
||||||
|
self.query().expr(SelectExpr {
|
||||||
|
expr: col.into_simple_expr(),
|
||||||
|
alias: Some(Rc::new(Alias::new(alias))),
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an AND WHERE expression
|
||||||
|
/// ```
|
||||||
|
/// use sea_orm::{ColumnTrait, EntityTrait, QueryHelper, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// cake::Entity::find()
|
||||||
|
/// .filter(cake::Column::Id.eq(5))
|
||||||
|
/// .build(MysqlQueryBuilder)
|
||||||
|
/// .to_string(),
|
||||||
|
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` = 5"
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
fn filter(mut self, expr: SimpleExpr) -> Self {
|
||||||
|
self.query().and_where(expr);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a group by column
|
||||||
|
/// ```
|
||||||
|
/// use sea_orm::{ColumnTrait, EntityTrait, QueryHelper, tests_cfg::cake, sea_query::PostgresQueryBuilder};
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// cake::Entity::find()
|
||||||
|
/// .select_only()
|
||||||
|
/// .column(cake::Column::Name)
|
||||||
|
/// .group_by(cake::Column::Name)
|
||||||
|
/// .build(PostgresQueryBuilder)
|
||||||
|
/// .to_string(),
|
||||||
|
/// r#"SELECT "cake"."name" FROM "cake" GROUP BY "cake"."name""#
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
fn group_by<C>(mut self, col: C) -> Self
|
||||||
|
where
|
||||||
|
C: IntoSimpleExpr,
|
||||||
|
{
|
||||||
|
self.query().add_group_by(vec![col.into_simple_expr()]);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an order_by expression (ascending)
|
||||||
|
/// ```
|
||||||
|
/// use sea_orm::{EntityTrait, QueryHelper, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// cake::Entity::find()
|
||||||
|
/// .order_by(cake::Column::Id)
|
||||||
|
/// .build(MysqlQueryBuilder)
|
||||||
|
/// .to_string(),
|
||||||
|
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` ORDER BY `cake`.`id` ASC"
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
fn order_by<C>(mut self, col: C) -> Self
|
||||||
|
where
|
||||||
|
C: IntoSimpleExpr,
|
||||||
|
{
|
||||||
|
self.query()
|
||||||
|
.order_by_expr(col.into_simple_expr(), Order::Asc);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an order_by expression (descending)
|
||||||
|
/// ```
|
||||||
|
/// use sea_orm::{EntityTrait, QueryHelper, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// cake::Entity::find()
|
||||||
|
/// .order_by_desc(cake::Column::Id)
|
||||||
|
/// .build(MysqlQueryBuilder)
|
||||||
|
/// .to_string(),
|
||||||
|
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` ORDER BY `cake`.`id` DESC"
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
fn order_by_desc<C>(mut self, col: C) -> Self
|
||||||
|
where
|
||||||
|
C: IntoSimpleExpr,
|
||||||
|
{
|
||||||
|
self.query()
|
||||||
|
.order_by_expr(col.into_simple_expr(), Order::Desc);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
197
src/query/join.rs
Normal file
197
src/query/join.rs
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
use crate::{
|
||||||
|
ColumnTrait, EntityTrait, Identity, Iterable, ModelTrait, PrimaryKeyOfModel, QueryHelper,
|
||||||
|
Related, RelationDef, Select,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use sea_query::JoinType;
|
||||||
|
use sea_query::{Expr, IntoIden};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
impl<E> Select<E>
|
||||||
|
where
|
||||||
|
E: EntityTrait,
|
||||||
|
{
|
||||||
|
pub fn belongs_to<R>(self, model: &R::Model) -> Self
|
||||||
|
where
|
||||||
|
R: EntityTrait + Related<E>,
|
||||||
|
R::PrimaryKey: PrimaryKeyOfModel<R::Model>,
|
||||||
|
{
|
||||||
|
if let Some(key) = R::PrimaryKey::iter().next() {
|
||||||
|
// TODO: supporting composite primary key
|
||||||
|
let col = key.into_column();
|
||||||
|
self.filter(col.eq(model.get(col)))
|
||||||
|
} else {
|
||||||
|
panic!("undefined primary key");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.join_rev(JoinType::InnerJoin, R::to())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::tests_cfg::{cake, fruit};
|
||||||
|
use crate::{ColumnTrait, EntityTrait, QueryHelper};
|
||||||
|
use sea_query::MysqlQueryBuilder;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn join_1() {
|
||||||
|
assert_eq!(
|
||||||
|
cake::Entity::find()
|
||||||
|
.left_join(fruit::Entity)
|
||||||
|
.build(MysqlQueryBuilder)
|
||||||
|
.to_string(),
|
||||||
|
[
|
||||||
|
"SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
|
||||||
|
"LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
|
||||||
|
]
|
||||||
|
.join(" ")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn join_2() {
|
||||||
|
assert_eq!(
|
||||||
|
cake::Entity::find()
|
||||||
|
.inner_join(fruit::Entity)
|
||||||
|
.filter(fruit::Column::Name.contains("cherry"))
|
||||||
|
.build(MysqlQueryBuilder)
|
||||||
|
.to_string(),
|
||||||
|
[
|
||||||
|
"SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
|
||||||
|
"INNER JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
|
||||||
|
"WHERE `fruit`.`name` LIKE \'%cherry%\'"
|
||||||
|
]
|
||||||
|
.join(" ")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn join_3() {
|
||||||
|
assert_eq!(
|
||||||
|
fruit::Entity::find()
|
||||||
|
.reverse_join(cake::Entity)
|
||||||
|
.build(MysqlQueryBuilder)
|
||||||
|
.to_string(),
|
||||||
|
[
|
||||||
|
"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
|
||||||
|
"INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
|
||||||
|
]
|
||||||
|
.join(" ")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn join_4() {
|
||||||
|
use crate::{Related, Select};
|
||||||
|
|
||||||
|
let find_fruit: Select<fruit::Entity> = cake::Entity::find_related();
|
||||||
|
assert_eq!(
|
||||||
|
find_fruit
|
||||||
|
.filter(cake::Column::Id.eq(11))
|
||||||
|
.build(MysqlQueryBuilder)
|
||||||
|
.to_string(),
|
||||||
|
[
|
||||||
|
"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
|
||||||
|
"INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
|
||||||
|
"WHERE `cake`.`id` = 11",
|
||||||
|
]
|
||||||
|
.join(" ")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn join_5() {
|
||||||
|
let cake_model = cake::Model {
|
||||||
|
id: 12,
|
||||||
|
name: "".to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
cake_model.find_fruit().build(MysqlQueryBuilder).to_string(),
|
||||||
|
[
|
||||||
|
"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
|
||||||
|
"INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
|
||||||
|
"WHERE `cake`.`id` = 12",
|
||||||
|
]
|
||||||
|
.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,5 +1,9 @@
|
|||||||
|
mod helper;
|
||||||
|
mod join;
|
||||||
mod result;
|
mod result;
|
||||||
mod select;
|
mod select;
|
||||||
|
|
||||||
|
pub use helper::*;
|
||||||
|
pub use join::*;
|
||||||
pub use result::*;
|
pub use result::*;
|
||||||
pub use select::*;
|
pub use select::*;
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
use crate::{
|
use crate::{ColumnTrait, EntityTrait, Iterable, QueryHelper, Statement};
|
||||||
ColumnTrait, EntityTrait, Identity, Iterable, ModelTrait, PrimaryKeyOfModel, Related,
|
|
||||||
RelationDef, Statement,
|
|
||||||
};
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
pub use sea_query::JoinType;
|
pub use sea_query::JoinType;
|
||||||
use sea_query::{
|
use sea_query::{
|
||||||
Alias, ColumnRef, Expr, Iden, IntoColumnRef, IntoIden, Order, QueryBuilder, SelectExpr,
|
Alias, ColumnRef, Iden, IntoColumnRef, IntoIden, QueryBuilder, SelectStatement, SimpleExpr,
|
||||||
SelectStatement, SimpleExpr,
|
|
||||||
};
|
};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
@ -20,10 +16,39 @@ where
|
|||||||
pub(crate) entity: PhantomData<E>,
|
pub(crate) entity: PhantomData<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct SelectTwo<E, F>
|
||||||
|
where
|
||||||
|
E: EntityTrait,
|
||||||
|
F: EntityTrait,
|
||||||
|
{
|
||||||
|
pub(crate) query: SelectStatement,
|
||||||
|
pub(crate) entity: PhantomData<(E, F)>,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait IntoSimpleExpr {
|
pub trait IntoSimpleExpr {
|
||||||
fn into_simple_expr(self) -> SimpleExpr;
|
fn into_simple_expr(self) -> SimpleExpr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E> QueryHelper for Select<E>
|
||||||
|
where
|
||||||
|
E: EntityTrait,
|
||||||
|
{
|
||||||
|
fn query(&mut self) -> &mut SelectStatement {
|
||||||
|
&mut self.query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, F> QueryHelper for SelectTwo<E, F>
|
||||||
|
where
|
||||||
|
E: EntityTrait,
|
||||||
|
F: EntityTrait,
|
||||||
|
{
|
||||||
|
fn query(&mut self) -> &mut SelectStatement {
|
||||||
|
&mut self.query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<C> IntoSimpleExpr for C
|
impl<C> IntoSimpleExpr for C
|
||||||
where
|
where
|
||||||
C: ColumnTrait,
|
C: ColumnTrait,
|
||||||
@ -67,7 +92,7 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_alias(mut self, pre: &str) -> Self {
|
pub(crate) fn apply_alias(mut self, pre: &str) -> Self {
|
||||||
self.query().exprs_mut_for_each(|sel| {
|
self.query().exprs_mut_for_each(|sel| {
|
||||||
match &sel.alias {
|
match &sel.alias {
|
||||||
Some(alias) => {
|
Some(alias) => {
|
||||||
@ -90,87 +115,6 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn belongs_to<R>(self, model: &R::Model) -> Self
|
|
||||||
where
|
|
||||||
R: EntityTrait + Related<E>,
|
|
||||||
R::PrimaryKey: PrimaryKeyOfModel<R::Model>,
|
|
||||||
{
|
|
||||||
if let Some(key) = R::PrimaryKey::iter().next() {
|
|
||||||
// TODO: supporting composite primary key
|
|
||||||
let col = key.into_column();
|
|
||||||
self.filter(col.eq(model.get(col)))
|
|
||||||
} else {
|
|
||||||
panic!("undefined primary key");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.join_rev(JoinType::InnerJoin, R::to())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
||||||
@ -195,240 +139,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SelectQuery: Sized {
|
|
||||||
fn query(&mut self) -> &mut SelectStatement;
|
|
||||||
|
|
||||||
fn select_only(mut self) -> Self {
|
|
||||||
self.query().clear_selects();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a select column
|
|
||||||
/// ```
|
|
||||||
/// use sea_orm::{ColumnTrait, EntityTrait, SelectQuery, tests_cfg::cake, sea_query::PostgresQueryBuilder};
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// cake::Entity::find()
|
|
||||||
/// .select_only()
|
|
||||||
/// .column(cake::Column::Name)
|
|
||||||
/// .build(PostgresQueryBuilder)
|
|
||||||
/// .to_string(),
|
|
||||||
/// r#"SELECT "cake"."name" FROM "cake""#
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
fn column<C>(mut self, col: C) -> Self
|
|
||||||
where
|
|
||||||
C: ColumnTrait,
|
|
||||||
{
|
|
||||||
self.query().expr(col.into_simple_expr());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a select column with alias
|
|
||||||
/// ```
|
|
||||||
/// use sea_orm::{ColumnTrait, EntityTrait, SelectQuery, tests_cfg::cake, sea_query::PostgresQueryBuilder};
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// cake::Entity::find()
|
|
||||||
/// .select_only()
|
|
||||||
/// .column_as(cake::Column::Id.count(), "count")
|
|
||||||
/// .build(PostgresQueryBuilder)
|
|
||||||
/// .to_string(),
|
|
||||||
/// r#"SELECT COUNT("cake"."id") AS "count" FROM "cake""#
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
fn column_as<C>(mut self, col: C, alias: &str) -> Self
|
|
||||||
where
|
|
||||||
C: IntoSimpleExpr,
|
|
||||||
{
|
|
||||||
self.query().expr(SelectExpr {
|
|
||||||
expr: col.into_simple_expr(),
|
|
||||||
alias: Some(Rc::new(Alias::new(alias))),
|
|
||||||
});
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add an AND WHERE expression
|
|
||||||
/// ```
|
|
||||||
/// use sea_orm::{ColumnTrait, EntityTrait, SelectQuery, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// cake::Entity::find()
|
|
||||||
/// .filter(cake::Column::Id.eq(5))
|
|
||||||
/// .build(MysqlQueryBuilder)
|
|
||||||
/// .to_string(),
|
|
||||||
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` = 5"
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
fn filter(mut self, expr: SimpleExpr) -> Self {
|
|
||||||
self.query().and_where(expr);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a group by column
|
|
||||||
/// ```
|
|
||||||
/// use sea_orm::{ColumnTrait, EntityTrait, SelectQuery, tests_cfg::cake, sea_query::PostgresQueryBuilder};
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// cake::Entity::find()
|
|
||||||
/// .select_only()
|
|
||||||
/// .column(cake::Column::Name)
|
|
||||||
/// .group_by(cake::Column::Name)
|
|
||||||
/// .build(PostgresQueryBuilder)
|
|
||||||
/// .to_string(),
|
|
||||||
/// r#"SELECT "cake"."name" FROM "cake" GROUP BY "cake"."name""#
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
fn group_by<C>(mut self, col: C) -> Self
|
|
||||||
where
|
|
||||||
C: IntoSimpleExpr,
|
|
||||||
{
|
|
||||||
self.query().add_group_by(vec![col.into_simple_expr()]);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add an order_by expression (ascending)
|
|
||||||
/// ```
|
|
||||||
/// use sea_orm::{EntityTrait, SelectQuery, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// cake::Entity::find()
|
|
||||||
/// .order_by(cake::Column::Id)
|
|
||||||
/// .build(MysqlQueryBuilder)
|
|
||||||
/// .to_string(),
|
|
||||||
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` ORDER BY `cake`.`id` ASC"
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
fn order_by<C>(mut self, col: C) -> Self
|
|
||||||
where
|
|
||||||
C: IntoSimpleExpr,
|
|
||||||
{
|
|
||||||
self.query()
|
|
||||||
.order_by_expr(col.into_simple_expr(), Order::Asc);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add an order_by expression (descending)
|
|
||||||
/// ```
|
|
||||||
/// use sea_orm::{EntityTrait, SelectQuery, tests_cfg::cake, sea_query::MysqlQueryBuilder};
|
|
||||||
///
|
|
||||||
/// assert_eq!(
|
|
||||||
/// cake::Entity::find()
|
|
||||||
/// .order_by_desc(cake::Column::Id)
|
|
||||||
/// .build(MysqlQueryBuilder)
|
|
||||||
/// .to_string(),
|
|
||||||
/// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` ORDER BY `cake`.`id` DESC"
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
fn order_by_desc<C>(mut self, col: C) -> Self
|
|
||||||
where
|
|
||||||
C: IntoSimpleExpr,
|
|
||||||
{
|
|
||||||
self.query()
|
|
||||||
.order_by_expr(col.into_simple_expr(), Order::Desc);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E> SelectQuery for Select<E>
|
|
||||||
where
|
|
||||||
E: EntityTrait,
|
|
||||||
{
|
|
||||||
fn query(&mut self) -> &mut SelectStatement {
|
|
||||||
&mut self.query
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests_cfg::{cake, fruit};
|
use crate::tests_cfg::cake;
|
||||||
use crate::{ColumnTrait, EntityTrait, SelectQuery};
|
use crate::{EntityTrait, QueryHelper};
|
||||||
use sea_query::MysqlQueryBuilder;
|
use sea_query::MysqlQueryBuilder;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn join_1() {
|
|
||||||
assert_eq!(
|
|
||||||
cake::Entity::find()
|
|
||||||
.left_join(fruit::Entity)
|
|
||||||
.build(MysqlQueryBuilder)
|
|
||||||
.to_string(),
|
|
||||||
[
|
|
||||||
"SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
|
|
||||||
"LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
|
|
||||||
]
|
|
||||||
.join(" ")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn join_2() {
|
|
||||||
assert_eq!(
|
|
||||||
cake::Entity::find()
|
|
||||||
.inner_join(fruit::Entity)
|
|
||||||
.filter(fruit::Column::Name.contains("cherry"))
|
|
||||||
.build(MysqlQueryBuilder)
|
|
||||||
.to_string(),
|
|
||||||
[
|
|
||||||
"SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
|
|
||||||
"INNER JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
|
|
||||||
"WHERE `fruit`.`name` LIKE \'%cherry%\'"
|
|
||||||
]
|
|
||||||
.join(" ")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn join_3() {
|
|
||||||
assert_eq!(
|
|
||||||
fruit::Entity::find()
|
|
||||||
.reverse_join(cake::Entity)
|
|
||||||
.build(MysqlQueryBuilder)
|
|
||||||
.to_string(),
|
|
||||||
[
|
|
||||||
"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
|
|
||||||
"INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
|
|
||||||
]
|
|
||||||
.join(" ")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn join_4() {
|
|
||||||
use crate::{Related, Select};
|
|
||||||
|
|
||||||
let find_fruit: Select<fruit::Entity> = cake::Entity::find_related();
|
|
||||||
assert_eq!(
|
|
||||||
find_fruit
|
|
||||||
.filter(cake::Column::Id.eq(11))
|
|
||||||
.build(MysqlQueryBuilder)
|
|
||||||
.to_string(),
|
|
||||||
[
|
|
||||||
"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
|
|
||||||
"INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
|
|
||||||
"WHERE `cake`.`id` = 11",
|
|
||||||
]
|
|
||||||
.join(" ")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn join_5() {
|
|
||||||
let cake_model = cake::Model {
|
|
||||||
id: 12,
|
|
||||||
name: "".to_owned(),
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
cake_model.find_fruit().build(MysqlQueryBuilder).to_string(),
|
|
||||||
[
|
|
||||||
"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
|
|
||||||
"INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
|
|
||||||
"WHERE `cake`.`id` = 12",
|
|
||||||
]
|
|
||||||
.join(" ")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn alias_1() {
|
fn alias_1() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user