1use crate::{
2 db::{
3 DbSession, PagedLoadExecution, PagedLoadExecutionWithTrace,
4 executor::ExecutablePlan,
5 query::{
6 explain::ExplainPlan,
7 expr::{FilterExpr, SortExpr},
8 intent::{IntentError, Query, QueryError},
9 policy,
10 predicate::Predicate,
11 },
12 response::Response,
13 },
14 traits::{EntityKind, EntityValue, SingletonEntity},
15 types::{Decimal, Id},
16 value::Value,
17};
18
19type MinMaxByIds<E> = Option<(Id<E>, Id<E>)>;
20
21pub struct FluentLoadQuery<'a, E>
30where
31 E: EntityKind,
32{
33 session: &'a DbSession<E::Canister>,
34 query: Query<E>,
35 cursor_token: Option<String>,
36}
37
38pub struct PagedLoadQuery<'a, E>
46where
47 E: EntityKind,
48{
49 inner: FluentLoadQuery<'a, E>,
50}
51
52impl<'a, E> FluentLoadQuery<'a, E>
53where
54 E: EntityKind,
55{
56 pub(crate) const fn new(session: &'a DbSession<E::Canister>, query: Query<E>) -> Self {
57 Self {
58 session,
59 query,
60 cursor_token: None,
61 }
62 }
63
64 #[must_use]
69 pub const fn query(&self) -> &Query<E> {
70 &self.query
71 }
72
73 fn map_query(mut self, map: impl FnOnce(Query<E>) -> Query<E>) -> Self {
74 self.query = map(self.query);
75 self
76 }
77
78 fn try_map_query(
79 mut self,
80 map: impl FnOnce(Query<E>) -> Result<Query<E>, QueryError>,
81 ) -> Result<Self, QueryError> {
82 self.query = map(self.query)?;
83 Ok(self)
84 }
85
86 #[must_use]
94 pub fn by_id(mut self, id: Id<E>) -> Self {
95 self.query = self.query.by_id(id.key());
96 self
97 }
98
99 #[must_use]
103 pub fn by_ids<I>(mut self, ids: I) -> Self
104 where
105 I: IntoIterator<Item = Id<E>>,
106 {
107 self.query = self.query.by_ids(ids.into_iter().map(|id| id.key()));
108 self
109 }
110
111 #[must_use]
116 pub fn filter(self, predicate: Predicate) -> Self {
117 self.map_query(|query| query.filter(predicate))
118 }
119
120 pub fn filter_expr(self, expr: FilterExpr) -> Result<Self, QueryError> {
121 self.try_map_query(|query| query.filter_expr(expr))
122 }
123
124 pub fn sort_expr(self, expr: SortExpr) -> Result<Self, QueryError> {
125 self.try_map_query(|query| query.sort_expr(expr))
126 }
127
128 #[must_use]
129 pub fn order_by(self, field: impl AsRef<str>) -> Self {
130 self.map_query(|query| query.order_by(field))
131 }
132
133 #[must_use]
134 pub fn order_by_desc(self, field: impl AsRef<str>) -> Self {
135 self.map_query(|query| query.order_by_desc(field))
136 }
137
138 #[must_use]
143 pub fn limit(self, limit: u32) -> Self {
144 self.map_query(|query| query.limit(limit))
145 }
146
147 #[must_use]
152 pub fn offset(self, offset: u32) -> Self {
153 self.map_query(|query| query.offset(offset))
154 }
155
156 #[must_use]
162 pub fn cursor(mut self, token: impl Into<String>) -> Self {
163 self.cursor_token = Some(token.into());
164 self
165 }
166
167 pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
172 self.query.explain()
173 }
174
175 pub fn plan(&self) -> Result<ExecutablePlan<E>, QueryError> {
176 if let Some(err) = self.cursor_intent_error() {
177 return Err(QueryError::Intent(err));
178 }
179
180 self.query.plan()
181 }
182
183 pub fn execute(&self) -> Result<Response<E>, QueryError>
189 where
190 E: EntityValue,
191 {
192 self.ensure_non_paged_mode_ready()?;
193
194 self.session.execute_query(self.query())
195 }
196
197 pub fn page(self) -> Result<PagedLoadQuery<'a, E>, QueryError> {
208 self.ensure_paged_mode_ready()?;
209
210 Ok(PagedLoadQuery { inner: self })
211 }
212
213 pub fn execute_paged(self) -> Result<PagedLoadExecution<E>, QueryError>
217 where
218 E: EntityValue,
219 {
220 self.page()?.execute()
221 }
222
223 pub fn is_empty(&self) -> Result<bool, QueryError>
229 where
230 E: EntityValue,
231 {
232 Ok(!self.exists()?)
233 }
234
235 pub fn exists(&self) -> Result<bool, QueryError>
237 where
238 E: EntityValue,
239 {
240 self.ensure_non_paged_mode_ready()?;
241
242 self.session.execute_load_query_exists(self.query())
243 }
244
245 pub fn count(&self) -> Result<u32, QueryError>
247 where
248 E: EntityValue,
249 {
250 self.ensure_non_paged_mode_ready()?;
251
252 self.session.execute_load_query_count(self.query())
253 }
254
255 pub fn min(&self) -> Result<Option<Id<E>>, QueryError>
257 where
258 E: EntityValue,
259 {
260 self.ensure_non_paged_mode_ready()?;
261
262 self.session.execute_load_query_min(self.query())
263 }
264
265 pub fn min_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
269 where
270 E: EntityValue,
271 {
272 self.ensure_non_paged_mode_ready()?;
273
274 self.session
275 .execute_load_query_min_by(self.query(), field.as_ref())
276 }
277
278 pub fn max(&self) -> Result<Option<Id<E>>, QueryError>
280 where
281 E: EntityValue,
282 {
283 self.ensure_non_paged_mode_ready()?;
284
285 self.session.execute_load_query_max(self.query())
286 }
287
288 pub fn max_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
292 where
293 E: EntityValue,
294 {
295 self.ensure_non_paged_mode_ready()?;
296
297 self.session
298 .execute_load_query_max_by(self.query(), field.as_ref())
299 }
300
301 pub fn nth_by(&self, field: impl AsRef<str>, nth: usize) -> Result<Option<Id<E>>, QueryError>
304 where
305 E: EntityValue,
306 {
307 self.ensure_non_paged_mode_ready()?;
308
309 self.session
310 .execute_load_query_nth_by(self.query(), field.as_ref(), nth)
311 }
312
313 pub fn sum_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
315 where
316 E: EntityValue,
317 {
318 self.ensure_non_paged_mode_ready()?;
319
320 self.session
321 .execute_load_query_sum_by(self.query(), field.as_ref())
322 }
323
324 pub fn avg_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
326 where
327 E: EntityValue,
328 {
329 self.ensure_non_paged_mode_ready()?;
330
331 self.session
332 .execute_load_query_avg_by(self.query(), field.as_ref())
333 }
334
335 pub fn median_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
340 where
341 E: EntityValue,
342 {
343 self.ensure_non_paged_mode_ready()?;
344
345 self.session
346 .execute_load_query_median_by(self.query(), field.as_ref())
347 }
348
349 pub fn count_distinct_by(&self, field: impl AsRef<str>) -> Result<u32, QueryError>
352 where
353 E: EntityValue,
354 {
355 self.ensure_non_paged_mode_ready()?;
356
357 self.session
358 .execute_load_query_count_distinct_by(self.query(), field.as_ref())
359 }
360
361 pub fn min_max_by(&self, field: impl AsRef<str>) -> Result<MinMaxByIds<E>, QueryError>
365 where
366 E: EntityValue,
367 {
368 self.ensure_non_paged_mode_ready()?;
369
370 self.session
371 .execute_load_query_min_max_by(self.query(), field.as_ref())
372 }
373
374 pub fn values_by(&self, field: impl AsRef<str>) -> Result<Vec<Value>, QueryError>
376 where
377 E: EntityValue,
378 {
379 self.ensure_non_paged_mode_ready()?;
380
381 self.session
382 .execute_load_query_values_by(self.query(), field.as_ref())
383 }
384
385 pub fn take(&self, take_count: u32) -> Result<Response<E>, QueryError>
387 where
388 E: EntityValue,
389 {
390 self.ensure_non_paged_mode_ready()?;
391
392 self.session
393 .execute_load_query_take(self.query(), take_count)
394 }
395
396 pub fn top_k_by(
404 &self,
405 field: impl AsRef<str>,
406 take_count: u32,
407 ) -> Result<Response<E>, QueryError>
408 where
409 E: EntityValue,
410 {
411 self.ensure_non_paged_mode_ready()?;
412
413 self.session
414 .execute_load_query_top_k_by(self.query(), field.as_ref(), take_count)
415 }
416
417 pub fn bottom_k_by(
425 &self,
426 field: impl AsRef<str>,
427 take_count: u32,
428 ) -> Result<Response<E>, QueryError>
429 where
430 E: EntityValue,
431 {
432 self.ensure_non_paged_mode_ready()?;
433
434 self.session
435 .execute_load_query_bottom_k_by(self.query(), field.as_ref(), take_count)
436 }
437
438 pub fn top_k_by_values(
446 &self,
447 field: impl AsRef<str>,
448 take_count: u32,
449 ) -> Result<Vec<Value>, QueryError>
450 where
451 E: EntityValue,
452 {
453 self.ensure_non_paged_mode_ready()?;
454
455 self.session
456 .execute_load_query_top_k_by_values(self.query(), field.as_ref(), take_count)
457 }
458
459 pub fn bottom_k_by_values(
467 &self,
468 field: impl AsRef<str>,
469 take_count: u32,
470 ) -> Result<Vec<Value>, QueryError>
471 where
472 E: EntityValue,
473 {
474 self.ensure_non_paged_mode_ready()?;
475
476 self.session
477 .execute_load_query_bottom_k_by_values(self.query(), field.as_ref(), take_count)
478 }
479
480 pub fn top_k_by_with_ids(
488 &self,
489 field: impl AsRef<str>,
490 take_count: u32,
491 ) -> Result<Vec<(Id<E>, Value)>, QueryError>
492 where
493 E: EntityValue,
494 {
495 self.ensure_non_paged_mode_ready()?;
496
497 self.session
498 .execute_load_query_top_k_by_with_ids(self.query(), field.as_ref(), take_count)
499 }
500
501 pub fn bottom_k_by_with_ids(
509 &self,
510 field: impl AsRef<str>,
511 take_count: u32,
512 ) -> Result<Vec<(Id<E>, Value)>, QueryError>
513 where
514 E: EntityValue,
515 {
516 self.ensure_non_paged_mode_ready()?;
517
518 self.session.execute_load_query_bottom_k_by_with_ids(
519 self.query(),
520 field.as_ref(),
521 take_count,
522 )
523 }
524
525 pub fn distinct_values_by(&self, field: impl AsRef<str>) -> Result<Vec<Value>, QueryError>
528 where
529 E: EntityValue,
530 {
531 self.ensure_non_paged_mode_ready()?;
532
533 self.session
534 .execute_load_query_distinct_values_by(self.query(), field.as_ref())
535 }
536
537 pub fn values_by_with_ids(
540 &self,
541 field: impl AsRef<str>,
542 ) -> Result<Vec<(Id<E>, Value)>, QueryError>
543 where
544 E: EntityValue,
545 {
546 self.ensure_non_paged_mode_ready()?;
547
548 self.session
549 .execute_load_query_values_by_with_ids(self.query(), field.as_ref())
550 }
551
552 pub fn first_value_by(&self, field: impl AsRef<str>) -> Result<Option<Value>, QueryError>
555 where
556 E: EntityValue,
557 {
558 self.ensure_non_paged_mode_ready()?;
559
560 self.session
561 .execute_load_query_first_value_by(self.query(), field.as_ref())
562 }
563
564 pub fn last_value_by(&self, field: impl AsRef<str>) -> Result<Option<Value>, QueryError>
567 where
568 E: EntityValue,
569 {
570 self.ensure_non_paged_mode_ready()?;
571
572 self.session
573 .execute_load_query_last_value_by(self.query(), field.as_ref())
574 }
575
576 pub fn first(&self) -> Result<Option<Id<E>>, QueryError>
578 where
579 E: EntityValue,
580 {
581 self.ensure_non_paged_mode_ready()?;
582
583 self.session.execute_load_query_first(self.query())
584 }
585
586 pub fn last(&self) -> Result<Option<Id<E>>, QueryError>
588 where
589 E: EntityValue,
590 {
591 self.ensure_non_paged_mode_ready()?;
592
593 self.session.execute_load_query_last(self.query())
594 }
595
596 pub fn require_one(&self) -> Result<(), QueryError>
598 where
599 E: EntityValue,
600 {
601 self.execute()?.require_one()?;
602 Ok(())
603 }
604
605 pub fn require_some(&self) -> Result<(), QueryError>
607 where
608 E: EntityValue,
609 {
610 self.execute()?.require_some()?;
611 Ok(())
612 }
613}
614
615impl<E> FluentLoadQuery<'_, E>
616where
617 E: EntityKind,
618{
619 fn non_paged_intent_error(&self) -> Option<IntentError> {
620 self.cursor_token
621 .as_ref()
622 .map(|_| IntentError::CursorRequiresPagedExecution)
623 }
624
625 fn cursor_intent_error(&self) -> Option<IntentError> {
626 self.cursor_token
627 .as_ref()
628 .and_then(|_| self.paged_intent_error())
629 }
630
631 fn paged_intent_error(&self) -> Option<IntentError> {
632 let spec = self.query.load_spec()?;
633
634 policy::validate_cursor_paging_requirements(self.query.has_explicit_order(), spec)
635 .err()
636 .map(IntentError::from)
637 }
638
639 fn ensure_paged_mode_ready(&self) -> Result<(), QueryError> {
640 if let Some(err) = self.paged_intent_error() {
641 return Err(QueryError::Intent(err));
642 }
643
644 Ok(())
645 }
646
647 fn ensure_non_paged_mode_ready(&self) -> Result<(), QueryError> {
648 if let Some(err) = self.non_paged_intent_error() {
649 return Err(QueryError::Intent(err));
650 }
651
652 Ok(())
653 }
654}
655
656impl<E> FluentLoadQuery<'_, E>
657where
658 E: EntityKind + SingletonEntity,
659 E::Key: Default,
660{
661 #[must_use]
662 pub fn only(self) -> Self {
663 self.map_query(Query::only)
664 }
665}
666
667impl<E> PagedLoadQuery<'_, E>
668where
669 E: EntityKind,
670{
671 #[must_use]
676 pub const fn query(&self) -> &Query<E> {
677 self.inner.query()
678 }
679
680 #[must_use]
686 pub fn cursor(mut self, token: impl Into<String>) -> Self {
687 self.inner = self.inner.cursor(token);
688 self
689 }
690
691 pub fn execute(self) -> Result<PagedLoadExecution<E>, QueryError>
701 where
702 E: EntityValue,
703 {
704 self.execute_with_trace()
705 .map(PagedLoadExecutionWithTrace::into_execution)
706 }
707
708 pub fn execute_with_trace(self) -> Result<PagedLoadExecutionWithTrace<E>, QueryError>
714 where
715 E: EntityValue,
716 {
717 self.inner.ensure_paged_mode_ready()?;
718
719 self.inner.session.execute_load_query_paged_with_trace(
720 self.inner.query(),
721 self.inner.cursor_token.as_deref(),
722 )
723 }
724}