Skip to main content

sea_orm/query/
select.rs

1use crate::{
2    ColumnTrait, EntityTrait, Iterable, Order, PrimaryKeyToColumn, QueryFilter, QueryOrder,
3    QuerySelect, QueryTrait,
4};
5use core::fmt::Debug;
6use core::marker::PhantomData;
7use sea_query::{FunctionCall, IntoColumnRef, SelectStatement, SimpleExpr};
8
9/// A `SELECT` query against entity `E`. Returned by
10/// [`EntityTrait::find`](crate::EntityTrait::find); chain filters, joins,
11/// ordering, and projections onto it, then run it on a
12/// [`ConnectionTrait`](crate::ConnectionTrait) with `.one(db)` /
13/// `.all(db)` / `.stream(db)` / `.paginate(db, n)`.
14#[derive(Clone, Debug)]
15pub struct Select<E>
16where
17    E: EntityTrait,
18{
19    pub(crate) query: SelectStatement,
20    pub(crate) entity: PhantomData<E>,
21    pub(crate) linked_index: usize,
22}
23
24/// A `SELECT` joining two entities, yielding `(E::Model, Option<F::Model>)`
25/// per row — the right side is `None` for outer-join rows with no match.
26/// Returned by [`Select::find_also_related`] and similar helpers.
27#[derive(Clone, Debug)]
28pub struct SelectTwo<E, F>
29where
30    E: EntityTrait,
31    F: EntityTrait,
32{
33    pub(crate) query: SelectStatement,
34    pub(crate) entity: PhantomData<(E, F)>,
35}
36
37/// A `SELECT` joining two entities, with results grouped into
38/// `(E::Model, Vec<F::Model>)` per left model. Returned by
39/// [`Select::find_with_related`].
40#[derive(Clone, Debug)]
41pub struct SelectTwoMany<E, F>
42where
43    E: EntityTrait,
44    F: EntityTrait,
45{
46    pub(crate) query: SelectStatement,
47    pub(crate) entity: PhantomData<(E, F)>,
48}
49
50/// A `SELECT` joining two entities where both sides are required, yielding
51/// `(E::Model, F::Model)` per row (no `Option`).
52#[derive(Clone, Debug)]
53pub struct SelectTwoRequired<E, F>
54where
55    E: EntityTrait,
56    F: EntityTrait,
57{
58    pub(crate) query: SelectStatement,
59    pub(crate) entity: PhantomData<(E, F)>,
60}
61
62/// Marker trait describing how 3+ tables are joined: from one centre entity
63/// to several siblings ([`TopologyStar`]) or in a sequential chain
64/// ([`TopologyChain`]).
65pub trait Topology {}
66
67/// Star join: a centre entity joined separately to each of the others.
68#[derive(Debug, Clone)]
69pub struct TopologyStar;
70
71/// Chain join: each entity joined to the previous one in sequence.
72#[derive(Debug, Clone)]
73pub struct TopologyChain;
74
75impl Topology for TopologyStar {}
76impl Topology for TopologyChain {}
77
78/// Three-way join select, yielding `(E::Model, Option<F::Model>, Option<G::Model>)`.
79/// The `TOP` parameter is the join [`Topology`].
80#[derive(Clone, Debug)]
81pub struct SelectThree<E, F, G, TOP>
82where
83    E: EntityTrait,
84    F: EntityTrait,
85    G: EntityTrait,
86    TOP: Topology,
87{
88    pub(crate) query: SelectStatement,
89    pub(crate) entity: PhantomData<(E, F, G, TOP)>,
90}
91
92/// Like [`SelectThree`], but results are consolidated under the left model:
93/// `(E::Model, Vec<F::Model>, Vec<G::Model>)`.
94#[derive(Clone, Debug)]
95pub struct SelectThreeMany<E, F, G, TOP>
96where
97    E: EntityTrait,
98    F: EntityTrait,
99    G: EntityTrait,
100    TOP: Topology,
101{
102    pub(crate) query: SelectStatement,
103    pub(crate) entity: PhantomData<(E, F, G, TOP)>,
104}
105
106/// Four-way join select.
107#[derive(Clone, Debug)]
108pub struct SelectFour<E, F, G, H, TOP>
109where
110    E: EntityTrait,
111    F: EntityTrait,
112    G: EntityTrait,
113    H: EntityTrait,
114    TOP: Topology,
115{
116    pub(crate) query: SelectStatement,
117    pub(crate) entity: PhantomData<(E, F, G, H, TOP)>,
118}
119
120/// Like [`SelectFour`], but results are consolidated under the left model.
121#[derive(Clone, Debug)]
122pub struct SelectFourMany<E, F, G, H, TOP>
123where
124    E: EntityTrait,
125    F: EntityTrait,
126    G: EntityTrait,
127    H: EntityTrait,
128    TOP: Topology,
129{
130    pub(crate) query: SelectStatement,
131    pub(crate) entity: PhantomData<(E, F, G, H, TOP)>,
132}
133
134/// Five-way join select.
135#[derive(Clone, Debug)]
136pub struct SelectFive<E, F, G, H, I, TOP>
137where
138    E: EntityTrait,
139    F: EntityTrait,
140    G: EntityTrait,
141    H: EntityTrait,
142    I: EntityTrait,
143    TOP: Topology,
144{
145    pub(crate) query: SelectStatement,
146    pub(crate) entity: PhantomData<(E, F, G, H, I, TOP)>,
147}
148
149/// Six-way join select.
150#[derive(Clone, Debug)]
151pub struct SelectSix<E, F, G, H, I, J, TOP>
152where
153    E: EntityTrait,
154    F: EntityTrait,
155    G: EntityTrait,
156    H: EntityTrait,
157    I: EntityTrait,
158    J: EntityTrait,
159    TOP: Topology,
160{
161    pub(crate) query: SelectStatement,
162    pub(crate) entity: PhantomData<(E, F, G, H, I, J, TOP)>,
163}
164
165/// Conversion into a [`SimpleExpr`]. Implemented for entity columns so they
166/// can be used anywhere a `sea_query` expression is expected.
167pub trait IntoSimpleExpr {
168    /// Build the [`SimpleExpr`].
169    fn into_simple_expr(self) -> SimpleExpr;
170}
171
172/// Like [`IntoSimpleExpr`] but applies SeaORM's enum-to-text cast for
173/// `ActiveEnum` columns appearing in a SELECT list.
174pub trait ColumnAsExpr: IntoSimpleExpr {
175    /// Build the [`SimpleExpr`], casting `ActiveEnum` columns to text;
176    /// otherwise identical to [`IntoSimpleExpr::into_simple_expr`].
177    fn into_column_as_expr(self) -> SimpleExpr;
178}
179
180macro_rules! impl_query_trait {
181    ( $trait: ident ) => {
182        impl<E> $trait for Select<E>
183        where
184            E: EntityTrait,
185        {
186            type QueryStatement = SelectStatement;
187
188            fn query(&mut self) -> &mut SelectStatement {
189                &mut self.query
190            }
191        }
192
193        impl<E, F> $trait for SelectTwo<E, F>
194        where
195            E: EntityTrait,
196            F: EntityTrait,
197        {
198            type QueryStatement = SelectStatement;
199
200            fn query(&mut self) -> &mut SelectStatement {
201                &mut self.query
202            }
203        }
204
205        impl<E, F> $trait for SelectTwoMany<E, F>
206        where
207            E: EntityTrait,
208            F: EntityTrait,
209        {
210            type QueryStatement = SelectStatement;
211
212            fn query(&mut self) -> &mut SelectStatement {
213                &mut self.query
214            }
215        }
216
217        impl<E, F> $trait for SelectTwoRequired<E, F>
218        where
219            E: EntityTrait,
220            F: EntityTrait,
221        {
222            type QueryStatement = SelectStatement;
223
224            fn query(&mut self) -> &mut SelectStatement {
225                &mut self.query
226            }
227        }
228    };
229}
230
231impl_query_trait!(QuerySelect);
232impl_query_trait!(QueryFilter);
233impl_query_trait!(QueryOrder);
234
235impl<C> ColumnAsExpr for C
236where
237    C: ColumnTrait,
238{
239    fn into_column_as_expr(self) -> SimpleExpr {
240        self.select_as(self.as_column_ref().into_column_ref().into())
241    }
242}
243
244impl ColumnAsExpr for SimpleExpr {
245    fn into_column_as_expr(self) -> SimpleExpr {
246        self.into_simple_expr()
247    }
248}
249
250impl<C> IntoSimpleExpr for C
251where
252    C: ColumnTrait,
253{
254    fn into_simple_expr(self) -> SimpleExpr {
255        SimpleExpr::Column(self.as_column_ref().into_column_ref())
256    }
257}
258
259impl IntoSimpleExpr for SimpleExpr {
260    fn into_simple_expr(self) -> SimpleExpr {
261        self
262    }
263}
264
265impl IntoSimpleExpr for FunctionCall {
266    fn into_simple_expr(self) -> SimpleExpr {
267        SimpleExpr::FunctionCall(self)
268    }
269}
270
271impl<E> Select<E>
272where
273    E: EntityTrait,
274{
275    pub(crate) fn new() -> Self {
276        Self {
277            query: SelectStatement::new(),
278            entity: PhantomData,
279            linked_index: 0,
280        }
281        .prepare_select()
282        .prepare_from()
283    }
284
285    fn prepare_select(mut self) -> Self {
286        self.query.exprs(self.column_list());
287        self
288    }
289
290    fn column_list(&self) -> Vec<SimpleExpr> {
291        E::Column::iter()
292            .map(|col| col.select_as(col.into_expr()))
293            .collect()
294    }
295
296    fn prepare_from(mut self) -> Self {
297        self.query.from(E::default().table_ref());
298        self
299    }
300
301    /// Apply order by primary key to the query statement
302    pub fn order_by_id_asc(self) -> Self {
303        self.order_by_id(Order::Asc)
304    }
305
306    /// Apply order by primary key to the query statement
307    pub fn order_by_id_desc(self) -> Self {
308        self.order_by_id(Order::Desc)
309    }
310
311    /// Apply order by primary key to the query statement
312    pub fn order_by_id(mut self, order: Order) -> Self {
313        for key in E::PrimaryKey::iter() {
314            let col = key.into_column();
315            self.query
316                .order_by_expr(col.into_simple_expr(), order.clone());
317        }
318        self
319    }
320}
321
322impl<E> QueryTrait for Select<E>
323where
324    E: EntityTrait,
325{
326    type QueryStatement = SelectStatement;
327    fn query(&mut self) -> &mut SelectStatement {
328        &mut self.query
329    }
330    fn as_query(&self) -> &SelectStatement {
331        &self.query
332    }
333    fn into_query(self) -> SelectStatement {
334        self.query
335    }
336}
337
338macro_rules! select_two {
339    ( $selector: ident ) => {
340        impl<E, F> QueryTrait for $selector<E, F>
341        where
342            E: EntityTrait,
343            F: EntityTrait,
344        {
345            type QueryStatement = SelectStatement;
346            fn query(&mut self) -> &mut SelectStatement {
347                &mut self.query
348            }
349            fn as_query(&self) -> &SelectStatement {
350                &self.query
351            }
352            fn into_query(self) -> SelectStatement {
353                self.query
354            }
355        }
356    };
357}
358
359select_two!(SelectTwo);
360select_two!(SelectTwoMany);
361select_two!(SelectTwoRequired);