icydb_core/db/query/session/
load.rs1use crate::{
2 db::{
3 DbSession, PagedLoadExecution, PagedLoadExecutionWithTrace,
4 query::{
5 expr::{FilterExpr, SortExpr},
6 intent::{IntentError, Query, QueryError},
7 plan::{ExecutablePlan, ExplainPlan},
8 policy,
9 predicate::Predicate,
10 },
11 response::Response,
12 },
13 traits::{CanisterKind, EntityKind, EntityValue, SingletonEntity},
14 types::Id,
15};
16
17pub struct SessionLoadQuery<'a, C, E>
26where
27 C: CanisterKind,
28 E: EntityKind<Canister = C>,
29{
30 session: &'a DbSession<C>,
31 query: Query<E>,
32 cursor_token: Option<String>,
33}
34
35pub struct PagedLoadQuery<'a, C, E>
43where
44 C: CanisterKind,
45 E: EntityKind<Canister = C>,
46{
47 inner: SessionLoadQuery<'a, C, E>,
48}
49
50impl<'a, C, E> SessionLoadQuery<'a, C, E>
51where
52 C: CanisterKind,
53 E: EntityKind<Canister = C>,
54{
55 pub(crate) const fn new(session: &'a DbSession<C>, query: Query<E>) -> Self {
56 Self {
57 session,
58 query,
59 cursor_token: None,
60 }
61 }
62
63 #[must_use]
68 pub const fn query(&self) -> &Query<E> {
69 &self.query
70 }
71
72 fn map_query(mut self, map: impl FnOnce(Query<E>) -> Query<E>) -> Self {
73 self.query = map(self.query);
74 self
75 }
76
77 fn try_map_query(
78 mut self,
79 map: impl FnOnce(Query<E>) -> Result<Query<E>, QueryError>,
80 ) -> Result<Self, QueryError> {
81 self.query = map(self.query)?;
82 Ok(self)
83 }
84
85 #[must_use]
93 pub fn by_id(mut self, id: Id<E>) -> Self {
94 self.query = self.query.by_id(id.key());
95 self
96 }
97
98 #[must_use]
102 pub fn by_ids<I>(mut self, ids: I) -> Self
103 where
104 I: IntoIterator<Item = Id<E>>,
105 {
106 self.query = self.query.by_ids(ids.into_iter().map(|id| id.key()));
107 self
108 }
109
110 #[must_use]
115 pub fn filter(self, predicate: Predicate) -> Self {
116 self.map_query(|query| query.filter(predicate))
117 }
118
119 pub fn filter_expr(self, expr: FilterExpr) -> Result<Self, QueryError> {
120 self.try_map_query(|query| query.filter_expr(expr))
121 }
122
123 pub fn sort_expr(self, expr: SortExpr) -> Result<Self, QueryError> {
124 self.try_map_query(|query| query.sort_expr(expr))
125 }
126
127 #[must_use]
128 pub fn order_by(self, field: impl AsRef<str>) -> Self {
129 self.map_query(|query| query.order_by(field))
130 }
131
132 #[must_use]
133 pub fn order_by_desc(self, field: impl AsRef<str>) -> Self {
134 self.map_query(|query| query.order_by_desc(field))
135 }
136
137 #[must_use]
142 pub fn limit(self, limit: u32) -> Self {
143 self.map_query(|query| query.limit(limit))
144 }
145
146 #[must_use]
151 pub fn offset(self, offset: u32) -> Self {
152 self.map_query(|query| query.offset(offset))
153 }
154
155 #[must_use]
161 pub fn cursor(mut self, token: impl Into<String>) -> Self {
162 self.cursor_token = Some(token.into());
163 self
164 }
165
166 pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
171 self.query.explain()
172 }
173
174 pub fn plan(&self) -> Result<ExecutablePlan<E>, QueryError> {
175 if let Some(err) = self.cursor_intent_error() {
176 return Err(QueryError::Intent(err));
177 }
178
179 self.query.plan()
180 }
181
182 pub fn execute(&self) -> Result<Response<E>, QueryError>
188 where
189 E: EntityValue,
190 {
191 self.session.execute_query(self.query())
192 }
193
194 pub fn page(self) -> Result<PagedLoadQuery<'a, C, E>, QueryError> {
205 self.ensure_paged_mode_ready()?;
206
207 Ok(PagedLoadQuery { inner: self })
208 }
209
210 pub fn execute_paged(self) -> Result<(Response<E>, Option<Vec<u8>>), QueryError>
214 where
215 E: EntityValue,
216 {
217 self.page()?.execute()
218 }
219
220 pub fn is_empty(&self) -> Result<bool, QueryError>
226 where
227 E: EntityValue,
228 {
229 Ok(!self.exists()?)
230 }
231
232 pub fn exists(&self) -> Result<bool, QueryError>
234 where
235 E: EntityValue,
236 {
237 self.session.execute_load_query_exists(self.query())
238 }
239
240 pub fn count(&self) -> Result<u32, QueryError>
242 where
243 E: EntityValue,
244 {
245 self.session.execute_load_query_count(self.query())
246 }
247
248 pub fn min(&self) -> Result<Option<Id<E>>, QueryError>
250 where
251 E: EntityValue,
252 {
253 self.session.execute_load_query_min(self.query())
254 }
255
256 pub fn max(&self) -> Result<Option<Id<E>>, QueryError>
258 where
259 E: EntityValue,
260 {
261 self.session.execute_load_query_max(self.query())
262 }
263
264 pub fn require_one(&self) -> Result<(), QueryError>
266 where
267 E: EntityValue,
268 {
269 self.execute()?.require_one()?;
270 Ok(())
271 }
272
273 pub fn require_some(&self) -> Result<(), QueryError>
275 where
276 E: EntityValue,
277 {
278 self.execute()?.require_some()?;
279 Ok(())
280 }
281}
282
283impl<C, E> SessionLoadQuery<'_, C, E>
284where
285 C: CanisterKind,
286 E: EntityKind<Canister = C>,
287{
288 fn cursor_intent_error(&self) -> Option<IntentError> {
289 self.cursor_token
290 .as_ref()
291 .and_then(|_| self.paged_intent_error())
292 }
293
294 fn paged_intent_error(&self) -> Option<IntentError> {
295 let spec = self.query.load_spec()?;
296
297 policy::validate_cursor_paging_requirements(self.query.has_explicit_order(), spec)
298 .err()
299 .map(IntentError::from)
300 }
301
302 fn ensure_paged_mode_ready(&self) -> Result<(), QueryError> {
303 if let Some(err) = self.paged_intent_error() {
304 return Err(QueryError::Intent(err));
305 }
306
307 Ok(())
308 }
309}
310
311impl<C, E> SessionLoadQuery<'_, C, E>
312where
313 C: CanisterKind,
314 E: EntityKind<Canister = C> + SingletonEntity,
315 E::Key: Default,
316{
317 #[must_use]
318 pub fn only(self) -> Self {
319 self.map_query(Query::only)
320 }
321}
322
323impl<C, E> PagedLoadQuery<'_, C, E>
324where
325 C: CanisterKind,
326 E: EntityKind<Canister = C>,
327{
328 #[must_use]
333 pub const fn query(&self) -> &Query<E> {
334 self.inner.query()
335 }
336
337 #[must_use]
343 pub fn cursor(mut self, token: impl Into<String>) -> Self {
344 self.inner = self.inner.cursor(token);
345 self
346 }
347
348 pub fn execute(self) -> Result<PagedLoadExecution<E>, QueryError>
358 where
359 E: EntityValue,
360 {
361 self.execute_with_trace()
362 .map(|(items, next_cursor, _)| (items, next_cursor))
363 }
364
365 pub fn execute_with_trace(self) -> Result<PagedLoadExecutionWithTrace<E>, QueryError>
371 where
372 E: EntityValue,
373 {
374 self.inner.ensure_paged_mode_ready()?;
375
376 self.inner.session.execute_load_query_paged_with_trace(
377 self.inner.query(),
378 self.inner.cursor_token.as_deref(),
379 )
380 }
381}