use crate::{
db::{
DbSession, PersistedRow, Query,
query::{
api::ResponseCardinalityExt,
builder::{
ExistingRowsTerminalStrategy, NumericFieldStrategy, OrderSensitiveTerminalStrategy,
ProjectionStrategy, ScalarTerminalStrategy, ValueProjectionExpr,
},
explain::{ExplainAggregateTerminalPlan, ExplainExecutionNodeDescriptor},
fluent::load::{
FluentLoadQuery, FluentProjectionTerminalOutput, FluentScalarTerminalOutput,
LoadQueryResult,
},
intent::QueryError,
plan::AggregateKind,
},
response::EntityResponse,
},
error::InternalError,
traits::EntityValue,
types::{Decimal, Id},
value::{OutputValue, Value},
};
type MinMaxByIds<E> = Option<(Id<E>, Id<E>)>;
trait TerminalStrategyDriver<E: PersistedRow + EntityValue> {
type Output;
type ExplainOutput;
fn execute(
&self,
session: &DbSession<E::Canister>,
query: &Query<E>,
) -> Result<Self::Output, QueryError>;
fn explain(
&self,
session: &DbSession<E::Canister>,
query: &Query<E>,
) -> Result<Self::ExplainOutput, QueryError>;
}
impl<E> TerminalStrategyDriver<E> for ExistingRowsTerminalStrategy
where
E: PersistedRow + EntityValue,
{
type Output = FluentScalarTerminalOutput<E>;
type ExplainOutput = ExplainAggregateTerminalPlan;
fn execute(
&self,
session: &DbSession<E::Canister>,
query: &Query<E>,
) -> Result<Self::Output, QueryError> {
session.execute_fluent_existing_rows_terminal(query, self.clone())
}
fn explain(
&self,
session: &DbSession<E::Canister>,
query: &Query<E>,
) -> Result<Self::ExplainOutput, QueryError> {
session.explain_query_prepared_aggregate_terminal_with_visible_indexes(query, self)
}
}
impl<E> TerminalStrategyDriver<E> for ScalarTerminalStrategy
where
E: PersistedRow + EntityValue,
{
type Output = FluentScalarTerminalOutput<E>;
type ExplainOutput = ExplainAggregateTerminalPlan;
fn execute(
&self,
session: &DbSession<E::Canister>,
query: &Query<E>,
) -> Result<Self::Output, QueryError> {
session.execute_fluent_scalar_terminal(query, self.clone())
}
fn explain(
&self,
session: &DbSession<E::Canister>,
query: &Query<E>,
) -> Result<Self::ExplainOutput, QueryError> {
session.explain_query_prepared_aggregate_terminal_with_visible_indexes(query, self)
}
}
impl<E> TerminalStrategyDriver<E> for NumericFieldStrategy
where
E: PersistedRow + EntityValue,
{
type Output = Option<Decimal>;
type ExplainOutput = ExplainAggregateTerminalPlan;
fn execute(
&self,
session: &DbSession<E::Canister>,
query: &Query<E>,
) -> Result<Self::Output, QueryError> {
session.execute_fluent_numeric_field_terminal(query, self.clone())
}
fn explain(
&self,
session: &DbSession<E::Canister>,
query: &Query<E>,
) -> Result<Self::ExplainOutput, QueryError> {
session.explain_query_prepared_aggregate_terminal_with_visible_indexes(query, self)
}
}
impl<E> TerminalStrategyDriver<E> for OrderSensitiveTerminalStrategy
where
E: PersistedRow + EntityValue,
{
type Output = FluentScalarTerminalOutput<E>;
type ExplainOutput = ExplainAggregateTerminalPlan;
fn execute(
&self,
session: &DbSession<E::Canister>,
query: &Query<E>,
) -> Result<Self::Output, QueryError> {
session.execute_fluent_order_sensitive_terminal(query, self.clone())
}
fn explain(
&self,
session: &DbSession<E::Canister>,
query: &Query<E>,
) -> Result<Self::ExplainOutput, QueryError> {
session.explain_query_prepared_aggregate_terminal_with_visible_indexes(query, self)
}
}
impl<E> TerminalStrategyDriver<E> for ProjectionStrategy
where
E: PersistedRow + EntityValue,
{
type Output = FluentProjectionTerminalOutput<E>;
type ExplainOutput = ExplainExecutionNodeDescriptor;
fn execute(
&self,
session: &DbSession<E::Canister>,
query: &Query<E>,
) -> Result<Self::Output, QueryError> {
session.execute_fluent_projection_terminal(query, self.clone())
}
fn explain(
&self,
session: &DbSession<E::Canister>,
query: &Query<E>,
) -> Result<Self::ExplainOutput, QueryError> {
session.explain_query_prepared_projection_terminal_with_visible_indexes(query, self)
}
}
fn output(value: Value) -> OutputValue {
OutputValue::from(value)
}
fn output_values(values: Vec<Value>) -> Vec<OutputValue> {
values.into_iter().map(output).collect()
}
fn output_values_with_ids<E: PersistedRow>(
values: Vec<(Id<E>, Value)>,
) -> Vec<(Id<E>, OutputValue)> {
values
.into_iter()
.map(|(id, value)| (id, output(value)))
.collect()
}
impl<E> FluentLoadQuery<'_, E>
where
E: PersistedRow,
{
pub fn execute(&self) -> Result<LoadQueryResult<E>, QueryError>
where
E: EntityValue,
{
self.with_non_paged(DbSession::execute_query_result)
}
fn with_non_paged<T>(
&self,
map: impl FnOnce(&DbSession<E::Canister>, &Query<E>) -> Result<T, QueryError>,
) -> Result<T, QueryError>
where
E: EntityValue,
{
self.ensure_non_paged_mode_ready()?;
map(self.session, self.query())
}
fn explain_execution_descriptor(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
self.with_non_paged(DbSession::explain_query_execution_with_visible_indexes)
}
fn render_execution_descriptor(
&self,
render: impl FnOnce(ExplainExecutionNodeDescriptor) -> String,
) -> Result<String, QueryError>
where
E: EntityValue,
{
let descriptor = self.explain_execution_descriptor()?;
Ok(render(descriptor))
}
fn execute_terminal<S, T>(
&self,
strategy: S,
map: impl FnOnce(S::Output) -> Result<T, InternalError>,
) -> Result<T, QueryError>
where
E: EntityValue,
S: TerminalStrategyDriver<E>,
{
self.with_non_paged(|session, query| {
let output = strategy.execute(session, query)?;
map(output).map_err(QueryError::execute)
})
}
fn explain_terminal<S>(&self, strategy: &S) -> Result<S::ExplainOutput, QueryError>
where
E: EntityValue,
S: TerminalStrategyDriver<E>,
{
self.with_non_paged(|session, query| strategy.explain(session, query))
}
fn project_terminal_items<P, T, U>(
projection: &P,
values: impl IntoIterator<Item = T>,
mut map: impl FnMut(&P, T) -> Result<U, QueryError>,
) -> Result<Vec<U>, QueryError>
where
P: ValueProjectionExpr,
{
values
.into_iter()
.map(|value| map(projection, value))
.collect()
}
pub fn is_empty(&self) -> Result<bool, QueryError>
where
E: EntityValue,
{
self.not_exists()
}
pub fn not_exists(&self) -> Result<bool, QueryError>
where
E: EntityValue,
{
Ok(!self.exists()?)
}
pub fn exists(&self) -> Result<bool, QueryError>
where
E: EntityValue,
{
self.execute_terminal(
ExistingRowsTerminalStrategy::exists_rows(),
FluentScalarTerminalOutput::into_exists,
)
}
pub fn explain_exists(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.explain_terminal(&ExistingRowsTerminalStrategy::exists_rows())
}
pub fn explain_not_exists(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.explain_exists()
}
pub fn explain_execution(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
self.explain_execution_descriptor()
}
pub fn explain_execution_text(&self) -> Result<String, QueryError>
where
E: EntityValue,
{
self.render_execution_descriptor(|descriptor| descriptor.render_text_tree())
}
pub fn explain_execution_json(&self) -> Result<String, QueryError>
where
E: EntityValue,
{
self.render_execution_descriptor(|descriptor| descriptor.render_json_canonical())
}
pub fn explain_execution_verbose(&self) -> Result<String, QueryError>
where
E: EntityValue,
{
self.with_non_paged(DbSession::explain_query_execution_verbose_with_visible_indexes)
}
pub fn count(&self) -> Result<u32, QueryError>
where
E: EntityValue,
{
self.execute_terminal(
ExistingRowsTerminalStrategy::count_rows(),
FluentScalarTerminalOutput::into_count,
)
}
pub fn bytes(&self) -> Result<u64, QueryError>
where
E: EntityValue,
{
self.with_non_paged(DbSession::execute_fluent_bytes)
}
pub fn bytes_by(&self, field: impl AsRef<str>) -> Result<u64, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.with_non_paged(|session, query| {
session.execute_fluent_bytes_by_slot(query, target_slot)
})
}
pub fn explain_bytes_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.with_non_paged(|session, query| {
session.explain_query_bytes_by_with_visible_indexes(query, target_slot.field())
})
}
pub fn min(&self) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
self.execute_terminal(
ScalarTerminalStrategy::id_terminal(AggregateKind::Min),
FluentScalarTerminalOutput::into_id,
)
}
pub fn explain_min(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.explain_terminal(&ScalarTerminalStrategy::id_terminal(AggregateKind::Min))
}
pub fn min_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(
ScalarTerminalStrategy::id_by_slot(AggregateKind::Min, target_slot),
FluentScalarTerminalOutput::into_id,
)
}
pub fn max(&self) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
self.execute_terminal(
ScalarTerminalStrategy::id_terminal(AggregateKind::Max),
FluentScalarTerminalOutput::into_id,
)
}
pub fn explain_max(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.explain_terminal(&ScalarTerminalStrategy::id_terminal(AggregateKind::Max))
}
pub fn max_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(
ScalarTerminalStrategy::id_by_slot(AggregateKind::Max, target_slot),
FluentScalarTerminalOutput::into_id,
)
}
pub fn nth_by(&self, field: impl AsRef<str>, nth: usize) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(
OrderSensitiveTerminalStrategy::nth_by_slot(target_slot, nth),
FluentScalarTerminalOutput::into_id,
)
}
pub fn sum_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(NumericFieldStrategy::sum_by_slot(target_slot), Ok)
}
pub fn explain_sum_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.explain_terminal(&NumericFieldStrategy::sum_by_slot(target_slot))
}
pub fn sum_distinct_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(NumericFieldStrategy::sum_distinct_by_slot(target_slot), Ok)
}
pub fn explain_sum_distinct_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.explain_terminal(&NumericFieldStrategy::sum_distinct_by_slot(target_slot))
}
pub fn avg_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(NumericFieldStrategy::avg_by_slot(target_slot), Ok)
}
pub fn explain_avg_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.explain_terminal(&NumericFieldStrategy::avg_by_slot(target_slot))
}
pub fn avg_distinct_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(NumericFieldStrategy::avg_distinct_by_slot(target_slot), Ok)
}
pub fn explain_avg_distinct_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.explain_terminal(&NumericFieldStrategy::avg_distinct_by_slot(target_slot))
}
pub fn median_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(
OrderSensitiveTerminalStrategy::median_by_slot(target_slot),
FluentScalarTerminalOutput::into_id,
)
}
pub fn count_distinct_by(&self, field: impl AsRef<str>) -> Result<u32, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(
ProjectionStrategy::count_distinct_by_slot(target_slot),
FluentProjectionTerminalOutput::into_count,
)
}
pub fn explain_count_distinct_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.explain_terminal(&ProjectionStrategy::count_distinct_by_slot(target_slot))
}
pub fn min_max_by(&self, field: impl AsRef<str>) -> Result<MinMaxByIds<E>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(
OrderSensitiveTerminalStrategy::min_max_by_slot(target_slot),
FluentScalarTerminalOutput::into_id_pair,
)
}
pub fn values_by(&self, field: impl AsRef<str>) -> Result<Vec<OutputValue>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(
ProjectionStrategy::values_by_slot(target_slot),
FluentProjectionTerminalOutput::into_values,
)
.map(output_values)
}
pub fn project_values<P>(&self, projection: &P) -> Result<Vec<OutputValue>, QueryError>
where
E: EntityValue,
P: ValueProjectionExpr,
{
let target_slot = self.resolve_non_paged_slot(projection.field())?;
let values = self
.execute_terminal(ProjectionStrategy::values_by_slot(target_slot), Ok)?
.into_values()
.map_err(QueryError::execute)?;
Self::project_terminal_items(projection, values, |projection, value| {
projection.apply_value(value)
})
.map(output_values)
}
pub fn explain_project_values<P>(
&self,
projection: &P,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
P: ValueProjectionExpr,
{
let target_slot = self.resolve_non_paged_slot(projection.field())?;
self.explain_terminal(&ProjectionStrategy::values_by_slot(target_slot))
}
pub fn explain_values_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.explain_terminal(&ProjectionStrategy::values_by_slot(target_slot))
}
pub fn take(&self, take_count: u32) -> Result<EntityResponse<E>, QueryError>
where
E: EntityValue,
{
self.with_non_paged(|session, query| session.execute_fluent_take(query, take_count))
}
pub fn top_k_by(
&self,
field: impl AsRef<str>,
take_count: u32,
) -> Result<EntityResponse<E>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.with_non_paged(|session, query| {
session.execute_fluent_ranked_rows_by_slot(query, target_slot, take_count, true)
})
}
pub fn bottom_k_by(
&self,
field: impl AsRef<str>,
take_count: u32,
) -> Result<EntityResponse<E>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.with_non_paged(|session, query| {
session.execute_fluent_ranked_rows_by_slot(query, target_slot, take_count, false)
})
}
pub fn top_k_by_values(
&self,
field: impl AsRef<str>,
take_count: u32,
) -> Result<Vec<OutputValue>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.with_non_paged(|session, query| {
session
.execute_fluent_ranked_values_by_slot(query, target_slot, take_count, true)
.map(output_values)
})
}
pub fn bottom_k_by_values(
&self,
field: impl AsRef<str>,
take_count: u32,
) -> Result<Vec<OutputValue>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.with_non_paged(|session, query| {
session
.execute_fluent_ranked_values_by_slot(query, target_slot, take_count, false)
.map(output_values)
})
}
pub fn top_k_by_with_ids(
&self,
field: impl AsRef<str>,
take_count: u32,
) -> Result<Vec<(Id<E>, OutputValue)>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.with_non_paged(|session, query| {
session
.execute_fluent_ranked_values_with_ids_by_slot(query, target_slot, take_count, true)
.map(output_values_with_ids)
})
}
pub fn bottom_k_by_with_ids(
&self,
field: impl AsRef<str>,
take_count: u32,
) -> Result<Vec<(Id<E>, OutputValue)>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.with_non_paged(|session, query| {
session
.execute_fluent_ranked_values_with_ids_by_slot(
query,
target_slot,
take_count,
false,
)
.map(output_values_with_ids)
})
}
pub fn distinct_values_by(&self, field: impl AsRef<str>) -> Result<Vec<OutputValue>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(
ProjectionStrategy::distinct_values_by_slot(target_slot),
FluentProjectionTerminalOutput::into_values,
)
.map(output_values)
}
pub fn explain_distinct_values_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.explain_terminal(&ProjectionStrategy::distinct_values_by_slot(target_slot))
}
pub fn values_by_with_ids(
&self,
field: impl AsRef<str>,
) -> Result<Vec<(Id<E>, OutputValue)>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(
ProjectionStrategy::values_by_with_ids_slot(target_slot),
FluentProjectionTerminalOutput::into_values_with_ids,
)
.map(output_values_with_ids)
}
pub fn project_values_with_ids<P>(
&self,
projection: &P,
) -> Result<Vec<(Id<E>, OutputValue)>, QueryError>
where
E: EntityValue,
P: ValueProjectionExpr,
{
let target_slot = self.resolve_non_paged_slot(projection.field())?;
let values = self
.execute_terminal(ProjectionStrategy::values_by_with_ids_slot(target_slot), Ok)?
.into_values_with_ids()
.map_err(QueryError::execute)?;
Self::project_terminal_items(projection, values, |projection, (id, value)| {
Ok((id, projection.apply_value(value)?))
})
.map(output_values_with_ids)
}
pub fn explain_values_by_with_ids(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.explain_terminal(&ProjectionStrategy::values_by_with_ids_slot(target_slot))
}
pub fn first_value_by(&self, field: impl AsRef<str>) -> Result<Option<OutputValue>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(
ProjectionStrategy::first_value_by_slot(target_slot),
FluentProjectionTerminalOutput::into_terminal_value,
)
.map(|value| value.map(output))
}
pub fn project_first_value<P>(&self, projection: &P) -> Result<Option<OutputValue>, QueryError>
where
E: EntityValue,
P: ValueProjectionExpr,
{
let target_slot = self.resolve_non_paged_slot(projection.field())?;
let value = self
.execute_terminal(ProjectionStrategy::first_value_by_slot(target_slot), Ok)?
.into_terminal_value()
.map_err(QueryError::execute)?;
let mut projected =
Self::project_terminal_items(projection, value, |projection, value| {
projection.apply_value(value)
})?;
Ok(projected.pop().map(output))
}
pub fn explain_first_value_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.explain_terminal(&ProjectionStrategy::first_value_by_slot(target_slot))
}
pub fn last_value_by(&self, field: impl AsRef<str>) -> Result<Option<OutputValue>, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.execute_terminal(
ProjectionStrategy::last_value_by_slot(target_slot),
FluentProjectionTerminalOutput::into_terminal_value,
)
.map(|value| value.map(output))
}
pub fn project_last_value<P>(&self, projection: &P) -> Result<Option<OutputValue>, QueryError>
where
E: EntityValue,
P: ValueProjectionExpr,
{
let target_slot = self.resolve_non_paged_slot(projection.field())?;
let value = self
.execute_terminal(ProjectionStrategy::last_value_by_slot(target_slot), Ok)?
.into_terminal_value()
.map_err(QueryError::execute)?;
let mut projected =
Self::project_terminal_items(projection, value, |projection, value| {
projection.apply_value(value)
})?;
Ok(projected.pop().map(output))
}
pub fn explain_last_value_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
let target_slot = self.resolve_non_paged_slot(field)?;
self.explain_terminal(&ProjectionStrategy::last_value_by_slot(target_slot))
}
pub fn first(&self) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
self.execute_terminal(
OrderSensitiveTerminalStrategy::first(),
FluentScalarTerminalOutput::into_id,
)
}
pub fn explain_first(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.explain_terminal(&OrderSensitiveTerminalStrategy::first())
}
pub fn last(&self) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
self.execute_terminal(
OrderSensitiveTerminalStrategy::last(),
FluentScalarTerminalOutput::into_id,
)
}
pub fn explain_last(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.explain_terminal(&OrderSensitiveTerminalStrategy::last())
}
pub fn require_one(&self) -> Result<(), QueryError>
where
E: EntityValue,
{
self.execute()?.into_rows()?.require_one()?;
Ok(())
}
pub fn require_some(&self) -> Result<(), QueryError>
where
E: EntityValue,
{
self.execute()?.into_rows()?.require_some()?;
Ok(())
}
}