1use crate::{
2 db::{
3 query::fluent::load::FluentLoadQuery,
4 query::{
5 api::ResponseCardinalityExt,
6 builder::aggregate::{exists, first, last, max, min},
7 explain::{ExplainAggregateTerminalPlan, ExplainExecutionNodeDescriptor},
8 intent::QueryError,
9 },
10 response::EntityResponse,
11 },
12 traits::{EntityKind, EntityValue},
13 types::{Decimal, Id},
14 value::Value,
15};
16
17type MinMaxByIds<E> = Option<(Id<E>, Id<E>)>;
18
19impl<E> FluentLoadQuery<'_, E>
20where
21 E: EntityKind,
22{
23 pub fn execute(&self) -> Result<EntityResponse<E>, QueryError>
29 where
30 E: EntityValue,
31 {
32 self.ensure_non_paged_mode_ready()?;
33
34 self.session.execute_query(self.query())
35 }
36
37 pub fn is_empty(&self) -> Result<bool, QueryError>
43 where
44 E: EntityValue,
45 {
46 Ok(!self.exists()?)
47 }
48
49 pub fn exists(&self) -> Result<bool, QueryError>
51 where
52 E: EntityValue,
53 {
54 self.ensure_non_paged_mode_ready()?;
55
56 self.session
57 .execute_load_query_with(self.query(), |load, plan| load.aggregate_exists(plan))
58 }
59
60 pub fn explain_exists(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
62 where
63 E: EntityValue,
64 {
65 self.ensure_non_paged_mode_ready()?;
66
67 crate::db::DbSession::<E::Canister>::explain_load_query_terminal_with(
68 self.query(),
69 exists(),
70 )
71 }
72
73 pub fn explain_execution(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
75 where
76 E: EntityValue,
77 {
78 self.query().explain_execution()
79 }
80
81 pub fn explain_execution_text(&self) -> Result<String, QueryError>
83 where
84 E: EntityValue,
85 {
86 self.query().explain_execution_text()
87 }
88
89 pub fn explain_execution_json(&self) -> Result<String, QueryError>
91 where
92 E: EntityValue,
93 {
94 self.query().explain_execution_json()
95 }
96
97 pub fn explain_execution_verbose(&self) -> Result<String, QueryError>
99 where
100 E: EntityValue,
101 {
102 self.query().explain_execution_verbose()
103 }
104
105 pub fn count(&self) -> Result<u32, QueryError>
107 where
108 E: EntityValue,
109 {
110 self.ensure_non_paged_mode_ready()?;
111
112 self.session
113 .execute_load_query_with(self.query(), |load, plan| load.aggregate_count(plan))
114 }
115
116 pub fn bytes(&self) -> Result<u64, QueryError>
119 where
120 E: EntityValue,
121 {
122 self.ensure_non_paged_mode_ready()?;
123
124 self.session
125 .execute_load_query_with(self.query(), |load, plan| load.bytes(plan))
126 }
127
128 pub fn min(&self) -> Result<Option<Id<E>>, QueryError>
130 where
131 E: EntityValue,
132 {
133 self.ensure_non_paged_mode_ready()?;
134
135 self.session
136 .execute_load_query_with(self.query(), |load, plan| load.aggregate_min(plan))
137 }
138
139 pub fn explain_min(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
141 where
142 E: EntityValue,
143 {
144 self.ensure_non_paged_mode_ready()?;
145
146 crate::db::DbSession::<E::Canister>::explain_load_query_terminal_with(self.query(), min())
147 }
148
149 pub fn min_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
153 where
154 E: EntityValue,
155 {
156 self.ensure_non_paged_mode_ready()?;
157
158 Self::with_slot(field, |target_slot| {
159 self.session
160 .execute_load_query_with(self.query(), move |load, plan| {
161 load.aggregate_min_by_slot(plan, target_slot)
162 })
163 })
164 }
165
166 pub fn max(&self) -> Result<Option<Id<E>>, QueryError>
168 where
169 E: EntityValue,
170 {
171 self.ensure_non_paged_mode_ready()?;
172
173 self.session
174 .execute_load_query_with(self.query(), |load, plan| load.aggregate_max(plan))
175 }
176
177 pub fn explain_max(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
179 where
180 E: EntityValue,
181 {
182 self.ensure_non_paged_mode_ready()?;
183
184 crate::db::DbSession::<E::Canister>::explain_load_query_terminal_with(self.query(), max())
185 }
186
187 pub fn max_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
191 where
192 E: EntityValue,
193 {
194 self.ensure_non_paged_mode_ready()?;
195
196 Self::with_slot(field, |target_slot| {
197 self.session
198 .execute_load_query_with(self.query(), move |load, plan| {
199 load.aggregate_max_by_slot(plan, target_slot)
200 })
201 })
202 }
203
204 pub fn nth_by(&self, field: impl AsRef<str>, nth: usize) -> Result<Option<Id<E>>, QueryError>
207 where
208 E: EntityValue,
209 {
210 self.ensure_non_paged_mode_ready()?;
211
212 Self::with_slot(field, |target_slot| {
213 self.session
214 .execute_load_query_with(self.query(), move |load, plan| {
215 load.aggregate_nth_by_slot(plan, target_slot, nth)
216 })
217 })
218 }
219
220 pub fn sum_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
222 where
223 E: EntityValue,
224 {
225 self.ensure_non_paged_mode_ready()?;
226
227 Self::with_slot(field, |target_slot| {
228 self.session
229 .execute_load_query_with(self.query(), move |load, plan| {
230 load.aggregate_sum_by_slot(plan, target_slot)
231 })
232 })
233 }
234
235 pub fn sum_distinct_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
237 where
238 E: EntityValue,
239 {
240 self.ensure_non_paged_mode_ready()?;
241
242 Self::with_slot(field, |target_slot| {
243 self.session
244 .execute_load_query_with(self.query(), move |load, plan| {
245 load.aggregate_sum_distinct_by_slot(plan, target_slot)
246 })
247 })
248 }
249
250 pub fn avg_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
252 where
253 E: EntityValue,
254 {
255 self.ensure_non_paged_mode_ready()?;
256
257 Self::with_slot(field, |target_slot| {
258 self.session
259 .execute_load_query_with(self.query(), move |load, plan| {
260 load.aggregate_avg_by_slot(plan, target_slot)
261 })
262 })
263 }
264
265 pub fn median_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
270 where
271 E: EntityValue,
272 {
273 self.ensure_non_paged_mode_ready()?;
274
275 Self::with_slot(field, |target_slot| {
276 self.session
277 .execute_load_query_with(self.query(), move |load, plan| {
278 load.aggregate_median_by_slot(plan, target_slot)
279 })
280 })
281 }
282
283 pub fn count_distinct_by(&self, field: impl AsRef<str>) -> Result<u32, QueryError>
286 where
287 E: EntityValue,
288 {
289 self.ensure_non_paged_mode_ready()?;
290
291 Self::with_slot(field, |target_slot| {
292 self.session
293 .execute_load_query_with(self.query(), move |load, plan| {
294 load.aggregate_count_distinct_by_slot(plan, target_slot)
295 })
296 })
297 }
298
299 pub fn min_max_by(&self, field: impl AsRef<str>) -> Result<MinMaxByIds<E>, QueryError>
303 where
304 E: EntityValue,
305 {
306 self.ensure_non_paged_mode_ready()?;
307
308 Self::with_slot(field, |target_slot| {
309 self.session
310 .execute_load_query_with(self.query(), move |load, plan| {
311 load.aggregate_min_max_by_slot(plan, target_slot)
312 })
313 })
314 }
315
316 pub fn values_by(&self, field: impl AsRef<str>) -> Result<Vec<Value>, QueryError>
318 where
319 E: EntityValue,
320 {
321 self.ensure_non_paged_mode_ready()?;
322
323 Self::with_slot(field, |target_slot| {
324 self.session
325 .execute_load_query_with(self.query(), move |load, plan| {
326 load.values_by_slot(plan, target_slot)
327 })
328 })
329 }
330
331 pub fn take(&self, take_count: u32) -> Result<EntityResponse<E>, QueryError>
333 where
334 E: EntityValue,
335 {
336 self.ensure_non_paged_mode_ready()?;
337
338 self.session
339 .execute_load_query_with(self.query(), |load, plan| load.take(plan, take_count))
340 }
341
342 pub fn top_k_by(
350 &self,
351 field: impl AsRef<str>,
352 take_count: u32,
353 ) -> Result<EntityResponse<E>, QueryError>
354 where
355 E: EntityValue,
356 {
357 self.ensure_non_paged_mode_ready()?;
358
359 Self::with_slot(field, |target_slot| {
360 self.session
361 .execute_load_query_with(self.query(), move |load, plan| {
362 load.top_k_by_slot(plan, target_slot, take_count)
363 })
364 })
365 }
366
367 pub fn bottom_k_by(
375 &self,
376 field: impl AsRef<str>,
377 take_count: u32,
378 ) -> Result<EntityResponse<E>, QueryError>
379 where
380 E: EntityValue,
381 {
382 self.ensure_non_paged_mode_ready()?;
383
384 Self::with_slot(field, |target_slot| {
385 self.session
386 .execute_load_query_with(self.query(), move |load, plan| {
387 load.bottom_k_by_slot(plan, target_slot, take_count)
388 })
389 })
390 }
391
392 pub fn top_k_by_values(
400 &self,
401 field: impl AsRef<str>,
402 take_count: u32,
403 ) -> Result<Vec<Value>, QueryError>
404 where
405 E: EntityValue,
406 {
407 self.ensure_non_paged_mode_ready()?;
408
409 Self::with_slot(field, |target_slot| {
410 self.session
411 .execute_load_query_with(self.query(), move |load, plan| {
412 load.top_k_by_values_slot(plan, target_slot, take_count)
413 })
414 })
415 }
416
417 pub fn bottom_k_by_values(
425 &self,
426 field: impl AsRef<str>,
427 take_count: u32,
428 ) -> Result<Vec<Value>, QueryError>
429 where
430 E: EntityValue,
431 {
432 self.ensure_non_paged_mode_ready()?;
433
434 Self::with_slot(field, |target_slot| {
435 self.session
436 .execute_load_query_with(self.query(), move |load, plan| {
437 load.bottom_k_by_values_slot(plan, target_slot, take_count)
438 })
439 })
440 }
441
442 pub fn top_k_by_with_ids(
450 &self,
451 field: impl AsRef<str>,
452 take_count: u32,
453 ) -> Result<Vec<(Id<E>, Value)>, QueryError>
454 where
455 E: EntityValue,
456 {
457 self.ensure_non_paged_mode_ready()?;
458
459 Self::with_slot(field, |target_slot| {
460 self.session
461 .execute_load_query_with(self.query(), move |load, plan| {
462 load.top_k_by_with_ids_slot(plan, target_slot, take_count)
463 })
464 })
465 }
466
467 pub fn bottom_k_by_with_ids(
475 &self,
476 field: impl AsRef<str>,
477 take_count: u32,
478 ) -> Result<Vec<(Id<E>, Value)>, QueryError>
479 where
480 E: EntityValue,
481 {
482 self.ensure_non_paged_mode_ready()?;
483
484 Self::with_slot(field, |target_slot| {
485 self.session
486 .execute_load_query_with(self.query(), move |load, plan| {
487 load.bottom_k_by_with_ids_slot(plan, target_slot, take_count)
488 })
489 })
490 }
491
492 pub fn distinct_values_by(&self, field: impl AsRef<str>) -> Result<Vec<Value>, QueryError>
495 where
496 E: EntityValue,
497 {
498 self.ensure_non_paged_mode_ready()?;
499
500 Self::with_slot(field, |target_slot| {
501 self.session
502 .execute_load_query_with(self.query(), move |load, plan| {
503 load.distinct_values_by_slot(plan, target_slot)
504 })
505 })
506 }
507
508 pub fn values_by_with_ids(
511 &self,
512 field: impl AsRef<str>,
513 ) -> Result<Vec<(Id<E>, Value)>, QueryError>
514 where
515 E: EntityValue,
516 {
517 self.ensure_non_paged_mode_ready()?;
518
519 Self::with_slot(field, |target_slot| {
520 self.session
521 .execute_load_query_with(self.query(), move |load, plan| {
522 load.values_by_with_ids_slot(plan, target_slot)
523 })
524 })
525 }
526
527 pub fn first_value_by(&self, field: impl AsRef<str>) -> Result<Option<Value>, QueryError>
530 where
531 E: EntityValue,
532 {
533 self.ensure_non_paged_mode_ready()?;
534
535 Self::with_slot(field, |target_slot| {
536 self.session
537 .execute_load_query_with(self.query(), move |load, plan| {
538 load.first_value_by_slot(plan, target_slot)
539 })
540 })
541 }
542
543 pub fn last_value_by(&self, field: impl AsRef<str>) -> Result<Option<Value>, QueryError>
546 where
547 E: EntityValue,
548 {
549 self.ensure_non_paged_mode_ready()?;
550
551 Self::with_slot(field, |target_slot| {
552 self.session
553 .execute_load_query_with(self.query(), move |load, plan| {
554 load.last_value_by_slot(plan, target_slot)
555 })
556 })
557 }
558
559 pub fn first(&self) -> Result<Option<Id<E>>, QueryError>
561 where
562 E: EntityValue,
563 {
564 self.ensure_non_paged_mode_ready()?;
565
566 self.session
567 .execute_load_query_with(self.query(), |load, plan| load.aggregate_first(plan))
568 }
569
570 pub fn explain_first(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
572 where
573 E: EntityValue,
574 {
575 self.ensure_non_paged_mode_ready()?;
576
577 crate::db::DbSession::<E::Canister>::explain_load_query_terminal_with(self.query(), first())
578 }
579
580 pub fn last(&self) -> Result<Option<Id<E>>, QueryError>
582 where
583 E: EntityValue,
584 {
585 self.ensure_non_paged_mode_ready()?;
586
587 self.session
588 .execute_load_query_with(self.query(), |load, plan| load.aggregate_last(plan))
589 }
590
591 pub fn explain_last(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
593 where
594 E: EntityValue,
595 {
596 self.ensure_non_paged_mode_ready()?;
597
598 crate::db::DbSession::<E::Canister>::explain_load_query_terminal_with(self.query(), last())
599 }
600
601 pub fn require_one(&self) -> Result<(), QueryError>
603 where
604 E: EntityValue,
605 {
606 self.execute()?.require_one()?;
607 Ok(())
608 }
609
610 pub fn require_some(&self) -> Result<(), QueryError>
612 where
613 E: EntityValue,
614 {
615 self.execute()?.require_some()?;
616 Ok(())
617 }
618}