Skip to main content

siderite_orm/queryset/
rows.rs

1//! Dynamic projections: `project`, `rows`, `values`, `values_list`,
2//! `aggregate`, and subquery builders.
3
4use super::QuerySet;
5use super::fetch::{is_flat, unordered};
6use crate::backend::Row;
7use crate::decode::FromValues;
8use crate::error::{OrmError, QueryError};
9use crate::expr::Expr;
10use crate::model::Model;
11use crate::plan::{QueryPlan, SelectExpr};
12
13impl<M: Model> QuerySet<M> {
14    /// Select only `fields` (Django `values()` as a builder): the queryset
15    /// then yields dynamic rows through [`rows`](Self::rows) instead of
16    /// models. Fields may be `Field` / `Joined` handles (`Book::title.select()`),
17    /// column names, `("name", expr)` pairs, or annotation names.
18    ///
19    /// Call `project` first and `annotate` after it to group: aggregates
20    /// group by the projected columns. The model's default ordering is
21    /// dropped.
22    #[must_use]
23    pub fn project<S: Into<SelectExpr>>(mut self, fields: impl IntoIterator<Item = S>) -> Self {
24        let projection: Vec<SelectExpr> = fields
25            .into_iter()
26            .map(|field| {
27                let mut field: SelectExpr = field.into();
28                field.expr = self.resolve(field.expr);
29                field
30            })
31            .collect();
32        if self.default_ordering {
33            self.default_ordering = false;
34            self.plan.ordering.clear();
35        }
36        self.with_plan(|mut plan| {
37            plan.projection = projection;
38            plan
39        })
40    }
41
42    /// Run the query and return its rows undecoded.
43    ///
44    /// # Errors
45    /// Capability or backend errors.
46    pub async fn rows(self) -> Result<Vec<Row>, OrmError> {
47        self.ready()?;
48        if self.empty {
49            return Ok(Vec::new());
50        }
51        Ok(self.db.fetch(&self.plan).await?.rows)
52    }
53
54    /// Rows holding only `fields` (Django `values(..)`).
55    ///
56    /// # Errors
57    /// Capability or backend errors.
58    pub async fn values<S: Into<SelectExpr>>(
59        self,
60        fields: impl IntoIterator<Item = S>,
61    ) -> Result<Vec<Row>, OrmError> {
62        self.project(fields).rows().await
63    }
64
65    /// `fields` decoded by position into `T`, a scalar or tuple (Django
66    /// `values_list(..)`): `values_list::<(String, i64)>([..])`.
67    ///
68    /// # Errors
69    /// [`QueryError::Decode`] if a column does not fit `T`; capability or
70    /// backend errors.
71    pub async fn values_list<T: FromValues, S: Into<SelectExpr>>(
72        self,
73        fields: impl IntoIterator<Item = S>,
74    ) -> Result<Vec<T>, OrmError> {
75        let rows = self.values(fields).await?;
76        Ok(rows.iter().map(T::from_values).collect::<Result<_, _>>()?)
77    }
78
79    /// One row of aggregates over the whole queryset (Django `aggregate`):
80    /// `aggregate([("total", Sum::of(Book::price))])`.
81    ///
82    /// Ordering is ignored. A queryset with a limit, distinct or grouping is
83    /// aggregated over its result rows.
84    ///
85    /// # Errors
86    /// Capability (e.g. `StdDev` on SQLite) or backend errors.
87    pub async fn aggregate<S: Into<SelectExpr>>(
88        self,
89        aggregates: impl IntoIterator<Item = S>,
90    ) -> Result<Row, OrmError> {
91        self.ready()?;
92        let projection: Vec<SelectExpr> = aggregates
93            .into_iter()
94            .map(|a| {
95                let mut a: SelectExpr = a.into();
96                a.expr = self.resolve(a.expr);
97                a
98            })
99            .collect();
100        let mut plan = if is_flat(&self.plan) {
101            unordered(&self.plan)
102        } else {
103            QueryPlan::from_subquery(unordered(&self.plan), "aggregated")
104        };
105        if self.empty {
106            plan.filter = Some(Expr::Or(Vec::new()));
107        }
108        plan.projection = projection;
109        plan.grouping.clear();
110        plan.having = None;
111        let result = self.db.fetch(&plan.resolve_relations()).await?;
112        result.rows.into_iter().next().ok_or_else(|| {
113            QueryError::Decode {
114                column: "aggregate".into(),
115                reason: "no row returned".into(),
116            }
117            .into()
118        })
119    }
120
121    /// This queryset as a subquery selecting only `field`, for
122    /// `Field::in_subquery` and [`Expr::subquery`].
123    #[must_use]
124    pub fn subquery(self, field: impl Into<SelectExpr>) -> QueryPlan {
125        let qs = self.project([field]);
126        let mut plan = unordered(&qs.plan);
127        plan.origin = Some(qs.db.origin());
128        plan
129    }
130
131    /// `EXISTS (this queryset)`, for correlated filters:
132    /// `Author::objects(&db).filter(Book::objects(&db).filter(..).exists_expr())`.
133    #[must_use]
134    pub fn exists_expr(self) -> Expr {
135        let mut plan = unordered(&self.plan);
136        plan.projection = vec![SelectExpr::new(Expr::val(1), None)];
137        plan.origin = Some(self.db.origin());
138        Expr::exists(plan)
139    }
140}