Skip to content

Commit

Permalink
Select three (#2518)
Browse files Browse the repository at this point in the history
* SelectThree

* Tests

* More tests
  • Loading branch information
tyt2y3 authored Mar 2, 2025
1 parent f4b28bc commit 455fe64
Show file tree
Hide file tree
Showing 9 changed files with 420 additions and 25 deletions.
63 changes: 60 additions & 3 deletions src/executor/cursor.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
ConnectionTrait, DbErr, EntityTrait, FromQueryResult, Identity, IdentityOf, IntoIdentity,
PartialModelTrait, PrimaryKeyToColumn, QueryOrder, QuerySelect, Select, SelectModel, SelectTwo,
SelectTwoModel, SelectorTrait,
PartialModelTrait, PrimaryKeyToColumn, QueryOrder, QuerySelect, Select, SelectModel,
SelectThree, SelectThreeModel, SelectTwo, SelectTwoModel, SelectorTrait,
};
use sea_query::{
Condition, DynIden, Expr, IntoValueTuple, Order, SeaRc, SelectStatement, SimpleExpr, Value,
Expand Down Expand Up @@ -268,7 +268,7 @@ where
for (tbl, col) in self.secondary_order_by.iter().cloned() {
if let Identity::Unary(c1) = col {
query.order_by((tbl, c1), ord.clone());
};
}
}

self
Expand Down Expand Up @@ -408,6 +408,18 @@ where
type Selector = SelectTwoModel<M, N>;
}

impl<E, F, G, M, N, O> CursorTrait for SelectThree<E, F, G>
where
E: EntityTrait<Model = M>,
F: EntityTrait<Model = N>,
G: EntityTrait<Model = O>,
M: FromQueryResult + Sized + Send + Sync,
N: FromQueryResult + Sized + Send + Sync,
O: FromQueryResult + Sized + Send + Sync,
{
type Selector = SelectThreeModel<M, N, O>;
}

impl<E, F, M, N> SelectTwo<E, F>
where
E: EntityTrait<Model = M>,
Expand Down Expand Up @@ -460,6 +472,51 @@ where
}
}

impl<E, F, G, M, N, O> SelectThree<E, F, G>
where
E: EntityTrait<Model = M>,
F: EntityTrait<Model = N>,
G: EntityTrait<Model = O>,
M: FromQueryResult + Sized + Send + Sync,
N: FromQueryResult + Sized + Send + Sync,
O: FromQueryResult + Sized + Send + Sync,
{
/// Convert into a cursor using column of first entity
pub fn cursor_by<C>(self, order_columns: C) -> Cursor<SelectThreeModel<M, N, O>>
where
C: IdentityOf<E>,
{
let mut cursor = Cursor::new(
self.query,
SeaRc::new(E::default()),
order_columns.identity_of(),
);
{
let primary_keys: Vec<(DynIden, Identity)> = <F::PrimaryKey as Iterable>::iter()
.map(|pk| {
(
SeaRc::new(F::default()),
Identity::Unary(SeaRc::new(pk.into_column())),
)
})
.collect();
cursor.set_secondary_order_by(primary_keys);
}
{
let primary_keys: Vec<(DynIden, Identity)> = <G::PrimaryKey as Iterable>::iter()
.map(|pk| {
(
SeaRc::new(G::default()),
Identity::Unary(SeaRc::new(pk.into_column())),
)
})
.collect();
cursor.set_secondary_order_by(primary_keys);
}
cursor
}
}

#[cfg(test)]
#[cfg(feature = "mock")]
mod tests {
Expand Down
19 changes: 18 additions & 1 deletion src/executor/paginator.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
error::*, ConnectionTrait, DbBackend, EntityTrait, FromQueryResult, Select, SelectModel,
SelectTwo, SelectTwoModel, Selector, SelectorRaw, SelectorTrait,
SelectThree, SelectThreeModel, SelectTwo, SelectTwoModel, Selector, SelectorRaw, SelectorTrait,
};
use async_stream::stream;
use futures_util::Stream;
Expand Down Expand Up @@ -300,6 +300,23 @@ where
}
}

impl<'db, C, M, N, O, E, F, G> PaginatorTrait<'db, C> for SelectThree<E, F, G>
where
C: ConnectionTrait,
E: EntityTrait<Model = M>,
F: EntityTrait<Model = N>,
G: EntityTrait<Model = O>,
M: FromQueryResult + Sized + Send + Sync + 'db,
N: FromQueryResult + Sized + Send + Sync + 'db,
O: FromQueryResult + Sized + Send + Sync + 'db,
{
type Selector = SelectThreeModel<M, N, O>;

fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector> {
self.into_model().paginate(db, page_size)
}
}

