1use crate::{
2 db::{
3 DbSession, PagedLoadExecution, PagedLoadExecutionWithTrace,
4 query::{
5 explain::ExplainPlan,
6 expr::{FilterExpr, SortExpr},
7 intent::{IntentError, PlannedQuery, Query, QueryError},
8 policy,
9 predicate::Predicate,
10 },
11 response::Response,
12 },
13 traits::{EntityKind, EntityValue, SingletonEntity},
14 types::{Decimal, Id},
15 value::Value,
16};
17
18type MinMaxByIds<E> = Option<(Id<E>, Id<E>)>;
19
20pub struct FluentLoadQuery<'a, E>
29where
30 E: EntityKind,
31{
32 session: &'a DbSession<E::Canister>,
33 query: Query<E>,
34 cursor_token: Option<String>,
35}
36
37pub struct PagedLoadQuery<'a, E>
45where
46 E: EntityKind,
47{
48 inner: FluentLoadQuery<'a, E>,
49}
50
51impl<'a, E> FluentLoadQuery<'a, E>
52where
53 E: EntityKind,
54{
55 pub(crate) const fn new(session: &'a DbSession<E::Canister>, 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 planned(&self) -> Result<PlannedQuery<E>, QueryError> {
175 if let Some(err) = self.cursor_intent_error() {
176 return Err(QueryError::Intent(err));
177 }
178
179 self.query.planned()
180 }
181
182 pub fn execute(&self) -> Result<Response<E>, QueryError>
188 where
189 E: EntityValue,
190 {
191 self.ensure_non_paged_mode_ready()?;
192
193 self.session.execute_query(self.query())
194 }
195
196 pub fn page(self) -> Result<PagedLoadQuery<'a, E>, QueryError> {
207 self.ensure_paged_mode_ready()?;
208
209 Ok(PagedLoadQuery { inner: self })
210 }
211
212 pub fn execute_paged(self) -> Result<PagedLoadExecution<E>, QueryError>
216 where
217 E: EntityValue,
218 {
219 self.page()?.execute()
220 }
221
222 pub fn is_empty(&self) -> Result<bool, QueryError>
228 where
229 E: EntityValue,
230 {
231 Ok(!self.exists()?)
232 }
233
234 pub fn exists(&self) -> Result<bool, QueryError>
236 where
237 E: EntityValue,
238 {
239 self.ensure_non_paged_mode_ready()?;
240
241 self.session.execute_load_query_exists(self.query())
242 }
243
244 pub fn count(&self) -> Result<u32, QueryError>
246 where
247 E: EntityValue,
248 {
249 self.ensure_non_paged_mode_ready()?;
250
251 self.session.execute_load_query_count(self.query())
252 }
253
254 pub fn min(&self) -> Result<Option<Id<E>>, QueryError>
256 where
257 E: EntityValue,
258 {
259 self.ensure_non_paged_mode_ready()?;
260
261 self.session.execute_load_query_min(self.query())
262 }
263
264 pub fn min_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
268 where
269 E: EntityValue,
270 {
271 self.ensure_non_paged_mode_ready()?;
272
273 self.session
274 .execute_load_query_min_by(self.query(), field.as_ref())
275 }
276
277 pub fn max(&self) -> Result<Option<Id<E>>, QueryError>
279 where
280 E: EntityValue,
281 {
282 self.ensure_non_paged_mode_ready()?;
283
284 self.session.execute_load_query_max(self.query())
285 }
286
287 pub fn max_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
291 where
292 E: EntityValue,
293 {
294 self.ensure_non_paged_mode_ready()?;
295
296 self.session
297 .execute_load_query_max_by(self.query(), field.as_ref())
298 }
299
300 pub fn nth_by(&self, field: impl AsRef<str>, nth: usize) -> Result<Option<Id<E>>, QueryError>
303 where
304 E: EntityValue,
305 {
306 self.ensure_non_paged_mode_ready()?;
307
308 self.session
309 .execute_load_query_nth_by(self.query(), field.as_ref(), nth)
310 }
311
312 pub fn sum_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
314 where
315 E: EntityValue,
316 {
317 self.ensure_non_paged_mode_ready()?;
318
319 self.session
320 .execute_load_query_sum_by(self.query(), field.as_ref())
321 }
322
323 pub fn avg_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
325 where
326 E: EntityValue,
327 {
328 self.ensure_non_paged_mode_ready()?;
329
330 self.session
331 .execute_load_query_avg_by(self.query(), field.as_ref())
332 }
333
334 pub fn median_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
339 where
340 E: EntityValue,
341 {
342 self.ensure_non_paged_mode_ready()?;
343
344 self.session
345 .execute_load_query_median_by(self.query(), field.as_ref())
346 }
347
348 pub fn count_distinct_by(&self, field: impl AsRef<str>) -> Result<u32, QueryError>
351 where
352 E: EntityValue,
353 {
354 self.ensure_non_paged_mode_ready()?;
355
356 self.session
357 .execute_load_query_count_distinct_by(self.query(), field.as_ref())
358 }
359
360 pub fn min_max_by(&self, field: impl AsRef<str>) -> Result<MinMaxByIds<E>, QueryError>
364 where
365 E: EntityValue,
366 {
367 self.ensure_non_paged_mode_ready()?;
368
369 self.session
370 .execute_load_query_min_max_by(self.query(), field.as_ref())
371 }
372
373 pub fn values_by(&self, field: impl AsRef<str>) -> Result<Vec<Value>, QueryError>
375 where
376 E: EntityValue,
377 {
378 self.ensure_non_paged_mode_ready()?;
379
380 self.session
381 .execute_load_query_values_by(self.query(), field.as_ref())
382 }
383
384 pub fn take(&self, take_count: u32) -> Result<Response<E>, QueryError>
386 where
387 E: EntityValue,
388 {
389 self.ensure_non_paged_mode_ready()?;
390
391 self.session
392 .execute_load_query_take(self.query(), take_count)
393 }
394
395 pub fn top_k_by(
403 &self,
404 field: impl AsRef<str>,
405 take_count: u32,
406 ) -> Result<Response<E>, QueryError>
407 where
408 E: EntityValue,
409 {
410 self.ensure_non_paged_mode_ready()?;
411
412 self.session
413 .execute_load_query_top_k_by(self.query(), field.as_ref(), take_count)
414 }
415
416 pub fn bottom_k_by(
424 &self,
425 field: impl AsRef<str>,
426 take_count: u32,
427 ) -> Result<Response<E>, QueryError>
428 where
429 E: EntityValue,
430 {
431 self.ensure_non_paged_mode_ready()?;
432
433 self.session
434 .execute_load_query_bottom_k_by(self.query(), field.as_ref(), take_count)
435 }
436
437 pub fn top_k_by_values(
445 &self,
446 field: impl AsRef<str>,
447 take_count: u32,
448 ) -> Result<Vec<Value>, QueryError>
449 where
450 E: EntityValue,
451 {
452 self.ensure_non_paged_mode_ready()?;
453
454 self.session
455 .execute_load_query_top_k_by_values(self.query(), field.as_ref(), take_count)
456 }
457
458 pub fn bottom_k_by_values(
466 &self,
467 field: impl AsRef<str>,
468 take_count: u32,
469 ) -> Result<Vec<Value>, QueryError>
470 where
471 E: EntityValue,
472 {
473 self.ensure_non_paged_mode_ready()?;
474
475 self.session
476 .execute_load_query_bottom_k_by_values(self.query(), field.as_ref(), take_count)
477 }
478
479 pub fn top_k_by_with_ids(
487 &self,
488 field: impl AsRef<str>,
489 take_count: u32,
490 ) -> Result<Vec<(Id<E>, Value)>, QueryError>
491 where
492 E: EntityValue,
493 {
494 self.ensure_non_paged_mode_ready()?;
495
496 self.session
497 .execute_load_query_top_k_by_with_ids(self.query(), field.as_ref(), take_count)
498 }
499
500 pub fn bottom_k_by_with_ids(
508 &self,
509 field: impl AsRef<str>,
510 take_count: u32,
511 ) -> Result<Vec<(Id<E>, Value)>, QueryError>
512 where
513 E: EntityValue,
514 {
515 self.ensure_non_paged_mode_ready()?;
516
517 self.session.execute_load_query_bottom_k_by_with_ids(
518 self.query(),
519 field.as_ref(),
520 take_count,
521 )
522 }
523
524 pub fn distinct_values_by(&self, field: impl AsRef<str>) -> Result<Vec<Value>, QueryError>
527 where
528 E: EntityValue,
529 {
530 self.ensure_non_paged_mode_ready()?;
531
532 self.session
533 .execute_load_query_distinct_values_by(self.query(), field.as_ref())
534 }
535
536 pub fn values_by_with_ids(
539 &self,
540 field: impl AsRef<str>,
541 ) -> Result<Vec<(Id<E>, Value)>, QueryError>
542 where
543 E: EntityValue,
544 {
545 self.ensure_non_paged_mode_ready()?;
546
547 self.session
548 .execute_load_query_values_by_with_ids(self.query(), field.as_ref())
549 }
550
551 pub fn first_value_by(&self, field: impl AsRef<str>) -> Result<Option<Value>, QueryError>
554 where
555 E: EntityValue,
556 {
557 self.ensure_non_paged_mode_ready()?;
558
559 self.session
560 .execute_load_query_first_value_by(self.query(), field.as_ref())
561 }
562
563 pub fn last_value_by(&self, field: impl AsRef<str>) -> Result<Option<Value>, QueryError>
566 where
567 E: EntityValue,
568 {
569 self.ensure_non_paged_mode_ready()?;
570
571 self.session
572 .execute_load_query_last_value_by(self.query(), field.as_ref())
573 }
574
575 pub fn first(&self) -> Result<Option<Id<E>>, QueryError>
577 where
578 E: EntityValue,
579 {
580 self.ensure_non_paged_mode_ready()?;
581
582 self.session.execute_load_query_first(self.query())
583 }
584
585 pub fn last(&self) -> Result<Option<Id<E>>, QueryError>
587 where
588 E: EntityValue,
589 {
590 self.ensure_non_paged_mode_ready()?;
591
592 self.session.execute_load_query_last(self.query())
593 }
594
595 pub fn require_one(&self) -> Result<(), QueryError>
597 where
598 E: EntityValue,
599 {
600 self.execute()?.require_one()?;
601 Ok(())
602 }
603
604 pub fn require_some(&self) -> Result<(), QueryError>
606 where
607 E: EntityValue,
608 {
609 self.execute()?.require_some()?;
610 Ok(())
611 }
612}
613
614impl<E> FluentLoadQuery<'_, E>
615where
616 E: EntityKind,
617{
618 fn non_paged_intent_error(&self) -> Option<IntentError> {
619 self.cursor_token
620 .as_ref()
621 .map(|_| IntentError::CursorRequiresPagedExecution)
622 }
623
624 fn cursor_intent_error(&self) -> Option<IntentError> {
625 self.cursor_token
626 .as_ref()
627 .and_then(|_| self.paged_intent_error())
628 }
629
630 fn paged_intent_error(&self) -> Option<IntentError> {
631 let spec = self.query.load_spec()?;
632
633 policy::validate_cursor_paging_requirements(self.query.has_explicit_order(), spec)
634 .err()
635 .map(IntentError::from)
636 }
637
638 fn ensure_paged_mode_ready(&self) -> Result<(), QueryError> {
639 if let Some(err) = self.paged_intent_error() {
640 return Err(QueryError::Intent(err));
641 }
642
643 Ok(())
644 }
645
646 fn ensure_non_paged_mode_ready(&self) -> Result<(), QueryError> {
647 if let Some(err) = self.non_paged_intent_error() {
648 return Err(QueryError::Intent(err));
649 }
650
651 Ok(())
652 }
653}
654
655impl<E> FluentLoadQuery<'_, E>
656where
657 E: EntityKind + SingletonEntity,
658 E::Key: Default,
659{
660 #[must_use]
661 pub fn only(self) -> Self {
662 self.map_query(Query::only)
663 }
664}
665
666impl<E> PagedLoadQuery<'_, E>
667where
668 E: EntityKind,
669{
670 #[must_use]
675 pub const fn query(&self) -> &Query<E> {
676 self.inner.query()
677 }
678
679 #[must_use]
685 pub fn cursor(mut self, token: impl Into<String>) -> Self {
686 self.inner = self.inner.cursor(token);
687 self
688 }
689
690 pub fn execute(self) -> Result<PagedLoadExecution<E>, QueryError>
700 where
701 E: EntityValue,
702 {
703 self.execute_with_trace()
704 .map(PagedLoadExecutionWithTrace::into_execution)
705 }
706
707 pub fn execute_with_trace(self) -> Result<PagedLoadExecutionWithTrace<E>, QueryError>
713 where
714 E: EntityValue,
715 {
716 self.inner.ensure_paged_mode_ready()?;
717
718 self.inner.session.execute_load_query_paged_with_trace(
719 self.inner.query(),
720 self.inner.cursor_token.as_deref(),
721 )
722 }
723}