Skip to main content

sea_orm/executor/select/
three.rs

1use super::*;
2use crate::{
3    JoinType, Paginator, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect, QueryTrait, Related,
4    SelectC, SelectFour, SelectThree, SelectThreeMany, Topology, TopologyChain, TopologyStar,
5    combine::prepare_select_col,
6};
7
8impl<E, F, G, TOP> SelectThree<E, F, G, TOP>
9where
10    E: EntityTrait,
11    F: EntityTrait,
12    G: EntityTrait,
13    TOP: Topology,
14{
15    pub(crate) fn new(query: SelectStatement) -> Self {
16        Self::new_without_prepare(query).prepare_select()
17    }
18
19    pub(crate) fn new_without_prepare(query: SelectStatement) -> Self {
20        Self {
21            query,
22            entity: PhantomData,
23        }
24    }
25
26    fn prepare_select(mut self) -> Self {
27        prepare_select_col::<G, _, _>(&mut self, SelectC);
28        self
29    }
30
31    /// Left Join with a Related Entity and select all Entities.
32    pub fn find_also<T, H>(self, _: T, _: H) -> SelectFour<E, F, G, H, TopologyStar>
33    where
34        H: EntityTrait,
35        T: EntityTrait + Related<H>,
36    {
37        SelectFour::new(
38            self.join_join(JoinType::LeftJoin, T::to(), T::via())
39                .into_query(),
40        )
41    }
42}
43
44macro_rules! impl_query_trait {
45    ( $trait: ident ) => {
46        impl<E, F, G, TOP> $trait for SelectThree<E, F, G, TOP>
47        where
48            E: EntityTrait,
49            F: EntityTrait,
50            G: EntityTrait,
51            TOP: Topology,
52        {
53            type QueryStatement = SelectStatement;
54
55            fn query(&mut self) -> &mut SelectStatement {
56                &mut self.query
57            }
58        }
59    };
60}
61
62impl<E, F, G, TOP> QueryTrait for SelectThree<E, F, G, TOP>
63where
64    E: EntityTrait,
65    F: EntityTrait,
66    G: EntityTrait,
67    TOP: Topology,
68{
69    type QueryStatement = SelectStatement;
70    fn query(&mut self) -> &mut SelectStatement {
71        &mut self.query
72    }
73    fn as_query(&self) -> &SelectStatement {
74        &self.query
75    }
76    fn into_query(self) -> SelectStatement {
77        self.query
78    }
79}
80
81impl_query_trait!(QuerySelect);
82impl_query_trait!(QueryFilter);
83impl_query_trait!(QueryOrder);
84
85impl<M, N, O> SelectorTrait for SelectThreeModel<M, N, O>
86where
87    M: FromQueryResult + Sized,
88    N: FromQueryResult + Sized,
89    O: FromQueryResult + Sized,
90{
91    type Item = (M, Option<N>, Option<O>);
92
93    fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
94        Ok((
95            M::from_query_result(&res, SelectA.as_str())?,
96            N::from_query_result_optional(&res, SelectB.as_str())?,
97            O::from_query_result_optional(&res, SelectC.as_str())?,
98        ))
99    }
100}
101
102impl<E, F, G, TOP> SelectThree<E, F, G, TOP>
103where
104    E: EntityTrait,
105    F: EntityTrait,
106    G: EntityTrait,
107    TOP: Topology,
108{
109    /// Perform a conversion into a [SelectThreeModel]
110    pub fn into_model<M, N, O>(self) -> Selector<SelectThreeModel<M, N, O>>
111    where
112        M: FromQueryResult,
113        N: FromQueryResult,
114        O: FromQueryResult,
115    {
116        Selector {
117            query: self.query,
118            selector: PhantomData,
119        }
120    }
121
122    /// Perform a conversion into a [SelectThreeModel] with [PartialModel](PartialModelTrait)
123    pub fn into_partial_model<M, N, O>(self) -> Selector<SelectThreeModel<M, N, O>>
124    where
125        M: PartialModelTrait,
126        N: PartialModelTrait,
127        O: PartialModelTrait,
128    {
129        let select = QuerySelect::select_only(self);
130        let select = M::select_cols(select);
131        let select = N::select_cols(select);
132        let select = O::select_cols(select);
133        select.into_model::<M, N, O>()
134    }
135
136    /// Convert the Models into JsonValue
137    #[cfg(feature = "with-json")]
138    pub fn into_json(self) -> Selector<SelectThreeModel<JsonValue, JsonValue, JsonValue>> {
139        Selector {
140            query: self.query,
141            selector: PhantomData,
142        }
143    }
144
145    /// Get one Model from the Select query
146    pub fn one<C>(
147        self,
148        db: &C,
149    ) -> Result<Option<(E::Model, Option<F::Model>, Option<G::Model>)>, DbErr>
150    where
151        C: ConnectionTrait,
152    {
153        self.into_model().one(db)
154    }
155
156    /// Get all Models from the Select query
157    pub fn all<C>(
158        self,
159        db: &C,
160    ) -> Result<Vec<(E::Model, Option<F::Model>, Option<G::Model>)>, DbErr>
161    where
162        C: ConnectionTrait,
163    {
164        self.into_model().all(db)
165    }
166
167    /// Stream the results of a Select operation on a Model
168    pub fn stream<'a: 'b, 'b, C>(
169        self,
170        db: &'a C,
171    ) -> Result<
172        impl Iterator<Item = Result<(E::Model, Option<F::Model>, Option<G::Model>), DbErr>> + 'b,
173        DbErr,
174    >
175    where
176        C: ConnectionTrait + StreamTrait,
177    {
178        self.into_model().stream(db)
179    }
180
181    /// Stream the result of the operation with PartialModel
182    pub fn stream_partial_model<'a: 'b, 'b, C, M, N, O>(
183        self,
184        db: &'a C,
185    ) -> Result<impl Iterator<Item = Result<(M, Option<N>, Option<O>), DbErr>> + 'b, DbErr>
186    where
187        C: ConnectionTrait + StreamTrait,
188        M: PartialModelTrait + 'b,
189        N: PartialModelTrait + 'b,
190        O: PartialModelTrait + 'b,
191    {
192        self.into_partial_model().stream(db)
193    }
194
195    /// Consolidate query result by first / second model depending on join topology
196    /// ```
197    /// # use sea_orm::{tests_cfg::*, *};
198    /// # fn function(db: &DbConn) -> Result<(), DbErr> {
199    /// // fruit -> cake -> filling
200    /// let items: Vec<(fruit::Model, Vec<(cake::Model, Vec<filling::Model>)>)> = fruit::Entity::find()
201    ///     .find_also_related(cake::Entity)
202    ///     .and_also_related(filling::Entity)
203    ///     .consolidate()
204    ///     .all(db)?;
205    ///
206    /// // cake -> fruit
207    /// //      -> filling
208    /// let items: Vec<(cake::Model, Vec<fruit::Model>, Vec<filling::Model>)> = cake::Entity::find()
209    ///     .find_also_related(fruit::Entity)
210    ///     .find_also_related(filling::Entity)
211    ///     .consolidate()
212    ///     .all(db)?;
213    /// # Ok(())
214    /// # }
215    /// ```
216    pub fn consolidate(self) -> SelectThreeMany<E, F, G, TOP> {
217        SelectThreeMany {
218            query: self.query,
219            entity: self.entity,
220        }
221    }
222}
223
224impl<E, F, G, TOP> SelectThreeMany<E, F, G, TOP>
225where
226    E: EntityTrait,
227    F: EntityTrait,
228    G: EntityTrait,
229    TOP: Topology,
230{
231    /// Performs a conversion to [Selector]
232    fn into_model<M, N, O>(self) -> Selector<SelectThreeModel<M, N, O>>
233    where
234        M: FromQueryResult,
235        N: FromQueryResult,
236        O: FromQueryResult,
237    {
238        Selector {
239            query: self.query,
240            selector: PhantomData,
241        }
242    }
243}
244
245impl<E, F, G> SelectThreeMany<E, F, G, TopologyStar>
246where
247    E: EntityTrait,
248    F: EntityTrait,
249    G: EntityTrait,
250{
251    /// Execute query and consolidate rows by E
252    pub fn all<C>(self, db: &C) -> Result<Vec<(E::Model, Vec<F::Model>, Vec<G::Model>)>, DbErr>
253    where
254        C: ConnectionTrait,
255    {
256        let rows = self.into_model().all(db)?;
257        Ok(consolidate_query_result_tee::<E, F, G>(rows))
258    }
259}
260
261impl<E, F, G> SelectThreeMany<E, F, G, TopologyChain>
262where
263    E: EntityTrait,
264    F: EntityTrait,
265    G: EntityTrait,
266{
267    /// Execute query and consolidate rows in two passes, first by E, then by F
268    pub fn all<C>(self, db: &C) -> Result<Vec<(E::Model, Vec<(F::Model, Vec<G::Model>)>)>, DbErr>
269    where
270        C: ConnectionTrait,
271    {
272        let rows = self.into_model().all(db)?;
273        Ok(consolidate_query_result_chain::<E, F, G>(rows))
274    }
275}
276
277impl<'db, C, M, N, O, E, F, G, TOP> PaginatorTrait<'db, C> for SelectThree<E, F, G, TOP>
278where
279    C: ConnectionTrait,
280    E: EntityTrait<Model = M>,
281    F: EntityTrait<Model = N>,
282    G: EntityTrait<Model = O>,
283    M: FromQueryResult + Sized + 'db,
284    N: FromQueryResult + Sized + 'db,
285    O: FromQueryResult + Sized + 'db,
286    TOP: Topology,
287{
288    type Selector = SelectThreeModel<M, N, O>;
289
290    fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector> {
291        self.into_model().paginate(db, page_size)
292    }
293}