#[cfg(test)]
#[cfg(feature = "mock")]
mod tests {
Expand Down
128 changes: 124 additions & 4 deletions src/executor/select.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{
error::*, ConnectionTrait, DbBackend, EntityTrait, FromQueryResult, IdenStatic, Iterable,
ModelTrait, PartialModelTrait, PrimaryKeyArity, PrimaryKeyToColumn, PrimaryKeyTrait,
QueryResult, QuerySelect, Select, SelectA, SelectB, SelectTwo, SelectTwoMany, Statement,
StreamTrait, TryGetableMany,
QueryResult, QuerySelect, Select, SelectA, SelectB, SelectC, SelectThree, SelectTwo,
SelectTwoMany, Statement, StreamTrait, TryGetableMany,
};
use futures_util::{Stream, TryStreamExt};
use sea_query::{SelectStatement, Value};
Expand Down Expand Up @@ -62,7 +62,7 @@ where
model: PhantomData<T>,
}

/// Defines a type to get a Model
/// Helper class to handle query result for 1 Model
#[derive(Debug)]
pub struct SelectModel<M>
where
Expand All @@ -71,7 +71,7 @@ where
model: PhantomData<M>,
}

/// Defines a type to get two Models
/// Helper class to handle query result for 2 Models
#[derive(Clone, Debug)]
pub struct SelectTwoModel<M, N>
where
Expand All @@ -81,6 +81,17 @@ where
model: PhantomData<(M, N)>,
}

/// Helper class to handle query result for 3 Models
#[derive(Clone, Debug)]
pub struct SelectThreeModel<M, N, O>
where
M: FromQueryResult,
N: FromQueryResult,
O: FromQueryResult,
{
model: PhantomData<(M, N, O)>,
}

impl<T, C> SelectorTrait for SelectGetableValue<T, C>
where
T: TryGetableMany,
Expand Down Expand Up @@ -131,6 +142,23 @@ where
}
}

impl<M, N, O> SelectorTrait for SelectThreeModel<M, N, O>
where
M: FromQueryResult + Sized,
N: FromQueryResult + Sized,
O: FromQueryResult + Sized,
{
type Item = (M, Option<N>, Option<O>);

fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
Ok((
M::from_query_result(&res, SelectA.as_str())?,
N::from_query_result_optional(&res, SelectB.as_str())?,
O::from_query_result_optional(&res, SelectC.as_str())?,
))
}
}

impl<E> Select<E>
where
E: EntityTrait,
Expand Down Expand Up @@ -618,6 +646,98 @@ where
// we should only count the number of items of the parent model
}

impl<E, F, G> SelectThree<E, F, G>
where
E: EntityTrait,
F: EntityTrait,
G: EntityTrait,
{
/// Perform a conversion into a [SelectThreeModel]
pub fn into_model<M, N, O>(self) -> Selector<SelectThreeModel<M, N, O>>
where
M: FromQueryResult,
N: FromQueryResult,
O: FromQueryResult,
{
Selector {
query: self.query,
selector: SelectThreeModel { model: PhantomData },
}
}

/// Perform a conversion into a [SelectThreeModel] with [PartialModel](PartialModelTrait)
pub fn into_partial_model<M, N, O>(self) -> Selector<SelectThreeModel<M, N, O>>
where
M: PartialModelTrait,
N: PartialModelTrait,
O: PartialModelTrait,
{
let select = QuerySelect::select_only(self);
let select = M::select_cols(select);
let select = N::select_cols(select);
select.into_model::<M, N, O>()
}

/// Convert the Models into JsonValue
#[cfg(feature = "with-json")]
pub fn into_json(self) -> Selector<SelectThreeModel<JsonValue, JsonValue, JsonValue>> {
Selector {
query: self.query,
selector: SelectThreeModel { model: PhantomData },
}
}

/// Get one Model from the Select query
pub async fn one<C>(
self,
db: &C,
) -> Result<Option<(E::Model, Option<F::Model>, Option<G::Model>)>, DbErr>
where
C: ConnectionTrait,
{
self.into_model().one(db).await
}

/// Get all Models from the Select query
pub async fn all<C>(
self,
db: &C,
) -> Result<Vec<(E::Model, Option<F::Model>, Option<G::Model>)>, DbErr>
where
C: ConnectionTrait,
{
self.into_model().all(db).await
}

/// Stream the results of a Select operation on a Model
pub async fn stream<'a: 'b, 'b, C>(
self,
db: &'a C,
) -> Result<
impl Stream<Item = Result<(E::Model, Option<F::Model>, Option<G::Model>), DbErr>> + 'b,
DbErr,
>
where
C: ConnectionTrait + StreamTrait + Send,
{
self.into_model().stream(db).await
}

