Skip to main content

icydb_core/db/query/fluent/load/
terminals.rs

1//! Module: query::fluent::load::terminals
2//! Responsibility: fluent load terminal APIs and terminal-plan explanation entrypoints.
3//! Does not own: planner semantic validation or executor runtime routing decisions.
4//! Boundary: delegates to session planning/execution and returns typed query results.
5
6use crate::{
7    db::{
8        DbSession, PersistedRow,
9        executor::{
10            ExecutablePlan, LoadExecutor, ScalarNumericFieldBoundaryRequest,
11            ScalarProjectionBoundaryRequest, ScalarTerminalBoundaryRequest,
12        },
13        query::{
14            api::ResponseCardinalityExt,
15            builder::{
16                AggregateExpr,
17                aggregate::{exists, first, last, max, min},
18            },
19            explain::{ExplainAggregateTerminalPlan, ExplainExecutionNodeDescriptor},
20            fluent::load::FluentLoadQuery,
21            intent::QueryError,
22            plan::AggregateKind,
23        },
24        response::EntityResponse,
25    },
26    error::InternalError,
27    traits::EntityValue,
28    types::{Decimal, Id},
29    value::Value,
30};
31
32type MinMaxByIds<E> = Option<(Id<E>, Id<E>)>;
33
34impl<E> FluentLoadQuery<'_, E>
35where
36    E: PersistedRow,
37{
38    // ------------------------------------------------------------------
39    // Execution (single semantic boundary)
40    // ------------------------------------------------------------------
41
42    /// Execute this query using the session's policy settings.
43    pub fn execute(&self) -> Result<EntityResponse<E>, QueryError>
44    where
45        E: EntityValue,
46    {
47        self.ensure_non_paged_mode_ready()?;
48
49        self.session.execute_query(self.query())
50    }
51
52    // Run one scalar terminal through the canonical non-paged fluent policy
53    // gate before handing execution to the session load-query adapter.
54    fn execute_scalar_non_paged_terminal<T, F>(&self, execute: F) -> Result<T, QueryError>
55    where
56        E: EntityValue,
57        F: FnOnce(LoadExecutor<E>, ExecutablePlan<E>) -> Result<T, InternalError>,
58    {
59        self.ensure_non_paged_mode_ready()?;
60
61        self.session.execute_load_query_with(self.query(), execute)
62    }
63
64    // Run one scalar aggregate EXPLAIN terminal through the canonical
65    // non-paged fluent policy gate.
66    fn explain_scalar_non_paged_terminal(
67        &self,
68        aggregate: AggregateExpr,
69    ) -> Result<ExplainAggregateTerminalPlan, QueryError>
70    where
71        E: EntityValue,
72    {
73        self.ensure_non_paged_mode_ready()?;
74
75        DbSession::<E::Canister>::explain_load_query_terminal_with(self.query(), aggregate)
76    }
77
78    // ------------------------------------------------------------------
79    // Execution terminals — semantic only
80    // ------------------------------------------------------------------
81
82    /// Execute and return whether the result set is empty.
83    pub fn is_empty(&self) -> Result<bool, QueryError>
84    where
85        E: EntityValue,
86    {
87        self.not_exists()
88    }
89
90    /// Execute and return whether no matching row exists.
91    pub fn not_exists(&self) -> Result<bool, QueryError>
92    where
93        E: EntityValue,
94    {
95        Ok(!self.exists()?)
96    }
97
98    /// Execute and return whether at least one matching row exists.
99    pub fn exists(&self) -> Result<bool, QueryError>
100    where
101        E: EntityValue,
102    {
103        self.execute_scalar_non_paged_terminal(|load, plan| {
104            load.execute_scalar_terminal_request(plan, ScalarTerminalBoundaryRequest::Exists)?
105                .into_exists()
106        })
107    }
108
109    /// Explain scalar `exists()` routing without executing the terminal.
110    pub fn explain_exists(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
111    where
112        E: EntityValue,
113    {
114        self.explain_scalar_non_paged_terminal(exists())
115    }
116
117    /// Explain scalar `not_exists()` routing without executing the terminal.
118    ///
119    /// This remains an `exists()` execution plan with negated boolean semantics.
120    pub fn explain_not_exists(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
121    where
122        E: EntityValue,
123    {
124        self.explain_exists()
125    }
126
127    /// Explain scalar load execution shape without executing the query.
128    pub fn explain_execution(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
129    where
130        E: EntityValue,
131    {
132        self.query().explain_execution()
133    }
134
135    /// Explain scalar load execution shape as deterministic text.
136    pub fn explain_execution_text(&self) -> Result<String, QueryError>
137    where
138        E: EntityValue,
139    {
140        self.query().explain_execution_text()
141    }
142
143    /// Explain scalar load execution shape as canonical JSON.
144    pub fn explain_execution_json(&self) -> Result<String, QueryError>
145    where
146        E: EntityValue,
147    {
148        self.query().explain_execution_json()
149    }
150
151    /// Explain scalar load execution shape as verbose text with diagnostics.
152    pub fn explain_execution_verbose(&self) -> Result<String, QueryError>
153    where
154        E: EntityValue,
155    {
156        self.query().explain_execution_verbose()
157    }
158
159    /// Execute and return the number of matching rows.
160    pub fn count(&self) -> Result<u32, QueryError>
161    where
162        E: EntityValue,
163    {
164        self.execute_scalar_non_paged_terminal(|load, plan| {
165            load.execute_scalar_terminal_request(plan, ScalarTerminalBoundaryRequest::Count)?
166                .into_count()
167        })
168    }
169
170    /// Execute and return the total persisted payload bytes for the effective
171    /// result window.
172    pub fn bytes(&self) -> Result<u64, QueryError>
173    where
174        E: EntityValue,
175    {
176        self.execute_scalar_non_paged_terminal(|load, plan| load.bytes(plan))
177    }
178
179    /// Execute and return the total serialized bytes for `field` over the
180    /// effective result window.
181    pub fn bytes_by(&self, field: impl AsRef<str>) -> Result<u64, QueryError>
182    where
183        E: EntityValue,
184    {
185        self.ensure_non_paged_mode_ready()?;
186
187        Self::with_slot(field, |target_slot| {
188            self.session
189                .execute_load_query_with(self.query(), move |load, plan| {
190                    load.bytes_by_slot(plan, target_slot)
191                })
192        })
193    }
194
195    /// Explain `bytes_by(field)` routing without executing the terminal.
196    pub fn explain_bytes_by(
197        &self,
198        field: impl AsRef<str>,
199    ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
200    where
201        E: EntityValue,
202    {
203        self.ensure_non_paged_mode_ready()?;
204
205        Self::with_slot(field, |target_slot| {
206            DbSession::<E::Canister>::explain_load_query_bytes_by_with(
207                self.query(),
208                target_slot.field(),
209            )
210        })
211    }
212
213    /// Execute and return the smallest matching identifier, if any.
214    pub fn min(&self) -> Result<Option<Id<E>>, QueryError>
215    where
216        E: EntityValue,
217    {
218        self.execute_scalar_non_paged_terminal(|load, plan| {
219            load.execute_scalar_terminal_request(
220                plan,
221                ScalarTerminalBoundaryRequest::IdTerminal {
222                    kind: AggregateKind::Min,
223                },
224            )?
225            .into_id()
226        })
227    }
228
229    /// Explain scalar `min()` routing without executing the terminal.
230    pub fn explain_min(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
231    where
232        E: EntityValue,
233    {
234        self.explain_scalar_non_paged_terminal(min())
235    }
236
237    /// Execute and return the id of the row with the smallest value for `field`.
238    ///
239    /// Ties are deterministic: equal field values resolve by primary key ascending.
240    pub fn min_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
241    where
242        E: EntityValue,
243    {
244        self.ensure_non_paged_mode_ready()?;
245
246        Self::with_slot(field, |target_slot| {
247            self.session
248                .execute_load_query_with(self.query(), move |load, plan| {
249                    load.execute_scalar_terminal_request(
250                        plan,
251                        ScalarTerminalBoundaryRequest::IdBySlot {
252                            kind: AggregateKind::Min,
253                            target_field: target_slot,
254                        },
255                    )?
256                    .into_id()
257                })
258        })
259    }
260
261    /// Execute and return the largest matching identifier, if any.
262    pub fn max(&self) -> Result<Option<Id<E>>, QueryError>
263    where
264        E: EntityValue,
265    {
266        self.execute_scalar_non_paged_terminal(|load, plan| {
267            load.execute_scalar_terminal_request(
268                plan,
269                ScalarTerminalBoundaryRequest::IdTerminal {
270                    kind: AggregateKind::Max,
271                },
272            )?
273            .into_id()
274        })
275    }
276
277    /// Explain scalar `max()` routing without executing the terminal.
278    pub fn explain_max(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
279    where
280        E: EntityValue,
281    {
282        self.explain_scalar_non_paged_terminal(max())
283    }
284
285    /// Execute and return the id of the row with the largest value for `field`.
286    ///
287    /// Ties are deterministic: equal field values resolve by primary key ascending.
288    pub fn max_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
289    where
290        E: EntityValue,
291    {
292        self.ensure_non_paged_mode_ready()?;
293
294        Self::with_slot(field, |target_slot| {
295            self.session
296                .execute_load_query_with(self.query(), move |load, plan| {
297                    load.execute_scalar_terminal_request(
298                        plan,
299                        ScalarTerminalBoundaryRequest::IdBySlot {
300                            kind: AggregateKind::Max,
301                            target_field: target_slot,
302                        },
303                    )?
304                    .into_id()
305                })
306        })
307    }
308
309    /// Execute and return the id at zero-based ordinal `nth` when rows are
310    /// ordered by `field` ascending, with primary-key ascending tie-breaks.
311    pub fn nth_by(&self, field: impl AsRef<str>, nth: usize) -> Result<Option<Id<E>>, QueryError>
312    where
313        E: EntityValue,
314    {
315        self.ensure_non_paged_mode_ready()?;
316
317        Self::with_slot(field, |target_slot| {
318            self.session
319                .execute_load_query_with(self.query(), move |load, plan| {
320                    load.execute_scalar_terminal_request(
321                        plan,
322                        ScalarTerminalBoundaryRequest::NthBySlot {
323                            target_field: target_slot,
324                            nth,
325                        },
326                    )?
327                    .into_id()
328                })
329        })
330    }
331
332    /// Execute and return the sum of `field` over matching rows.
333    pub fn sum_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
334    where
335        E: EntityValue,
336    {
337        self.ensure_non_paged_mode_ready()?;
338
339        Self::with_slot(field, |target_slot| {
340            self.session
341                .execute_load_query_with(self.query(), move |load, plan| {
342                    load.execute_numeric_field_boundary(
343                        plan,
344                        target_slot,
345                        ScalarNumericFieldBoundaryRequest::Sum,
346                    )
347                })
348        })
349    }
350
351    /// Execute and return the sum of distinct `field` values.
352    pub fn sum_distinct_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
353    where
354        E: EntityValue,
355    {
356        self.ensure_non_paged_mode_ready()?;
357
358        Self::with_slot(field, |target_slot| {
359            self.session
360                .execute_load_query_with(self.query(), move |load, plan| {
361                    load.execute_numeric_field_boundary(
362                        plan,
363                        target_slot,
364                        ScalarNumericFieldBoundaryRequest::SumDistinct,
365                    )
366                })
367        })
368    }
369
370    /// Execute and return the average of `field` over matching rows.
371    pub fn avg_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
372    where
373        E: EntityValue,
374    {
375        self.ensure_non_paged_mode_ready()?;
376
377        Self::with_slot(field, |target_slot| {
378            self.session
379                .execute_load_query_with(self.query(), move |load, plan| {
380                    load.execute_numeric_field_boundary(
381                        plan,
382                        target_slot,
383                        ScalarNumericFieldBoundaryRequest::Avg,
384                    )
385                })
386        })
387    }
388
389    /// Execute and return the average of distinct `field` values.
390    pub fn avg_distinct_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
391    where
392        E: EntityValue,
393    {
394        self.ensure_non_paged_mode_ready()?;
395
396        Self::with_slot(field, |target_slot| {
397            self.session
398                .execute_load_query_with(self.query(), move |load, plan| {
399                    load.execute_numeric_field_boundary(
400                        plan,
401                        target_slot,
402                        ScalarNumericFieldBoundaryRequest::AvgDistinct,
403                    )
404                })
405        })
406    }
407
408    /// Execute and return the median id by `field` using deterministic ordering
409    /// `(field asc, primary key asc)`.
410    ///
411    /// Even-length windows select the lower median.
412    pub fn median_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
413    where
414        E: EntityValue,
415    {
416        self.ensure_non_paged_mode_ready()?;
417
418        Self::with_slot(field, |target_slot| {
419            self.session
420                .execute_load_query_with(self.query(), move |load, plan| {
421                    load.execute_scalar_terminal_request(
422                        plan,
423                        ScalarTerminalBoundaryRequest::MedianBySlot {
424                            target_field: target_slot,
425                        },
426                    )?
427                    .into_id()
428                })
429        })
430    }
431
432    /// Execute and return the number of distinct values for `field` over the
433    /// effective result window.
434    pub fn count_distinct_by(&self, field: impl AsRef<str>) -> Result<u32, QueryError>
435    where
436        E: EntityValue,
437    {
438        self.ensure_non_paged_mode_ready()?;
439
440        Self::with_slot(field, |target_slot| {
441            self.session
442                .execute_load_query_with(self.query(), move |load, plan| {
443                    load.execute_scalar_projection_boundary(
444                        plan,
445                        target_slot,
446                        ScalarProjectionBoundaryRequest::CountDistinct,
447                    )?
448                    .into_count()
449                })
450        })
451    }
452
453    /// Execute and return both `(min_by(field), max_by(field))` in one terminal.
454    ///
455    /// Tie handling is deterministic for both extrema: primary key ascending.
456    pub fn min_max_by(&self, field: impl AsRef<str>) -> Result<MinMaxByIds<E>, QueryError>
457    where
458        E: EntityValue,
459    {
460        self.ensure_non_paged_mode_ready()?;
461
462        Self::with_slot(field, |target_slot| {
463            self.session
464                .execute_load_query_with(self.query(), move |load, plan| {
465                    load.execute_scalar_terminal_request(
466                        plan,
467                        ScalarTerminalBoundaryRequest::MinMaxBySlot {
468                            target_field: target_slot,
469                        },
470                    )?
471                    .into_id_pair()
472                })
473        })
474    }
475
476    /// Execute and return projected field values for the effective result window.
477    pub fn values_by(&self, field: impl AsRef<str>) -> Result<Vec<Value>, QueryError>
478    where
479        E: EntityValue,
480    {
481        self.ensure_non_paged_mode_ready()?;
482
483        Self::with_slot(field, |target_slot| {
484            self.session
485                .execute_load_query_with(self.query(), move |load, plan| {
486                    load.execute_scalar_projection_boundary(
487                        plan,
488                        target_slot,
489                        ScalarProjectionBoundaryRequest::Values,
490                    )?
491                    .into_values()
492                })
493        })
494    }
495
496    /// Execute and return the first `k` rows from the effective response window.
497    pub fn take(&self, take_count: u32) -> Result<EntityResponse<E>, QueryError>
498    where
499        E: EntityValue,
500    {
501        self.execute_scalar_non_paged_terminal(|load, plan| load.take(plan, take_count))
502    }
503
504    /// Execute and return the top `k` rows by `field` under deterministic
505    /// ordering `(field desc, primary_key asc)` over the effective response
506    /// window.
507    ///
508    /// This terminal applies its own ordering and does not preserve query
509    /// `order_by(...)` row order in the returned rows. For `k = 1`, this
510    /// matches `max_by(field)` selection semantics.
511    pub fn top_k_by(
512        &self,
513        field: impl AsRef<str>,
514        take_count: u32,
515    ) -> Result<EntityResponse<E>, QueryError>
516    where
517        E: EntityValue,
518    {
519        self.ensure_non_paged_mode_ready()?;
520
521        Self::with_slot(field, |target_slot| {
522            self.session
523                .execute_load_query_with(self.query(), move |load, plan| {
524                    load.top_k_by_slot(plan, target_slot, take_count)
525                })
526        })
527    }
528
529    /// Execute and return the bottom `k` rows by `field` under deterministic
530    /// ordering `(field asc, primary_key asc)` over the effective response
531    /// window.
532    ///
533    /// This terminal applies its own ordering and does not preserve query
534    /// `order_by(...)` row order in the returned rows. For `k = 1`, this
535    /// matches `min_by(field)` selection semantics.
536    pub fn bottom_k_by(
537        &self,
538        field: impl AsRef<str>,
539        take_count: u32,
540    ) -> Result<EntityResponse<E>, QueryError>
541    where
542        E: EntityValue,
543    {
544        self.ensure_non_paged_mode_ready()?;
545
546        Self::with_slot(field, |target_slot| {
547            self.session
548                .execute_load_query_with(self.query(), move |load, plan| {
549                    load.bottom_k_by_slot(plan, target_slot, take_count)
550                })
551        })
552    }
553
554    /// Execute and return projected values for the top `k` rows by `field`
555    /// under deterministic ordering `(field desc, primary_key asc)` over the
556    /// effective response window.
557    ///
558    /// Ranking is applied before projection and does not preserve query
559    /// `order_by(...)` row order in the returned values. For `k = 1`, this
560    /// matches `max_by(field)` projected to one value.
561    pub fn top_k_by_values(
562        &self,
563        field: impl AsRef<str>,
564        take_count: u32,
565    ) -> Result<Vec<Value>, QueryError>
566    where
567        E: EntityValue,
568    {
569        self.ensure_non_paged_mode_ready()?;
570
571        Self::with_slot(field, |target_slot| {
572            self.session
573                .execute_load_query_with(self.query(), move |load, plan| {
574                    load.top_k_by_values_slot(plan, target_slot, take_count)
575                })
576        })
577    }
578
579    /// Execute and return projected values for the bottom `k` rows by `field`
580    /// under deterministic ordering `(field asc, primary_key asc)` over the
581    /// effective response window.
582    ///
583    /// Ranking is applied before projection and does not preserve query
584    /// `order_by(...)` row order in the returned values. For `k = 1`, this
585    /// matches `min_by(field)` projected to one value.
586    pub fn bottom_k_by_values(
587        &self,
588        field: impl AsRef<str>,
589        take_count: u32,
590    ) -> Result<Vec<Value>, QueryError>
591    where
592        E: EntityValue,
593    {
594        self.ensure_non_paged_mode_ready()?;
595
596        Self::with_slot(field, |target_slot| {
597            self.session
598                .execute_load_query_with(self.query(), move |load, plan| {
599                    load.bottom_k_by_values_slot(plan, target_slot, take_count)
600                })
601        })
602    }
603
604    /// Execute and return projected id/value pairs for the top `k` rows by
605    /// `field` under deterministic ordering `(field desc, primary_key asc)`
606    /// over the effective response window.
607    ///
608    /// Ranking is applied before projection and does not preserve query
609    /// `order_by(...)` row order in the returned values. For `k = 1`, this
610    /// matches `max_by(field)` projected to one `(id, value)` pair.
611    pub fn top_k_by_with_ids(
612        &self,
613        field: impl AsRef<str>,
614        take_count: u32,
615    ) -> Result<Vec<(Id<E>, Value)>, QueryError>
616    where
617        E: EntityValue,
618    {
619        self.ensure_non_paged_mode_ready()?;
620
621        Self::with_slot(field, |target_slot| {
622            self.session
623                .execute_load_query_with(self.query(), move |load, plan| {
624                    load.top_k_by_with_ids_slot(plan, target_slot, take_count)
625                })
626        })
627    }
628
629    /// Execute and return projected id/value pairs for the bottom `k` rows by
630    /// `field` under deterministic ordering `(field asc, primary_key asc)`
631    /// over the effective response window.
632    ///
633    /// Ranking is applied before projection and does not preserve query
634    /// `order_by(...)` row order in the returned values. For `k = 1`, this
635    /// matches `min_by(field)` projected to one `(id, value)` pair.
636    pub fn bottom_k_by_with_ids(
637        &self,
638        field: impl AsRef<str>,
639        take_count: u32,
640    ) -> Result<Vec<(Id<E>, Value)>, QueryError>
641    where
642        E: EntityValue,
643    {
644        self.ensure_non_paged_mode_ready()?;
645
646        Self::with_slot(field, |target_slot| {
647            self.session
648                .execute_load_query_with(self.query(), move |load, plan| {
649                    load.bottom_k_by_with_ids_slot(plan, target_slot, take_count)
650                })
651        })
652    }
653
654    /// Execute and return distinct projected field values for the effective
655    /// result window, preserving first-observed value order.
656    pub fn distinct_values_by(&self, field: impl AsRef<str>) -> Result<Vec<Value>, QueryError>
657    where
658        E: EntityValue,
659    {
660        self.ensure_non_paged_mode_ready()?;
661
662        Self::with_slot(field, |target_slot| {
663            self.session
664                .execute_load_query_with(self.query(), move |load, plan| {
665                    load.execute_scalar_projection_boundary(
666                        plan,
667                        target_slot,
668                        ScalarProjectionBoundaryRequest::DistinctValues,
669                    )?
670                    .into_values()
671                })
672        })
673    }
674
675    /// Execute and return projected field values paired with row ids for the
676    /// effective result window.
677    pub fn values_by_with_ids(
678        &self,
679        field: impl AsRef<str>,
680    ) -> Result<Vec<(Id<E>, Value)>, QueryError>
681    where
682        E: EntityValue,
683    {
684        self.ensure_non_paged_mode_ready()?;
685
686        Self::with_slot(field, |target_slot| {
687            self.session
688                .execute_load_query_with(self.query(), move |load, plan| {
689                    load.execute_scalar_projection_boundary(
690                        plan,
691                        target_slot,
692                        ScalarProjectionBoundaryRequest::ValuesWithIds,
693                    )?
694                    .into_values_with_ids()
695                })
696        })
697    }
698
699    /// Execute and return the first projected field value in effective response
700    /// order, if any.
701    pub fn first_value_by(&self, field: impl AsRef<str>) -> Result<Option<Value>, QueryError>
702    where
703        E: EntityValue,
704    {
705        self.ensure_non_paged_mode_ready()?;
706
707        Self::with_slot(field, |target_slot| {
708            self.session
709                .execute_load_query_with(self.query(), move |load, plan| {
710                    load.execute_scalar_projection_boundary(
711                        plan,
712                        target_slot,
713                        ScalarProjectionBoundaryRequest::TerminalValue {
714                            terminal_kind: AggregateKind::First,
715                        },
716                    )?
717                    .into_terminal_value()
718                })
719        })
720    }
721
722    /// Execute and return the last projected field value in effective response
723    /// order, if any.
724    pub fn last_value_by(&self, field: impl AsRef<str>) -> Result<Option<Value>, QueryError>
725    where
726        E: EntityValue,
727    {
728        self.ensure_non_paged_mode_ready()?;
729
730        Self::with_slot(field, |target_slot| {
731            self.session
732                .execute_load_query_with(self.query(), move |load, plan| {
733                    load.execute_scalar_projection_boundary(
734                        plan,
735                        target_slot,
736                        ScalarProjectionBoundaryRequest::TerminalValue {
737                            terminal_kind: AggregateKind::Last,
738                        },
739                    )?
740                    .into_terminal_value()
741                })
742        })
743    }
744
745    /// Execute and return the first matching identifier in response order, if any.
746    pub fn first(&self) -> Result<Option<Id<E>>, QueryError>
747    where
748        E: EntityValue,
749    {
750        self.execute_scalar_non_paged_terminal(|load, plan| {
751            load.execute_scalar_terminal_request(
752                plan,
753                ScalarTerminalBoundaryRequest::IdTerminal {
754                    kind: AggregateKind::First,
755                },
756            )?
757            .into_id()
758        })
759    }
760
761    /// Explain scalar `first()` routing without executing the terminal.
762    pub fn explain_first(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
763    where
764        E: EntityValue,
765    {
766        self.explain_scalar_non_paged_terminal(first())
767    }
768
769    /// Execute and return the last matching identifier in response order, if any.
770    pub fn last(&self) -> Result<Option<Id<E>>, QueryError>
771    where
772        E: EntityValue,
773    {
774        self.execute_scalar_non_paged_terminal(|load, plan| {
775            load.execute_scalar_terminal_request(
776                plan,
777                ScalarTerminalBoundaryRequest::IdTerminal {
778                    kind: AggregateKind::Last,
779                },
780            )?
781            .into_id()
782        })
783    }
784
785    /// Explain scalar `last()` routing without executing the terminal.
786    pub fn explain_last(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
787    where
788        E: EntityValue,
789    {
790        self.explain_scalar_non_paged_terminal(last())
791    }
792
793    /// Execute and require exactly one matching row.
794    pub fn require_one(&self) -> Result<(), QueryError>
795    where
796        E: EntityValue,
797    {
798        self.execute()?.require_one()?;
799        Ok(())
800    }
801
802    /// Execute and require at least one matching row.
803    pub fn require_some(&self) -> Result<(), QueryError>
804    where
805        E: EntityValue,
806    {
807        self.execute()?.require_some()?;
808        Ok(())
809    }
810}