icydb_core/db/query/fluent/load/
builder.rs1use crate::{
7 db::{
8 DbSession,
9 predicate::CompareOp,
10 query::{
11 builder::aggregate::AggregateExpr,
12 explain::ExplainPlan,
13 expr::{FilterExpr, OrderTerm},
14 intent::{CompiledQuery, PlannedQuery, Query, QueryError},
15 trace::QueryTracePlan,
16 },
17 },
18 traits::{EntityKind, SingletonEntity},
19 types::Id,
20 value::InputValue,
21};
22
23pub struct FluentLoadQuery<'a, E>
32where
33 E: EntityKind,
34{
35 pub(super) session: &'a DbSession<E::Canister>,
36 pub(super) query: Query<E>,
37 pub(super) cursor_token: Option<String>,
38}
39
40impl<'a, E> FluentLoadQuery<'a, E>
41where
42 E: EntityKind,
43{
44 pub(in crate::db) const fn new(session: &'a DbSession<E::Canister>, query: Query<E>) -> Self {
45 Self {
46 session,
47 query,
48 cursor_token: None,
49 }
50 }
51
52 #[must_use]
58 pub const fn query(&self) -> &Query<E> {
59 &self.query
60 }
61
62 pub(super) fn map_query(mut self, map: impl FnOnce(Query<E>) -> Query<E>) -> Self {
63 self.query = map(self.query);
64 self
65 }
66
67 pub(super) fn try_map_query(
68 mut self,
69 map: impl FnOnce(Query<E>) -> Result<Query<E>, QueryError>,
70 ) -> Result<Self, QueryError> {
71 self.query = map(self.query)?;
72 Ok(self)
73 }
74
75 fn map_session_query_output<T>(
79 &self,
80 map: impl FnOnce(&DbSession<E::Canister>, &Query<E>) -> Result<T, QueryError>,
81 ) -> Result<T, QueryError> {
82 map(self.session, self.query())
83 }
84
85 #[must_use]
93 pub fn by_id(self, id: Id<E>) -> Self {
94 self.map_query(|query| query.by_id(id.key()))
95 }
96
97 #[must_use]
101 pub fn by_ids<I>(self, ids: I) -> Self
102 where
103 I: IntoIterator<Item = Id<E>>,
104 {
105 self.map_query(|query| query.by_ids(ids.into_iter().map(|id| id.key())))
106 }
107
108 #[must_use]
114 pub fn filter(self, expr: impl Into<FilterExpr>) -> Self {
115 self.map_query(|query| query.filter(expr))
116 }
117
118 #[must_use]
120 pub fn order_term(self, term: OrderTerm) -> Self {
121 self.map_query(|query| query.order_term(term))
122 }
123
124 #[must_use]
126 pub fn order_terms<I>(self, terms: I) -> Self
127 where
128 I: IntoIterator<Item = OrderTerm>,
129 {
130 self.map_query(|query| query.order_terms(terms))
131 }
132
133 pub fn group_by(self, field: impl AsRef<str>) -> Result<Self, QueryError> {
135 let field = field.as_ref().to_owned();
136 let schema = self
137 .session
138 .accepted_schema_info_for_entity::<E>()
139 .map_err(QueryError::execute)?;
140
141 self.try_map_query(|query| query.group_by_with_schema(&field, &schema))
142 }
143
144 #[must_use]
146 pub fn aggregate(self, aggregate: AggregateExpr) -> Self {
147 self.map_query(|query| query.aggregate(aggregate))
148 }
149
150 #[must_use]
152 pub fn grouped_limits(self, max_groups: u64, max_group_bytes: u64) -> Self {
153 self.map_query(|query| query.grouped_limits(max_groups, max_group_bytes))
154 }
155
156 pub fn having_group(
158 self,
159 field: impl AsRef<str>,
160 op: CompareOp,
161 value: InputValue,
162 ) -> Result<Self, QueryError> {
163 let field = field.as_ref().to_owned();
164 let schema = self
165 .session
166 .accepted_schema_info_for_entity::<E>()
167 .map_err(QueryError::execute)?;
168
169 self.try_map_query(|query| query.having_group_with_schema(&field, &schema, op, value))
170 }
171
172 pub fn having_aggregate(
174 self,
175 aggregate_index: usize,
176 op: CompareOp,
177 value: InputValue,
178 ) -> Result<Self, QueryError> {
179 self.try_map_query(|query| query.having_aggregate(aggregate_index, op, value))
180 }
181
182 #[must_use]
188 pub fn limit(self, limit: u32) -> Self {
189 self.map_query(|query| query.limit(limit))
190 }
191
192 #[must_use]
198 pub fn offset(self, offset: u32) -> Self {
199 self.map_query(|query| query.offset(offset))
200 }
201
202 #[must_use]
208 pub fn cursor(mut self, token: impl Into<String>) -> Self {
209 self.cursor_token = Some(token.into());
210 self
211 }
212
213 pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
219 self.map_session_query_output(DbSession::explain_query_with_visible_indexes)
220 }
221
222 pub fn plan_hash_hex(&self) -> Result<String, QueryError> {
224 self.map_session_query_output(DbSession::query_plan_hash_hex_with_visible_indexes)
225 }
226
227 pub fn trace(&self) -> Result<QueryTracePlan, QueryError> {
229 self.map_session_query_output(DbSession::trace_query)
230 }
231
232 pub fn planned(&self) -> Result<PlannedQuery<E>, QueryError> {
234 self.ensure_cursor_mode_ready()?;
235 self.map_session_query_output(DbSession::planned_query_with_visible_indexes)
236 }
237
238 pub fn plan(&self) -> Result<CompiledQuery<E>, QueryError> {
240 self.ensure_cursor_mode_ready()?;
241 self.map_session_query_output(DbSession::compile_query_with_visible_indexes)
242 }
243}
244
245impl<E> FluentLoadQuery<'_, E>
246where
247 E: EntityKind + SingletonEntity,
248 E::Key: Default,
249{
250 #[must_use]
252 pub fn only(self) -> Self {
253 self.map_query(Query::only)
254 }
255}