/// Stream the result of the operation with PartialModel
pub async fn stream_partial_model<'a: 'b, 'b, C, M, N, O>(
self,
db: &'a C,
) -> Result<impl Stream<Item = Result<(M, Option<N>, Option<O>), DbErr>> + 'b + Send, DbErr>
where
C: ConnectionTrait + StreamTrait + Send,
M: PartialModelTrait + Send + 'b,
N: PartialModelTrait + Send + 'b,
O: PartialModelTrait + Send + 'b,
{
self.into_partial_model().stream(db).await
}
}

impl<S> Selector<S>
where
S: SelectorTrait,
Expand Down
46 changes: 40 additions & 6 deletions src/query/combine.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
ColumnTrait, EntityTrait, IdenStatic, Iterable, QueryTrait, Select, SelectTwo, SelectTwoMany,
ColumnTrait, EntityTrait, IdenStatic, Iterable, QueryTrait, Select, SelectThree, SelectTwo,
SelectTwoMany,
};
use core::marker::PhantomData;
use sea_query::{Alias, ColumnRef, Iden, Order, SeaRc, SelectExpr, SelectStatement, SimpleExpr};
Expand All @@ -26,6 +27,7 @@ macro_rules! select_def {

select_def!(SelectA, "A_");
select_def!(SelectB, "B_");
select_def!(SelectC, "C_");

impl<E> Select<E>
where
Expand Down Expand Up @@ -71,7 +73,7 @@ where
self
}

/// Selects and Entity and returns it together with the Entity from `Self`
/// Selects extra Entity and returns it together with the Entity from `Self`
pub fn select_also<F>(mut self, _: F) -> SelectTwo<E, F>
where
F: EntityTrait,
Expand All @@ -95,6 +97,14 @@ where
E: EntityTrait,
F: EntityTrait,
{
/// Selects extra Entity and returns it together with the Entities from `Self`
pub fn select_also<G>(self, _: G) -> SelectThree<E, F, G>
where
G: EntityTrait,
{
SelectThree::new(self.into_query())
}

pub(crate) fn new(query: SelectStatement) -> Self {
Self::new_without_prepare(query).prepare_select()
}
Expand All @@ -107,7 +117,7 @@ where
}

fn prepare_select(mut self) -> Self {
prepare_select_two::<F, Self>(&mut self);
prepare_select_col::<F, _, _>(&mut self, SelectB);
self
}
}
Expand All @@ -131,7 +141,7 @@ where
}

fn prepare_select(mut self) -> Self {
prepare_select_two::<F, Self>(&mut self);
prepare_select_col::<F, _, _>(&mut self, SelectB);
self
}

Expand All @@ -143,13 +153,37 @@ where
}
}

fn prepare_select_two<F, S>(selector: &mut S)
impl<E, F, G> SelectThree<E, F, G>
where
E: EntityTrait,
F: EntityTrait,
G: EntityTrait,
{
pub(crate) fn new(query: SelectStatement) -> Self {
Self::new_without_prepare(query).prepare_select()
}

pub(crate) fn new_without_prepare(query: SelectStatement) -> Self {
Self {
query,
entity: PhantomData,
}
}

fn prepare_select(mut self) -> Self {
prepare_select_col::<G, _, _>(&mut self, SelectC);
self
}
}

fn prepare_select_col<F, S, A>(selector: &mut S, alias: A)
where
F: EntityTrait,
S: QueryTrait<QueryStatement = SelectStatement>,
A: IdenStatic,
{
for col in <F::Column as Iterable>::iter() {
let alias = format!("{}{}", SelectB.as_str(), col.as_str());
let alias = format!("{}{}", alias.as_str(), col.as_str());
selector.query().expr(SelectExpr {
expr: col.select_as(col.into_expr()),
alias: Some(SeaRc::new(Alias::new(alias))),
Expand Down
Loading

0 comments on commit 455fe64

Please sign in to comment.