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//!
6//! Terminal Execution Model
7//!
8//! Fluent terminals are concrete descriptors with a 1:1 mapping to session
9//! execution and explain entrypoints. This module may orchestrate descriptor
10//! construction, non-paged gating, and public output shaping, but it must not
11//! carry terminal-kind enums, transport output enums, or match-based execution
12//! dispatch. Adding a new terminal means adding a new descriptor type and one
13//! direct `TerminalStrategyDriver` implementation for that descriptor.
14
15use std::num::NonZeroU32;
16
17#[cfg(feature = "diagnostics")]
18use crate::db::FluentTerminalExecutionAttribution;
19#[cfg(feature = "diagnostics")]
20use crate::db::QueryExecutionAttribution;
21use crate::db::query::read_intent::ReadIntentKind;
22use crate::{
23    db::{
24        DbSession, PersistedRow, Query,
25        query::{
26            admission::QueryAdmissionPolicy,
27            api::ResponseCardinalityExt,
28            builder::{
29                AvgBySlotTerminal, AvgDistinctBySlotTerminal, CountDistinctBySlotTerminal,
30                CountRowsTerminal, DistinctValuesBySlotTerminal, ExistsRowsTerminal,
31                FirstIdTerminal, FirstValueBySlotTerminal, LastIdTerminal, LastValueBySlotTerminal,
32                MaxIdBySlotTerminal, MaxIdTerminal, MedianIdBySlotTerminal, MinIdBySlotTerminal,
33                MinIdTerminal, MinMaxIdBySlotTerminal, NthIdBySlotTerminal, SumBySlotTerminal,
34                SumDistinctBySlotTerminal, ValueProjectionExpr, ValuesBySlotTerminal,
35                ValuesBySlotWithIdsTerminal,
36            },
37            explain::{ExplainAggregateTerminalPlan, ExplainExecutionNodeDescriptor},
38            fluent::load::{FluentLoadQuery, LoadQueryResult},
39            intent::{IntentError, QueryError},
40            plan::AggregateKind,
41            read_intent::{
42                COMPLETE_SMALL_EXECUTION_LIMIT, COMPLETE_SMALL_MAX_ROWS,
43                PUBLIC_PAGE_MAX_RESPONSE_BYTES,
44            },
45        },
46        response::EntityResponse,
47    },
48    traits::EntityValue,
49    types::{Decimal, Id},
50    value::{OutputValue, Value},
51};
52
53type MinMaxByIds<E> = Option<(Id<E>, Id<E>)>;
54
55///
56/// TerminalStrategyDriver
57///
58/// TerminalStrategyDriver is the fluent terminal wiring adapter between a
59/// query-owned strategy object and the session-owned execution/explain
60/// boundary. Implementations are deliberately thin: they only choose the
61/// matching `DbSession` method for an existing strategy type.
62///
63
64trait TerminalStrategyDriver<E: PersistedRow + EntityValue> {
65    type Output;
66    type ExplainOutput;
67
68    fn execute(
69        self,
70        session: &DbSession<E::Canister>,
71        query: &Query<E>,
72    ) -> Result<Self::Output, QueryError>;
73
74    fn explain(
75        &self,
76        session: &DbSession<E::Canister>,
77        query: &Query<E>,
78    ) -> Result<Self::ExplainOutput, QueryError>;
79}
80
81// Define one aggregate-style terminal driver implementation. The macro keeps
82// terminal descriptors 1:1 with session methods while removing repeated explain
83// wiring that is identical for every aggregate terminal.
84macro_rules! impl_aggregate_terminal_driver {
85    ($terminal:ty, $output:ty, $execute:ident) => {
86        impl<E> TerminalStrategyDriver<E> for $terminal
87        where
88            E: PersistedRow + EntityValue,
89        {
90            type Output = $output;
91            type ExplainOutput = ExplainAggregateTerminalPlan;
92
93            fn execute(
94                self,
95                session: &DbSession<E::Canister>,
96                query: &Query<E>,
97            ) -> Result<Self::Output, QueryError> {
98                session.$execute(query, self)
99            }
100
101            fn explain(
102                &self,
103                session: &DbSession<E::Canister>,
104                query: &Query<E>,
105            ) -> Result<Self::ExplainOutput, QueryError> {
106                session.explain_query_prepared_aggregate_terminal_with_visible_indexes(query, self)
107            }
108        }
109    };
110}
111
112// Define one projection-style terminal driver implementation. Projection
113// terminals share the execution/explain shape but return execution descriptors
114// rather than aggregate-terminal plans.
115macro_rules! impl_projection_terminal_driver {
116    ($terminal:ty, $output:ty, $execute:ident) => {
117        impl<E> TerminalStrategyDriver<E> for $terminal
118        where
119            E: PersistedRow + EntityValue,
120        {
121            type Output = $output;
122            type ExplainOutput = ExplainExecutionNodeDescriptor;
123
124            fn execute(
125                self,
126                session: &DbSession<E::Canister>,
127                query: &Query<E>,
128            ) -> Result<Self::Output, QueryError> {
129                session.$execute(query, self)
130            }
131
132            fn explain(
133                &self,
134                session: &DbSession<E::Canister>,
135                query: &Query<E>,
136            ) -> Result<Self::ExplainOutput, QueryError> {
137                session.explain_query_prepared_projection_terminal_with_visible_indexes(query, self)
138            }
139        }
140    };
141}
142
143impl_aggregate_terminal_driver!(CountRowsTerminal, u32, execute_fluent_count_rows_terminal);
144impl_aggregate_terminal_driver!(
145    ExistsRowsTerminal,
146    bool,
147    execute_fluent_exists_rows_terminal
148);
149impl_aggregate_terminal_driver!(MinIdTerminal, Option<Id<E>>, execute_fluent_min_id_terminal);
150impl_aggregate_terminal_driver!(MaxIdTerminal, Option<Id<E>>, execute_fluent_max_id_terminal);
151impl_aggregate_terminal_driver!(
152    MinIdBySlotTerminal,
153    Option<Id<E>>,
154    execute_fluent_min_id_by_slot
155);
156impl_aggregate_terminal_driver!(
157    MaxIdBySlotTerminal,
158    Option<Id<E>>,
159    execute_fluent_max_id_by_slot
160);
161impl_aggregate_terminal_driver!(
162    SumBySlotTerminal,
163    Option<Decimal>,
164    execute_fluent_sum_by_slot
165);
166impl_aggregate_terminal_driver!(
167    SumDistinctBySlotTerminal,
168    Option<Decimal>,
169    execute_fluent_sum_distinct_by_slot
170);
171impl_aggregate_terminal_driver!(
172    AvgBySlotTerminal,
173    Option<Decimal>,
174    execute_fluent_avg_by_slot
175);
176impl_aggregate_terminal_driver!(
177    AvgDistinctBySlotTerminal,
178    Option<Decimal>,
179    execute_fluent_avg_distinct_by_slot
180);
181impl_aggregate_terminal_driver!(
182    FirstIdTerminal,
183    Option<Id<E>>,
184    execute_fluent_first_id_terminal
185);
186impl_aggregate_terminal_driver!(
187    LastIdTerminal,
188    Option<Id<E>>,
189    execute_fluent_last_id_terminal
190);
191impl_aggregate_terminal_driver!(
192    NthIdBySlotTerminal,
193    Option<Id<E>>,
194    execute_fluent_nth_id_by_slot
195);
196impl_aggregate_terminal_driver!(
197    MedianIdBySlotTerminal,
198    Option<Id<E>>,
199    execute_fluent_median_id_by_slot
200);
201impl_aggregate_terminal_driver!(
202    MinMaxIdBySlotTerminal,
203    MinMaxByIds<E>,
204    execute_fluent_min_max_id_by_slot
205);
206
207impl_projection_terminal_driver!(
208    ValuesBySlotTerminal,
209    Vec<Value>,
210    execute_fluent_values_by_slot
211);
212impl_projection_terminal_driver!(
213    DistinctValuesBySlotTerminal,
214    Vec<Value>,
215    execute_fluent_distinct_values_by_slot
216);
217impl_projection_terminal_driver!(
218    CountDistinctBySlotTerminal,
219    u32,
220    execute_fluent_count_distinct_by_slot
221);
222impl_projection_terminal_driver!(
223    ValuesBySlotWithIdsTerminal,
224    Vec<(Id<E>, Value)>,
225    execute_fluent_values_by_with_ids_slot
226);
227impl_projection_terminal_driver!(
228    FirstValueBySlotTerminal,
229    Option<Value>,
230    execute_fluent_first_value_by_slot
231);
232impl_projection_terminal_driver!(
233    LastValueBySlotTerminal,
234    Option<Value>,
235    execute_fluent_last_value_by_slot
236);
237
238// Convert one runtime projection value into the public output boundary type.
239fn output(value: Value) -> OutputValue {
240    OutputValue::from(value)
241}
242
243// Convert one ordered runtime projection vector into the public output form.
244fn output_values(values: Vec<Value>) -> Vec<OutputValue> {
245    values.into_iter().map(output).collect()
246}
247
248// Convert one ordered runtime `(id, value)` projection vector into the public output form.
249fn output_values_with_ids<E: PersistedRow>(
250    values: Vec<(Id<E>, Value)>,
251) -> Vec<(Id<E>, OutputValue)> {
252    values
253        .into_iter()
254        .map(|(id, value)| (id, output(value)))
255        .collect()
256}
257
258fn non_zero_u32(value: u32) -> Result<NonZeroU32, QueryError> {
259    NonZeroU32::new(value).ok_or_else(QueryError::invariant)
260}
261
262fn collect_complete_entities<E>(response: EntityResponse<E>) -> Result<Vec<E>, QueryError>
263where
264    E: PersistedRow,
265{
266    if response.count() > COMPLETE_SMALL_MAX_ROWS {
267        return Err(QueryError::intent(
268            IntentError::complete_read_too_many_rows(),
269        ));
270    }
271
272    Ok(response.entities())
273}
274
275impl<E> FluentLoadQuery<'_, E>
276where
277    E: PersistedRow,
278{
279    // ------------------------------------------------------------------
280    // Execution (single semantic boundary)
281    // ------------------------------------------------------------------
282
283    /// Execute this query using the session's policy settings.
284    pub fn execute(&self) -> Result<LoadQueryResult<E>, QueryError>
285    where
286        E: EntityValue,
287    {
288        self.with_admitted_non_paged(DbSession::execute_query_result)
289    }
290
291    /// Execute this query through the scalar rows-only session boundary.
292    pub fn execute_rows(&self) -> Result<EntityResponse<E>, QueryError>
293    where
294        E: EntityValue,
295    {
296        self.with_admitted_non_paged(DbSession::execute_scalar_query_rows)
297    }
298
299    fn with_admitted_non_paged<T>(
300        &self,
301        map: impl FnOnce(&DbSession<E::Canister>, &Query<E>) -> Result<T, QueryError>,
302    ) -> Result<T, QueryError>
303    where
304        E: EntityValue,
305    {
306        self.ensure_non_paged_mode_ready()?;
307        self.ensure_default_read_admission()?;
308        map(self.session, self.query())
309    }
310
311    // Run one terminal operation through the canonical non-paged fluent policy
312    // gate so execution and explain helpers cannot drift on readiness checks.
313    fn with_non_paged<T>(
314        &self,
315        map: impl FnOnce(&DbSession<E::Canister>, &Query<E>) -> Result<T, QueryError>,
316    ) -> Result<T, QueryError>
317    where
318        E: EntityValue,
319    {
320        self.ensure_non_paged_mode_ready()?;
321        map(self.session, self.query())
322    }
323
324    // Resolve the structural execution descriptor for this fluent load query
325    // through the session-owned visible-index explain path once.
326    fn explain_execution_descriptor(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
327    where
328        E: EntityValue,
329    {
330        self.with_non_paged(DbSession::explain_query_execution_with_visible_indexes)
331    }
332
333    // Render one descriptor-derived execution surface so text/json explain
334    // terminals do not each forward the same session explain call ad hoc.
335    fn render_execution_descriptor(
336        &self,
337        render: impl FnOnce(ExplainExecutionNodeDescriptor) -> String,
338    ) -> Result<String, QueryError>
339    where
340        E: EntityValue,
341    {
342        let descriptor = self.explain_execution_descriptor()?;
343
344        Ok(render(descriptor))
345    }
346
347    // Execute one prepared terminal descriptor through the canonical
348    // non-paged fluent policy gate.
349    fn execute_terminal<S>(&self, strategy: S) -> Result<S::Output, QueryError>
350    where
351        E: EntityValue,
352        S: TerminalStrategyDriver<E>,
353    {
354        self.with_admitted_non_paged(|session, query| strategy.execute(session, query))
355    }
356
357    // Explain one prepared terminal strategy through the same non-paged fluent
358    // policy gate used by execution.
359    fn explain_terminal<S>(&self, strategy: &S) -> Result<S::ExplainOutput, QueryError>
360    where
361        E: EntityValue,
362        S: TerminalStrategyDriver<E>,
363    {
364        self.with_non_paged(|session, query| strategy.explain(session, query))
365    }
366
367    fn explain_exact_aggregate_terminal<S>(
368        &self,
369        strategy: &S,
370    ) -> Result<ExplainAggregateTerminalPlan, QueryError>
371    where
372        E: EntityValue,
373        S: TerminalStrategyDriver<E, ExplainOutput = ExplainAggregateTerminalPlan>,
374    {
375        self.explain_terminal(strategy)
376            .map(|plan| plan.with_read_intent(ReadIntentKind::ExactAggregate))
377    }
378
379    fn ensure_exists_intent_owns_limit(&self) -> Result<(), QueryError> {
380        self.ensure_semantic_terminal_owns_limit(IntentError::raw_limit_before_exists_terminal())
381    }
382
383    fn ensure_count_exact_intent_owns_limit(&self) -> Result<(), QueryError> {
384        self.ensure_exact_aggregate_intent_owns_limit(
385            IntentError::raw_limit_before_count_exact_terminal(),
386        )
387    }
388
389    fn ensure_sum_exact_intent_owns_limit(&self) -> Result<(), QueryError> {
390        self.ensure_exact_aggregate_intent_owns_limit(
391            IntentError::raw_limit_before_sum_exact_terminal(),
392        )
393    }
394
395    fn ensure_min_exact_intent_owns_limit(&self) -> Result<(), QueryError> {
396        self.ensure_exact_aggregate_intent_owns_limit(
397            IntentError::raw_limit_before_min_exact_terminal(),
398        )
399    }
400
401    fn ensure_max_exact_intent_owns_limit(&self) -> Result<(), QueryError> {
402        self.ensure_exact_aggregate_intent_owns_limit(
403            IntentError::raw_limit_before_max_exact_terminal(),
404        )
405    }
406
407    fn ensure_avg_exact_intent_owns_limit(&self) -> Result<(), QueryError> {
408        self.ensure_exact_aggregate_intent_owns_limit(
409            IntentError::raw_limit_before_avg_exact_terminal(),
410        )
411    }
412
413    fn ensure_exact_aggregate_intent_owns_limit(&self, err: IntentError) -> Result<(), QueryError> {
414        self.ensure_semantic_terminal_owns_limit(err)
415    }
416
417    fn ensure_collect_complete_intent_owns_limit(&self) -> Result<(), QueryError> {
418        self.ensure_semantic_terminal_owns_limit(
419            IntentError::raw_limit_before_collect_complete_terminal(),
420        )
421    }
422
423    // ------------------------------------------------------------------
424    // Execution terminals — semantic only
425    // ------------------------------------------------------------------
426
427    /// Execute and return whether the result set is empty.
428    pub fn is_empty(&self) -> Result<bool, QueryError>
429    where
430        E: EntityValue,
431    {
432        self.not_exists()
433    }
434
435    /// Execute and return whether no matching row exists.
436    pub fn not_exists(&self) -> Result<bool, QueryError>
437    where
438        E: EntityValue,
439    {
440        Ok(!self.exists()?)
441    }
442
443    /// Execute and return whether at least one matching row exists.
444    pub fn exists(&self) -> Result<bool, QueryError>
445    where
446        E: EntityValue,
447    {
448        self.ensure_exists_intent_owns_limit()?;
449
450        self.execute_terminal(ExistsRowsTerminal::new())
451    }
452
453    /// Execute and return whether at least one matching row exists with
454    /// terminal attribution.
455    #[cfg(feature = "diagnostics")]
456    #[doc(hidden)]
457    pub fn exists_with_attribution(
458        &self,
459    ) -> Result<(bool, FluentTerminalExecutionAttribution), QueryError>
460    where
461        E: EntityValue,
462    {
463        self.ensure_exists_intent_owns_limit()?;
464
465        let (exists, attribution) = self.with_admitted_non_paged(|session, query| {
466            session.execute_fluent_exists_rows_terminal_with_attribution(
467                query,
468                ExistsRowsTerminal::new(),
469            )
470        })?;
471
472        Ok((
473            exists,
474            attribution.with_read_intent(ReadIntentKind::ExistenceCheck),
475        ))
476    }
477
478    /// Explain scalar `exists()` routing without executing the terminal.
479    pub fn explain_exists(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
480    where
481        E: EntityValue,
482    {
483        self.ensure_exists_intent_owns_limit()?;
484
485        self.explain_terminal(&ExistsRowsTerminal::new())
486            .map(|plan| plan.with_read_intent(ReadIntentKind::ExistenceCheck))
487    }
488
489    /// Execute and return all matching rows if the complete result fits in
490    /// the default public-read small-set cap.
491    ///
492    /// This semantic terminal rejects a prior row-window cap. It owns an
493    /// internal lookahead limit so it can distinguish a complete small set
494    /// from a silently truncated row window.
495    pub fn collect_complete(&self) -> Result<Vec<E>, QueryError>
496    where
497        E: EntityValue,
498    {
499        self.ensure_collect_complete_intent_owns_limit()?;
500        self.ensure_non_paged_mode_ready()?;
501
502        let query = self.query().with_load_limit(COMPLETE_SMALL_EXECUTION_LIMIT);
503        let policy = QueryAdmissionPolicy::public_read(
504            non_zero_u32(COMPLETE_SMALL_EXECUTION_LIMIT)?,
505            non_zero_u32(PUBLIC_PAGE_MAX_RESPONSE_BYTES)?,
506        );
507
508        self.session
509            .ensure_query_read_admission_policy(&query, &policy)?;
510
511        let response = self.session.execute_scalar_query_rows(&query)?;
512
513        collect_complete_entities(response)
514    }
515
516    /// Execute and return all matching rows with query diagnostics attribution
517    /// if the complete result fits in the default public-read small-set cap.
518    #[cfg(feature = "diagnostics")]
519    #[doc(hidden)]
520    pub fn collect_complete_with_attribution(
521        &self,
522    ) -> Result<(Vec<E>, QueryExecutionAttribution), QueryError>
523    where
524        E: EntityValue,
525    {
526        self.ensure_collect_complete_intent_owns_limit()?;
527        self.ensure_non_paged_mode_ready()?;
528
529        let query = self.query().with_load_limit(COMPLETE_SMALL_EXECUTION_LIMIT);
530        let policy = QueryAdmissionPolicy::public_read(
531            non_zero_u32(COMPLETE_SMALL_EXECUTION_LIMIT)?,
532            non_zero_u32(PUBLIC_PAGE_MAX_RESPONSE_BYTES)?,
533        );
534
535        self.session
536            .ensure_query_read_admission_policy(&query, &policy)?;
537
538        let (result, attribution) = self.session.execute_query_result_with_attribution(&query)?;
539        let response = result.into_rows()?;
540        let entities = collect_complete_entities(response)?;
541
542        Ok((
543            entities,
544            attribution.with_read_intent(ReadIntentKind::CompleteSmallSet),
545        ))
546    }
547
548    /// Explain scalar `not_exists()` routing without executing the terminal.
549    ///
550    /// This remains an `exists()` execution plan with negated boolean semantics.
551    pub fn explain_not_exists(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
552    where
553        E: EntityValue,
554    {
555        self.explain_exists()
556    }
557
558    /// Explain scalar load execution shape without executing the query.
559    pub fn explain_execution(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
560    where
561        E: EntityValue,
562    {
563        self.explain_execution_descriptor()
564    }
565
566    /// Explain scalar load execution shape as deterministic text.
567    pub fn explain_execution_text(&self) -> Result<String, QueryError>
568    where
569        E: EntityValue,
570    {
571        self.render_execution_descriptor(|descriptor| descriptor.render_text_tree())
572    }
573
574    /// Explain scalar load execution shape as canonical JSON.
575    pub fn explain_execution_json(&self) -> Result<String, QueryError>
576    where
577        E: EntityValue,
578    {
579        self.with_non_paged(DbSession::explain_query_execution_json_with_visible_indexes)
580    }
581
582    /// Explain scalar load execution shape as verbose text with diagnostics.
583    pub fn explain_execution_verbose(&self) -> Result<String, QueryError>
584    where
585        E: EntityValue,
586    {
587        self.with_non_paged(DbSession::explain_query_execution_verbose_with_visible_indexes)
588    }
589
590    /// Execute and return the number of matching rows.
591    pub fn count(&self) -> Result<u32, QueryError>
592    where
593        E: EntityValue,
594    {
595        self.execute_terminal(CountRowsTerminal::new())
596    }
597
598    /// Execute and return the exact number of matching rows.
599    ///
600    /// Unlike `count()`, this semantic aggregate rejects a prior raw
601    /// `limit(...)` so exact counts cannot accidentally mean "count the first
602    /// N rows."
603    pub fn count_exact(&self) -> Result<u32, QueryError>
604    where
605        E: EntityValue,
606    {
607        self.ensure_count_exact_intent_owns_limit()?;
608
609        self.execute_terminal(CountRowsTerminal::new())
610    }
611
612    /// Explain exact count routing without executing the terminal.
613    pub fn explain_count_exact(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
614    where
615        E: EntityValue,
616    {
617        self.ensure_count_exact_intent_owns_limit()?;
618
619        self.explain_exact_aggregate_terminal(&CountRowsTerminal::new())
620    }
621
622    /// Execute and return the number of matching rows with terminal attribution.
623    #[cfg(feature = "diagnostics")]
624    #[doc(hidden)]
625    pub fn count_with_attribution(
626        &self,
627    ) -> Result<(u32, FluentTerminalExecutionAttribution), QueryError>
628    where
629        E: EntityValue,
630    {
631        let (count, attribution) = self.with_admitted_non_paged(|session, query| {
632            session.execute_fluent_count_rows_terminal_with_attribution(
633                query,
634                CountRowsTerminal::new(),
635            )
636        })?;
637
638        Ok((
639            count,
640            attribution.with_read_intent(ReadIntentKind::BoundedRowWindow),
641        ))
642    }
643
644    /// Execute and return the exact number of matching rows with terminal attribution.
645    #[cfg(feature = "diagnostics")]
646    #[doc(hidden)]
647    pub fn count_exact_with_attribution(
648        &self,
649    ) -> Result<(u32, FluentTerminalExecutionAttribution), QueryError>
650    where
651        E: EntityValue,
652    {
653        self.ensure_count_exact_intent_owns_limit()?;
654
655        let (count, attribution) = self.with_admitted_non_paged(|session, query| {
656            session.execute_fluent_count_rows_terminal_with_attribution(
657                query,
658                CountRowsTerminal::new(),
659            )
660        })?;
661
662        Ok((
663            count,
664            attribution.with_read_intent(ReadIntentKind::ExactAggregate),
665        ))
666    }
667
668    /// Execute and return the total persisted payload bytes for the effective
669    /// result window.
670    pub fn bytes(&self) -> Result<u64, QueryError>
671    where
672        E: EntityValue,
673    {
674        self.with_admitted_non_paged(DbSession::execute_fluent_bytes)
675    }
676
677    /// Execute and return the total serialized bytes for `field` over the
678    /// effective result window.
679    pub fn bytes_by(&self, field: impl AsRef<str>) -> Result<u64, QueryError>
680    where
681        E: EntityValue,
682    {
683        let target_slot = self.resolve_non_paged_slot(field)?;
684
685        self.with_admitted_non_paged(|session, query| {
686            session.execute_fluent_bytes_by_slot(query, target_slot)
687        })
688    }
689
690    /// Explain `bytes_by(field)` routing without executing the terminal.
691    pub fn explain_bytes_by(
692        &self,
693        field: impl AsRef<str>,
694    ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
695    where
696        E: EntityValue,
697    {
698        let target_slot = self.resolve_non_paged_slot(field)?;
699
700        self.with_non_paged(|session, query| {
701            session.explain_query_bytes_by_with_visible_indexes(query, target_slot.field())
702        })
703    }
704
705    /// Execute and return the smallest matching identifier, if any.
706    pub fn min(&self) -> Result<Option<Id<E>>, QueryError>
707    where
708        E: EntityValue,
709    {
710        self.execute_terminal(MinIdTerminal::new())
711    }
712
713    /// Execute and return the exact smallest matching identifier.
714    ///
715    /// Unlike `min()`, this semantic aggregate rejects a prior raw
716    /// `limit(...)` so exact minimum selection cannot accidentally mean
717    /// "minimum over the first N rows."
718    pub fn min_id_exact(&self) -> Result<Option<Id<E>>, QueryError>
719    where
720        E: EntityValue,
721    {
722        self.ensure_min_exact_intent_owns_limit()?;
723
724        self.execute_terminal(MinIdTerminal::new())
725    }
726
727    /// Explain scalar `min()` routing without executing the terminal.
728    pub fn explain_min(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
729    where
730        E: EntityValue,
731    {
732        self.explain_terminal(&MinIdTerminal::new())
733    }
734
735    /// Explain exact `min_id_exact()` routing without executing the terminal.
736    pub fn explain_min_id_exact(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
737    where
738        E: EntityValue,
739    {
740        self.ensure_min_exact_intent_owns_limit()?;
741
742        self.explain_exact_aggregate_terminal(&MinIdTerminal::new())
743    }
744
745    /// Execute and return the id of the row with the smallest value for `field`.
746    ///
747    /// Ties are deterministic: equal field values resolve by primary key ascending.
748    pub fn min_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
749    where
750        E: EntityValue,
751    {
752        let target_slot = self.resolve_non_paged_slot(field)?;
753
754        self.execute_terminal(MinIdBySlotTerminal::new(target_slot))
755    }
756
757    /// Execute and return the id of the row with the exact minimum `field` value.
758    ///
759    /// Ties are deterministic: equal field values resolve by primary key ascending.
760    /// A prior row-window cap is rejected because the terminal owns the exact
761    /// aggregate intent.
762    pub fn min_exact_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
763    where
764        E: EntityValue,
765    {
766        self.ensure_min_exact_intent_owns_limit()?;
767
768        let target_slot = self.resolve_non_paged_slot(field)?;
769
770        self.execute_terminal(MinIdBySlotTerminal::new(target_slot))
771    }
772
773    /// Explain exact `min_exact_by(field)` routing without executing the terminal.
774    pub fn explain_min_exact_by(
775        &self,
776        field: impl AsRef<str>,
777    ) -> Result<ExplainAggregateTerminalPlan, QueryError>
778    where
779        E: EntityValue,
780    {
781        self.ensure_min_exact_intent_owns_limit()?;
782
783        let target_slot = self.resolve_non_paged_slot(field)?;
784
785        self.explain_exact_aggregate_terminal(&MinIdBySlotTerminal::new(target_slot))
786    }
787
788    /// Execute and return the largest matching identifier, if any.
789    pub fn max(&self) -> Result<Option<Id<E>>, QueryError>
790    where
791        E: EntityValue,
792    {
793        self.execute_terminal(MaxIdTerminal::new())
794    }
795
796    /// Execute and return the exact largest matching identifier.
797    ///
798    /// Unlike `max()`, this semantic aggregate rejects a prior row-window cap
799    /// so exact maximum selection cannot accidentally mean
800    /// "maximum over the first N rows."
801    pub fn max_id_exact(&self) -> Result<Option<Id<E>>, QueryError>
802    where
803        E: EntityValue,
804    {
805        self.ensure_max_exact_intent_owns_limit()?;
806
807        self.execute_terminal(MaxIdTerminal::new())
808    }
809
810    /// Explain scalar `max()` routing without executing the terminal.
811    pub fn explain_max(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
812    where
813        E: EntityValue,
814    {
815        self.explain_terminal(&MaxIdTerminal::new())
816    }
817
818    /// Explain exact `max_id_exact()` routing without executing the terminal.
819    pub fn explain_max_id_exact(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
820    where
821        E: EntityValue,
822    {
823        self.ensure_max_exact_intent_owns_limit()?;
824
825        self.explain_exact_aggregate_terminal(&MaxIdTerminal::new())
826    }
827
828    /// Execute and return the id of the row with the largest value for `field`.
829    ///
830    /// Ties are deterministic: equal field values resolve by primary key ascending.
831    pub fn max_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
832    where
833        E: EntityValue,
834    {
835        let target_slot = self.resolve_non_paged_slot(field)?;
836
837        self.execute_terminal(MaxIdBySlotTerminal::new(target_slot))
838    }
839
840    /// Execute and return the id of the row with the exact maximum `field` value.
841    ///
842    /// Ties are deterministic: equal field values resolve by primary key ascending.
843    /// A prior row-window cap is rejected because the terminal owns the exact
844    /// aggregate intent.
845    pub fn max_exact_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
846    where
847        E: EntityValue,
848    {
849        self.ensure_max_exact_intent_owns_limit()?;
850
851        let target_slot = self.resolve_non_paged_slot(field)?;
852
853        self.execute_terminal(MaxIdBySlotTerminal::new(target_slot))
854    }
855
856    /// Explain exact `max_exact_by(field)` routing without executing the terminal.
857    pub fn explain_max_exact_by(
858        &self,
859        field: impl AsRef<str>,
860    ) -> Result<ExplainAggregateTerminalPlan, QueryError>
861    where
862        E: EntityValue,
863    {
864        self.ensure_max_exact_intent_owns_limit()?;
865
866        let target_slot = self.resolve_non_paged_slot(field)?;
867
868        self.explain_exact_aggregate_terminal(&MaxIdBySlotTerminal::new(target_slot))
869    }
870
871    /// Execute and return the id at zero-based ordinal `nth` when rows are
872    /// ordered by `field` ascending, with primary-key ascending tie-breaks.
873    pub fn nth_by(&self, field: impl AsRef<str>, nth: usize) -> Result<Option<Id<E>>, QueryError>
874    where
875        E: EntityValue,
876    {
877        let target_slot = self.resolve_non_paged_slot(field)?;
878
879        self.execute_terminal(NthIdBySlotTerminal::new(target_slot, nth))
880    }
881
882    /// Execute and return the sum of `field` over matching rows.
883    pub fn sum_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
884    where
885        E: EntityValue,
886    {
887        let target_slot = self.resolve_non_paged_slot(field)?;
888
889        self.execute_terminal(SumBySlotTerminal::new(target_slot))
890    }
891
892    /// Execute and return the exact sum of `field` over matching rows.
893    ///
894    /// Unlike `sum_by(...)`, this semantic aggregate rejects a prior raw
895    /// `limit(...)` so exact sums cannot accidentally mean "sum the first N
896    /// rows."
897    pub fn sum_exact(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
898    where
899        E: EntityValue,
900    {
901        self.ensure_sum_exact_intent_owns_limit()?;
902
903        let target_slot = self.resolve_non_paged_slot(field)?;
904
905        self.execute_terminal(SumBySlotTerminal::new(target_slot))
906    }
907
908    /// Explain exact sum routing without executing the terminal.
909    pub fn explain_sum_exact(
910        &self,
911        field: impl AsRef<str>,
912    ) -> Result<ExplainAggregateTerminalPlan, QueryError>
913    where
914        E: EntityValue,
915    {
916        self.ensure_sum_exact_intent_owns_limit()?;
917
918        let target_slot = self.resolve_non_paged_slot(field)?;
919
920        self.explain_exact_aggregate_terminal(&SumBySlotTerminal::new(target_slot))
921    }
922
923    /// Explain scalar `sum_by(field)` routing without executing the terminal.
924    pub fn explain_sum_by(
925        &self,
926        field: impl AsRef<str>,
927    ) -> Result<ExplainAggregateTerminalPlan, QueryError>
928    where
929        E: EntityValue,
930    {
931        let target_slot = self.resolve_non_paged_slot(field)?;
932
933        self.explain_terminal(&SumBySlotTerminal::new(target_slot))
934    }
935
936    /// Execute and return the sum of distinct `field` values.
937    pub fn sum_distinct_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
938    where
939        E: EntityValue,
940    {
941        let target_slot = self.resolve_non_paged_slot(field)?;
942
943        self.execute_terminal(SumDistinctBySlotTerminal::new(target_slot))
944    }
945
946    /// Explain scalar `sum(distinct field)` routing without executing the terminal.
947    pub fn explain_sum_distinct_by(
948        &self,
949        field: impl AsRef<str>,
950    ) -> Result<ExplainAggregateTerminalPlan, QueryError>
951    where
952        E: EntityValue,
953    {
954        let target_slot = self.resolve_non_paged_slot(field)?;
955
956        self.explain_terminal(&SumDistinctBySlotTerminal::new(target_slot))
957    }
958
959    /// Execute and return the average of `field` over matching rows.
960    pub fn avg_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
961    where
962        E: EntityValue,
963    {
964        let target_slot = self.resolve_non_paged_slot(field)?;
965
966        self.execute_terminal(AvgBySlotTerminal::new(target_slot))
967    }
968
969    /// Execute and return the exact average of `field` over matching rows.
970    ///
971    /// Unlike `avg_by(...)`, this semantic aggregate rejects a prior raw
972    /// `limit(...)` so exact averages cannot accidentally mean "average the
973    /// first N rows."
974    pub fn avg_exact(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
975    where
976        E: EntityValue,
977    {
978        self.ensure_avg_exact_intent_owns_limit()?;
979
980        let target_slot = self.resolve_non_paged_slot(field)?;
981
982        self.execute_terminal(AvgBySlotTerminal::new(target_slot))
983    }
984
985    /// Explain scalar `avg_by(field)` routing without executing the terminal.
986    pub fn explain_avg_by(
987        &self,
988        field: impl AsRef<str>,
989    ) -> Result<ExplainAggregateTerminalPlan, QueryError>
990    where
991        E: EntityValue,
992    {
993        let target_slot = self.resolve_non_paged_slot(field)?;
994
995        self.explain_terminal(&AvgBySlotTerminal::new(target_slot))
996    }
997
998    /// Explain exact `avg_exact(field)` routing without executing the terminal.
999    pub fn explain_avg_exact(
1000        &self,
1001        field: impl AsRef<str>,
1002    ) -> Result<ExplainAggregateTerminalPlan, QueryError>
1003    where
1004        E: EntityValue,
1005    {
1006        self.ensure_avg_exact_intent_owns_limit()?;
1007
1008        let target_slot = self.resolve_non_paged_slot(field)?;
1009
1010        self.explain_exact_aggregate_terminal(&AvgBySlotTerminal::new(target_slot))
1011    }
1012
1013    /// Execute and return the average of distinct `field` values.
1014    pub fn avg_distinct_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
1015    where
1016        E: EntityValue,
1017    {
1018        let target_slot = self.resolve_non_paged_slot(field)?;
1019
1020        self.execute_terminal(AvgDistinctBySlotTerminal::new(target_slot))
1021    }
1022
1023    /// Explain scalar `avg(distinct field)` routing without executing the terminal.
1024    pub fn explain_avg_distinct_by(
1025        &self,
1026        field: impl AsRef<str>,
1027    ) -> Result<ExplainAggregateTerminalPlan, QueryError>
1028    where
1029        E: EntityValue,
1030    {
1031        let target_slot = self.resolve_non_paged_slot(field)?;
1032
1033        self.explain_terminal(&AvgDistinctBySlotTerminal::new(target_slot))
1034    }
1035
1036    /// Execute and return the median id by `field` using deterministic ordering
1037    /// `(field asc, primary key asc)`.
1038    ///
1039    /// Even-length windows select the lower median.
1040    pub fn median_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
1041    where
1042        E: EntityValue,
1043    {
1044        let target_slot = self.resolve_non_paged_slot(field)?;
1045
1046        self.execute_terminal(MedianIdBySlotTerminal::new(target_slot))
1047    }
1048
1049    /// Execute and return the number of distinct values for `field` over the
1050    /// effective result window.
1051    pub fn count_distinct_by(&self, field: impl AsRef<str>) -> Result<u32, QueryError>
1052    where
1053        E: EntityValue,
1054    {
1055        let target_slot = self.resolve_non_paged_slot(field)?;
1056
1057        self.execute_terminal(CountDistinctBySlotTerminal::new(target_slot))
1058    }
1059
1060    /// Explain `count_distinct_by(field)` routing without executing the terminal.
1061    pub fn explain_count_distinct_by(
1062        &self,
1063        field: impl AsRef<str>,
1064    ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1065    where
1066        E: EntityValue,
1067    {
1068        let target_slot = self.resolve_non_paged_slot(field)?;
1069
1070        self.explain_terminal(&CountDistinctBySlotTerminal::new(target_slot))
1071    }
1072
1073    /// Execute and return both `(min_by(field), max_by(field))` in one terminal.
1074    ///
1075    /// Tie handling is deterministic for both extrema: primary key ascending.
1076    pub fn min_max_by(&self, field: impl AsRef<str>) -> Result<MinMaxByIds<E>, QueryError>
1077    where
1078        E: EntityValue,
1079    {
1080        let target_slot = self.resolve_non_paged_slot(field)?;
1081
1082        self.execute_terminal(MinMaxIdBySlotTerminal::new(target_slot))
1083    }
1084
1085    /// Execute and return projected field values for the effective result window.
1086    pub fn values_by(&self, field: impl AsRef<str>) -> Result<Vec<OutputValue>, QueryError>
1087    where
1088        E: EntityValue,
1089    {
1090        let target_slot = self.resolve_non_paged_slot(field)?;
1091
1092        self.execute_terminal(ValuesBySlotTerminal::new(target_slot))
1093            .map(output_values)
1094    }
1095
1096    /// Execute and return projected values for one shared bounded projection
1097    /// over the effective response window.
1098    pub fn project_values<P>(&self, projection: &P) -> Result<Vec<OutputValue>, QueryError>
1099    where
1100        E: EntityValue,
1101        P: ValueProjectionExpr,
1102    {
1103        let target_slot = self.resolve_non_paged_slot(projection.field())?;
1104
1105        self.with_admitted_non_paged(|session, query| {
1106            session.execute_fluent_project_values_by_slot(
1107                query,
1108                target_slot,
1109                projection.projection_plan().into_expr(),
1110            )
1111        })
1112        .map(output_values)
1113    }
1114
1115    /// Explain `project_values(projection)` routing without executing it.
1116    pub fn explain_project_values<P>(
1117        &self,
1118        projection: &P,
1119    ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1120    where
1121        E: EntityValue,
1122        P: ValueProjectionExpr,
1123    {
1124        let target_slot = self.resolve_non_paged_slot(projection.field())?;
1125
1126        self.explain_terminal(&ValuesBySlotTerminal::new(target_slot))
1127    }
1128
1129    /// Explain `values_by(field)` routing without executing the terminal.
1130    pub fn explain_values_by(
1131        &self,
1132        field: impl AsRef<str>,
1133    ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1134    where
1135        E: EntityValue,
1136    {
1137        let target_slot = self.resolve_non_paged_slot(field)?;
1138
1139        self.explain_terminal(&ValuesBySlotTerminal::new(target_slot))
1140    }
1141
1142    /// Execute and return the first `k` rows from the effective response window.
1143    pub fn take(&self, take_count: u32) -> Result<EntityResponse<E>, QueryError>
1144    where
1145        E: EntityValue,
1146    {
1147        self.with_admitted_non_paged(|session, query| {
1148            session.execute_fluent_take(query, take_count)
1149        })
1150    }
1151
1152    /// Execute and return the top `k` rows by `field` under deterministic
1153    /// ordering `(field desc, primary_key asc)` over the effective response
1154    /// window.
1155    ///
1156    /// This terminal applies its own ordering and does not preserve query
1157    /// `order_term(...)` row order in the returned rows. For `k = 1`, this
1158    /// matches `max_by(field)` selection semantics.
1159    pub fn top_k_by(
1160        &self,
1161        field: impl AsRef<str>,
1162        take_count: u32,
1163    ) -> Result<EntityResponse<E>, QueryError>
1164    where
1165        E: EntityValue,
1166    {
1167        let target_slot = self.resolve_non_paged_slot(field)?;
1168
1169        self.with_admitted_non_paged(|session, query| {
1170            session.execute_fluent_top_k_rows_by_slot(query, target_slot, take_count)
1171        })
1172    }
1173
1174    /// Execute and return the bottom `k` rows by `field` under deterministic
1175    /// ordering `(field asc, primary_key asc)` over the effective response
1176    /// window.
1177    ///
1178    /// This terminal applies its own ordering and does not preserve query
1179    /// `order_term(...)` row order in the returned rows. For `k = 1`, this
1180    /// matches `min_by(field)` selection semantics.
1181    pub fn bottom_k_by(
1182        &self,
1183        field: impl AsRef<str>,
1184        take_count: u32,
1185    ) -> Result<EntityResponse<E>, QueryError>
1186    where
1187        E: EntityValue,
1188    {
1189        let target_slot = self.resolve_non_paged_slot(field)?;
1190
1191        self.with_admitted_non_paged(|session, query| {
1192            session.execute_fluent_bottom_k_rows_by_slot(query, target_slot, take_count)
1193        })
1194    }
1195
1196    /// Execute and return projected values for the top `k` rows by `field`
1197    /// under deterministic ordering `(field desc, primary_key asc)` over the
1198    /// effective response window.
1199    ///
1200    /// Ranking is applied before projection and does not preserve query
1201    /// `order_term(...)` row order in the returned values. For `k = 1`, this
1202    /// matches `max_by(field)` projected to one value.
1203    pub fn top_k_by_values(
1204        &self,
1205        field: impl AsRef<str>,
1206        take_count: u32,
1207    ) -> Result<Vec<OutputValue>, QueryError>
1208    where
1209        E: EntityValue,
1210    {
1211        let target_slot = self.resolve_non_paged_slot(field)?;
1212
1213        self.with_admitted_non_paged(|session, query| {
1214            session
1215                .execute_fluent_top_k_values_by_slot(query, target_slot, take_count)
1216                .map(output_values)
1217        })
1218    }
1219
1220    /// Execute and return projected values for the bottom `k` rows by `field`
1221    /// under deterministic ordering `(field asc, primary_key asc)` over the
1222    /// effective response window.
1223    ///
1224    /// Ranking is applied before projection and does not preserve query
1225    /// `order_term(...)` row order in the returned values. For `k = 1`, this
1226    /// matches `min_by(field)` projected to one value.
1227    pub fn bottom_k_by_values(
1228        &self,
1229        field: impl AsRef<str>,
1230        take_count: u32,
1231    ) -> Result<Vec<OutputValue>, QueryError>
1232    where
1233        E: EntityValue,
1234    {
1235        let target_slot = self.resolve_non_paged_slot(field)?;
1236
1237        self.with_admitted_non_paged(|session, query| {
1238            session
1239                .execute_fluent_bottom_k_values_by_slot(query, target_slot, take_count)
1240                .map(output_values)
1241        })
1242    }
1243
1244    /// Execute and return projected id/value pairs for the top `k` rows by
1245    /// `field` under deterministic ordering `(field desc, primary_key asc)`
1246    /// over the effective response window.
1247    ///
1248    /// Ranking is applied before projection and does not preserve query
1249    /// `order_term(...)` row order in the returned values. For `k = 1`, this
1250    /// matches `max_by(field)` projected to one `(id, value)` pair.
1251    pub fn top_k_by_with_ids(
1252        &self,
1253        field: impl AsRef<str>,
1254        take_count: u32,
1255    ) -> Result<Vec<(Id<E>, OutputValue)>, QueryError>
1256    where
1257        E: EntityValue,
1258    {
1259        let target_slot = self.resolve_non_paged_slot(field)?;
1260
1261        self.with_admitted_non_paged(|session, query| {
1262            session
1263                .execute_fluent_top_k_values_with_ids_by_slot(query, target_slot, take_count)
1264                .map(output_values_with_ids)
1265        })
1266    }
1267
1268    /// Execute and return projected id/value pairs for the bottom `k` rows by
1269    /// `field` under deterministic ordering `(field asc, primary_key asc)`
1270    /// over the effective response window.
1271    ///
1272    /// Ranking is applied before projection and does not preserve query
1273    /// `order_term(...)` row order in the returned values. For `k = 1`, this
1274    /// matches `min_by(field)` projected to one `(id, value)` pair.
1275    pub fn bottom_k_by_with_ids(
1276        &self,
1277        field: impl AsRef<str>,
1278        take_count: u32,
1279    ) -> Result<Vec<(Id<E>, OutputValue)>, QueryError>
1280    where
1281        E: EntityValue,
1282    {
1283        let target_slot = self.resolve_non_paged_slot(field)?;
1284
1285        self.with_admitted_non_paged(|session, query| {
1286            session
1287                .execute_fluent_bottom_k_values_with_ids_by_slot(query, target_slot, take_count)
1288                .map(output_values_with_ids)
1289        })
1290    }
1291
1292    /// Execute and return distinct projected field values for the effective
1293    /// result window, preserving first-observed value order.
1294    pub fn distinct_values_by(&self, field: impl AsRef<str>) -> Result<Vec<OutputValue>, QueryError>
1295    where
1296        E: EntityValue,
1297    {
1298        let target_slot = self.resolve_non_paged_slot(field)?;
1299
1300        self.execute_terminal(DistinctValuesBySlotTerminal::new(target_slot))
1301            .map(output_values)
1302    }
1303
1304    /// Explain `distinct_values_by(field)` routing without executing the terminal.
1305    pub fn explain_distinct_values_by(
1306        &self,
1307        field: impl AsRef<str>,
1308    ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1309    where
1310        E: EntityValue,
1311    {
1312        let target_slot = self.resolve_non_paged_slot(field)?;
1313
1314        self.explain_terminal(&DistinctValuesBySlotTerminal::new(target_slot))
1315    }
1316
1317    /// Execute and return projected field values paired with row ids for the
1318    /// effective result window.
1319    pub fn values_by_with_ids(
1320        &self,
1321        field: impl AsRef<str>,
1322    ) -> Result<Vec<(Id<E>, OutputValue)>, QueryError>
1323    where
1324        E: EntityValue,
1325    {
1326        let target_slot = self.resolve_non_paged_slot(field)?;
1327
1328        self.execute_terminal(ValuesBySlotWithIdsTerminal::new(target_slot))
1329            .map(output_values_with_ids)
1330    }
1331
1332    /// Execute and return projected id/value pairs for one shared bounded
1333    /// projection over the effective response window.
1334    pub fn project_values_with_ids<P>(
1335        &self,
1336        projection: &P,
1337    ) -> Result<Vec<(Id<E>, OutputValue)>, QueryError>
1338    where
1339        E: EntityValue,
1340        P: ValueProjectionExpr,
1341    {
1342        let target_slot = self.resolve_non_paged_slot(projection.field())?;
1343
1344        self.with_admitted_non_paged(|session, query| {
1345            session.execute_fluent_project_values_with_ids_by_slot(
1346                query,
1347                target_slot,
1348                projection.projection_plan().into_expr(),
1349            )
1350        })
1351        .map(output_values_with_ids)
1352    }
1353
1354    /// Explain `values_by_with_ids(field)` routing without executing the terminal.
1355    pub fn explain_values_by_with_ids(
1356        &self,
1357        field: impl AsRef<str>,
1358    ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1359    where
1360        E: EntityValue,
1361    {
1362        let target_slot = self.resolve_non_paged_slot(field)?;
1363
1364        self.explain_terminal(&ValuesBySlotWithIdsTerminal::new(target_slot))
1365    }
1366
1367    /// Execute and return the first projected field value in effective response
1368    /// order, if any.
1369    pub fn first_value_by(&self, field: impl AsRef<str>) -> Result<Option<OutputValue>, QueryError>
1370    where
1371        E: EntityValue,
1372    {
1373        let target_slot = self.resolve_non_paged_slot(field)?;
1374
1375        self.execute_terminal(FirstValueBySlotTerminal::new(target_slot))
1376            .map(|value| value.map(output))
1377    }
1378
1379    /// Execute and return the first projected value for one shared bounded
1380    /// projection in effective response order, if any.
1381    pub fn project_first_value<P>(&self, projection: &P) -> Result<Option<OutputValue>, QueryError>
1382    where
1383        E: EntityValue,
1384        P: ValueProjectionExpr,
1385    {
1386        let target_slot = self.resolve_non_paged_slot(projection.field())?;
1387
1388        self.with_admitted_non_paged(|session, query| {
1389            session.execute_fluent_project_terminal_value_by_slot(
1390                query,
1391                target_slot,
1392                AggregateKind::First,
1393                projection.projection_plan().into_expr(),
1394            )
1395        })
1396        .map(|value| value.map(output))
1397    }
1398
1399    /// Explain `first_value_by(field)` routing without executing the terminal.
1400    pub fn explain_first_value_by(
1401        &self,
1402        field: impl AsRef<str>,
1403    ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1404    where
1405        E: EntityValue,
1406    {
1407        let target_slot = self.resolve_non_paged_slot(field)?;
1408
1409        self.explain_terminal(&FirstValueBySlotTerminal::new(target_slot))
1410    }
1411
1412    /// Execute and return the last projected field value in effective response
1413    /// order, if any.
1414    pub fn last_value_by(&self, field: impl AsRef<str>) -> Result<Option<OutputValue>, QueryError>
1415    where
1416        E: EntityValue,
1417    {
1418        let target_slot = self.resolve_non_paged_slot(field)?;
1419
1420        self.execute_terminal(LastValueBySlotTerminal::new(target_slot))
1421            .map(|value| value.map(output))
1422    }
1423
1424    /// Execute and return the last projected value for one shared bounded
1425    /// projection in effective response order, if any.
1426    pub fn project_last_value<P>(&self, projection: &P) -> Result<Option<OutputValue>, QueryError>
1427    where
1428        E: EntityValue,
1429        P: ValueProjectionExpr,
1430    {
1431        let target_slot = self.resolve_non_paged_slot(projection.field())?;
1432
1433        self.with_admitted_non_paged(|session, query| {
1434            session.execute_fluent_project_terminal_value_by_slot(
1435                query,
1436                target_slot,
1437                AggregateKind::Last,
1438                projection.projection_plan().into_expr(),
1439            )
1440        })
1441        .map(|value| value.map(output))
1442    }
1443
1444    /// Explain `last_value_by(field)` routing without executing the terminal.
1445    pub fn explain_last_value_by(
1446        &self,
1447        field: impl AsRef<str>,
1448    ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1449    where
1450        E: EntityValue,
1451    {
1452        let target_slot = self.resolve_non_paged_slot(field)?;
1453
1454        self.explain_terminal(&LastValueBySlotTerminal::new(target_slot))
1455    }
1456
1457    /// Execute and return the first matching identifier in response order, if any.
1458    pub fn first(&self) -> Result<Option<Id<E>>, QueryError>
1459    where
1460        E: EntityValue,
1461    {
1462        self.execute_terminal(FirstIdTerminal::new())
1463    }
1464
1465    /// Explain scalar `first()` routing without executing the terminal.
1466    pub fn explain_first(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
1467    where
1468        E: EntityValue,
1469    {
1470        self.explain_terminal(&FirstIdTerminal::new())
1471    }
1472
1473    /// Execute and return the last matching identifier in response order, if any.
1474    pub fn last(&self) -> Result<Option<Id<E>>, QueryError>
1475    where
1476        E: EntityValue,
1477    {
1478        self.execute_terminal(LastIdTerminal::new())
1479    }
1480
1481    /// Explain scalar `last()` routing without executing the terminal.
1482    pub fn explain_last(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
1483    where
1484        E: EntityValue,
1485    {
1486        self.explain_terminal(&LastIdTerminal::new())
1487    }
1488
1489    /// Execute and require exactly one matching row.
1490    pub fn require_one(&self) -> Result<(), QueryError>
1491    where
1492        E: EntityValue,
1493    {
1494        self.execute_rows()?.require_one()?;
1495        Ok(())
1496    }
1497
1498    /// Execute and require at least one matching row.
1499    pub fn require_some(&self) -> Result<(), QueryError>
1500    where
1501        E: EntityValue,
1502    {
1503        self.execute_rows()?.require_some()?;
1504        Ok(())
1505    }
1506}