use crate::{
db::{
DbSession, PersistedRow, Query,
executor::{
LoadExecutor, PreparedExecutionPlan, ScalarNumericFieldBoundaryRequest,
ScalarProjectionBoundaryRequest, ScalarTerminalBoundaryOutput,
ScalarTerminalBoundaryRequest,
},
query::{
api::ResponseCardinalityExt,
builder::{
PreparedFluentAggregateExplainStrategy,
PreparedFluentExistingRowsTerminalRuntimeRequest,
PreparedFluentExistingRowsTerminalStrategy,
PreparedFluentNumericFieldRuntimeRequest, PreparedFluentNumericFieldStrategy,
PreparedFluentOrderSensitiveTerminalRuntimeRequest,
PreparedFluentOrderSensitiveTerminalStrategy,
PreparedFluentProjectionRuntimeRequest, PreparedFluentProjectionStrategy,
PreparedFluentScalarTerminalRuntimeRequest, PreparedFluentScalarTerminalStrategy,
ValueProjectionExpr,
},
explain::{ExplainAggregateTerminalPlan, ExplainExecutionNodeDescriptor},
fluent::load::{FluentLoadQuery, LoadQueryResult},
intent::QueryError,
plan::AggregateKind,
},
response::EntityResponse,
},
error::InternalError,
traits::EntityValue,
types::{Decimal, Id},
value::Value,
};
type MinMaxByIds<E> = Option<(Id<E>, Id<E>)>;
impl<E> FluentLoadQuery<'_, E>
where
E: PersistedRow,
{
pub fn execute(&self) -> Result<LoadQueryResult<E>, QueryError>
where
E: EntityValue,
{
self.ensure_non_paged_mode_ready()?;
self.session.execute_query_result(self.query())
}
fn execute_scalar_non_paged_terminal<T, F>(&self, execute: F) -> Result<T, QueryError>
where
E: EntityValue,
F: FnOnce(LoadExecutor<E>, PreparedExecutionPlan<E>) -> Result<T, InternalError>,
{
self.ensure_non_paged_mode_ready()?;
self.session.execute_load_query_with(self.query(), execute)
}
fn map_non_paged_query_output<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_prepared_aggregate_non_paged_terminal<S>(
&self,
strategy: &S,
) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
S: PreparedFluentAggregateExplainStrategy,
{
self.map_non_paged_query_output(|session, query| {
session.explain_query_prepared_aggregate_terminal_with_visible_indexes(query, strategy)
})
}
fn explain_prepared_projection_non_paged_terminal(
&self,
strategy: &PreparedFluentProjectionStrategy,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
self.map_non_paged_query_output(|session, query| {
session.explain_query_prepared_projection_terminal_with_visible_indexes(query, strategy)
})
}
fn explain_execution_descriptor(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
self.map_non_paged_query_output(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 map_prepared_existing_rows_terminal_output<T>(
&self,
strategy: PreparedFluentExistingRowsTerminalStrategy,
map: impl FnOnce(ScalarTerminalBoundaryOutput) -> Result<T, InternalError>,
) -> Result<T, QueryError>
where
E: EntityValue,
{
let output =
self.execute_scalar_terminal_boundary_output(strategy.into_runtime_request())?;
map(output).map_err(QueryError::execute)
}
fn execute_prepared_numeric_field_terminal(
&self,
strategy: PreparedFluentNumericFieldStrategy,
) -> Result<Option<Decimal>, QueryError>
where
E: EntityValue,
{
let (target_field, runtime_request) = strategy.into_runtime_parts();
self.execute_scalar_non_paged_terminal(move |load, plan| {
load.execute_numeric_field_boundary(plan, target_field, runtime_request.into())
})
}
fn map_prepared_order_sensitive_terminal_output<T>(
&self,
strategy: PreparedFluentOrderSensitiveTerminalStrategy,
map: impl FnOnce(ScalarTerminalBoundaryOutput) -> Result<T, InternalError>,
) -> Result<T, QueryError>
where
E: EntityValue,
{
let output =
self.execute_scalar_terminal_boundary_output(strategy.into_runtime_request())?;
map(output).map_err(QueryError::execute)
}
fn execute_prepared_projection_terminal_output(
&self,
strategy: PreparedFluentProjectionStrategy,
) -> Result<crate::db::executor::ScalarProjectionBoundaryOutput, QueryError>
where
E: EntityValue,
{
let (target_field, runtime_request) = strategy.into_runtime_parts();
self.execute_scalar_non_paged_terminal(move |load, plan| {
load.execute_scalar_projection_boundary(plan, target_field, runtime_request.into())
})
}
fn map_prepared_projection_terminal_output<T>(
&self,
strategy: PreparedFluentProjectionStrategy,
map: impl FnOnce(
crate::db::executor::ScalarProjectionBoundaryOutput,
) -> Result<T, InternalError>,
) -> Result<T, QueryError>
where
E: EntityValue,
{
let output = self.execute_prepared_projection_terminal_output(strategy)?;
map(output).map_err(QueryError::execute)
}
fn execute_scalar_terminal_boundary_output<R>(
&self,
runtime_request: R,
) -> Result<ScalarTerminalBoundaryOutput, QueryError>
where
E: EntityValue,
R: Into<ScalarTerminalBoundaryRequest>,
{
self.execute_scalar_non_paged_terminal(move |load, plan| {
load.execute_scalar_terminal_request(plan, runtime_request.into())
})
}
fn map_prepared_scalar_terminal_output<T>(
&self,
strategy: PreparedFluentScalarTerminalStrategy,
map: impl FnOnce(ScalarTerminalBoundaryOutput) -> Result<T, InternalError>,
) -> Result<T, QueryError>
where
E: EntityValue,
{
let output =
self.execute_scalar_terminal_boundary_output(strategy.into_runtime_request())?;
map(output).map_err(QueryError::execute)
}
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.map_prepared_existing_rows_terminal_output(
PreparedFluentExistingRowsTerminalStrategy::exists_rows(),
ScalarTerminalBoundaryOutput::into_exists,
)
}
pub fn explain_exists(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.explain_prepared_aggregate_non_paged_terminal(
&PreparedFluentExistingRowsTerminalStrategy::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.map_non_paged_query_output(
DbSession::explain_query_execution_verbose_with_visible_indexes,
)
}
pub fn count(&self) -> Result<u32, QueryError>
where
E: EntityValue,
{
self.map_prepared_existing_rows_terminal_output(
PreparedFluentExistingRowsTerminalStrategy::count_rows(),
ScalarTerminalBoundaryOutput::into_count,
)
}
pub fn bytes(&self) -> Result<u64, QueryError>
where
E: EntityValue,
{
self.execute_scalar_non_paged_terminal(|load, plan| load.bytes(plan))
}
pub fn bytes_by(&self, field: impl AsRef<str>) -> Result<u64, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.session
.execute_load_query_with(self.query(), move |load, plan| {
load.bytes_by_slot(plan, target_slot)
})
})
}
pub fn explain_bytes_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.session
.explain_query_bytes_by_with_visible_indexes(self.query(), target_slot.field())
})
}
pub fn min(&self) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
self.map_prepared_scalar_terminal_output(
PreparedFluentScalarTerminalStrategy::id_terminal(AggregateKind::Min),
ScalarTerminalBoundaryOutput::into_id,
)
}
pub fn explain_min(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.explain_prepared_aggregate_non_paged_terminal(
&PreparedFluentScalarTerminalStrategy::id_terminal(AggregateKind::Min),
)
}
pub fn min_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.map_prepared_scalar_terminal_output(
PreparedFluentScalarTerminalStrategy::id_by_slot(AggregateKind::Min, target_slot),
ScalarTerminalBoundaryOutput::into_id,
)
})
}
pub fn max(&self) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
self.map_prepared_scalar_terminal_output(
PreparedFluentScalarTerminalStrategy::id_terminal(AggregateKind::Max),
ScalarTerminalBoundaryOutput::into_id,
)
}
pub fn explain_max(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.explain_prepared_aggregate_non_paged_terminal(
&PreparedFluentScalarTerminalStrategy::id_terminal(AggregateKind::Max),
)
}
pub fn max_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.map_prepared_scalar_terminal_output(
PreparedFluentScalarTerminalStrategy::id_by_slot(AggregateKind::Max, target_slot),
ScalarTerminalBoundaryOutput::into_id,
)
})
}
pub fn nth_by(&self, field: impl AsRef<str>, nth: usize) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.map_prepared_order_sensitive_terminal_output(
PreparedFluentOrderSensitiveTerminalStrategy::nth_by_slot(target_slot, nth),
ScalarTerminalBoundaryOutput::into_id,
)
})
}
pub fn sum_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.execute_prepared_numeric_field_terminal(
PreparedFluentNumericFieldStrategy::sum_by_slot(target_slot),
)
})
}
pub fn explain_sum_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.explain_prepared_aggregate_non_paged_terminal(
&PreparedFluentNumericFieldStrategy::sum_by_slot(target_slot),
)
})
}
pub fn sum_distinct_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.execute_prepared_numeric_field_terminal(
PreparedFluentNumericFieldStrategy::sum_distinct_by_slot(target_slot),
)
})
}
pub fn explain_sum_distinct_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.explain_prepared_aggregate_non_paged_terminal(
&PreparedFluentNumericFieldStrategy::sum_distinct_by_slot(target_slot),
)
})
}
pub fn avg_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.execute_prepared_numeric_field_terminal(
PreparedFluentNumericFieldStrategy::avg_by_slot(target_slot),
)
})
}
pub fn explain_avg_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.explain_prepared_aggregate_non_paged_terminal(
&PreparedFluentNumericFieldStrategy::avg_by_slot(target_slot),
)
})
}
pub fn avg_distinct_by(&self, field: impl AsRef<str>) -> Result<Option<Decimal>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.execute_prepared_numeric_field_terminal(
PreparedFluentNumericFieldStrategy::avg_distinct_by_slot(target_slot),
)
})
}
pub fn explain_avg_distinct_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.explain_prepared_aggregate_non_paged_terminal(
&PreparedFluentNumericFieldStrategy::avg_distinct_by_slot(target_slot),
)
})
}
pub fn median_by(&self, field: impl AsRef<str>) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.map_prepared_order_sensitive_terminal_output(
PreparedFluentOrderSensitiveTerminalStrategy::median_by_slot(target_slot),
ScalarTerminalBoundaryOutput::into_id,
)
})
}
pub fn count_distinct_by(&self, field: impl AsRef<str>) -> Result<u32, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.map_prepared_projection_terminal_output(
PreparedFluentProjectionStrategy::count_distinct_by_slot(target_slot),
crate::db::executor::ScalarProjectionBoundaryOutput::into_count,
)
})
}
pub fn explain_count_distinct_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.explain_prepared_projection_non_paged_terminal(
&PreparedFluentProjectionStrategy::count_distinct_by_slot(target_slot),
)
})
}
pub fn min_max_by(&self, field: impl AsRef<str>) -> Result<MinMaxByIds<E>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.map_prepared_order_sensitive_terminal_output(
PreparedFluentOrderSensitiveTerminalStrategy::min_max_by_slot(target_slot),
ScalarTerminalBoundaryOutput::into_id_pair,
)
})
}
pub fn values_by(&self, field: impl AsRef<str>) -> Result<Vec<Value>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.map_prepared_projection_terminal_output(
PreparedFluentProjectionStrategy::values_by_slot(target_slot),
crate::db::executor::ScalarProjectionBoundaryOutput::into_values,
)
})
}
pub fn project_values<P>(&self, projection: &P) -> Result<Vec<Value>, QueryError>
where
E: EntityValue,
P: ValueProjectionExpr,
{
self.with_non_paged_slot(projection.field(), |target_slot| {
let values = self
.execute_prepared_projection_terminal_output(
PreparedFluentProjectionStrategy::values_by_slot(target_slot),
)?
.into_values()
.map_err(QueryError::execute)?;
Self::project_terminal_items(projection, values, |projection, value| {
projection.apply_value(value)
})
})
}
pub fn explain_project_values<P>(
&self,
projection: &P,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
P: ValueProjectionExpr,
{
self.with_non_paged_slot(projection.field(), |target_slot| {
self.explain_prepared_projection_non_paged_terminal(
&PreparedFluentProjectionStrategy::values_by_slot(target_slot),
)
})
}
pub fn explain_values_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.explain_prepared_projection_non_paged_terminal(
&PreparedFluentProjectionStrategy::values_by_slot(target_slot),
)
})
}
pub fn take(&self, take_count: u32) -> Result<EntityResponse<E>, QueryError>
where
E: EntityValue,
{
self.execute_scalar_non_paged_terminal(|load, plan| load.take(plan, take_count))
}
pub fn top_k_by(
&self,
field: impl AsRef<str>,
take_count: u32,
) -> Result<EntityResponse<E>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.session
.execute_load_query_with(self.query(), move |load, plan| {
load.top_k_by_slot(plan, target_slot, take_count)
})
})
}
pub fn bottom_k_by(
&self,
field: impl AsRef<str>,
take_count: u32,
) -> Result<EntityResponse<E>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.session
.execute_load_query_with(self.query(), move |load, plan| {
load.bottom_k_by_slot(plan, target_slot, take_count)
})
})
}
pub fn top_k_by_values(
&self,
field: impl AsRef<str>,
take_count: u32,
) -> Result<Vec<Value>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.session
.execute_load_query_with(self.query(), move |load, plan| {
load.top_k_by_values_slot(plan, target_slot, take_count)
})
})
}
pub fn bottom_k_by_values(
&self,
field: impl AsRef<str>,
take_count: u32,
) -> Result<Vec<Value>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.session
.execute_load_query_with(self.query(), move |load, plan| {
load.bottom_k_by_values_slot(plan, target_slot, take_count)
})
})
}
pub fn top_k_by_with_ids(
&self,
field: impl AsRef<str>,
take_count: u32,
) -> Result<Vec<(Id<E>, Value)>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.session
.execute_load_query_with(self.query(), move |load, plan| {
load.top_k_by_with_ids_slot(plan, target_slot, take_count)
})
})
}
pub fn bottom_k_by_with_ids(
&self,
field: impl AsRef<str>,
take_count: u32,
) -> Result<Vec<(Id<E>, Value)>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.session
.execute_load_query_with(self.query(), move |load, plan| {
load.bottom_k_by_with_ids_slot(plan, target_slot, take_count)
})
})
}
pub fn distinct_values_by(&self, field: impl AsRef<str>) -> Result<Vec<Value>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.map_prepared_projection_terminal_output(
PreparedFluentProjectionStrategy::distinct_values_by_slot(target_slot),
crate::db::executor::ScalarProjectionBoundaryOutput::into_values,
)
})
}
pub fn explain_distinct_values_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.explain_prepared_projection_non_paged_terminal(
&PreparedFluentProjectionStrategy::distinct_values_by_slot(target_slot),
)
})
}
pub fn values_by_with_ids(
&self,
field: impl AsRef<str>,
) -> Result<Vec<(Id<E>, Value)>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.map_prepared_projection_terminal_output(
PreparedFluentProjectionStrategy::values_by_with_ids_slot(target_slot),
crate::db::executor::ScalarProjectionBoundaryOutput::into_values_with_ids,
)
})
}
pub fn project_values_with_ids<P>(
&self,
projection: &P,
) -> Result<Vec<(Id<E>, Value)>, QueryError>
where
E: EntityValue,
P: ValueProjectionExpr,
{
self.with_non_paged_slot(projection.field(), |target_slot| {
let values = self
.execute_prepared_projection_terminal_output(
PreparedFluentProjectionStrategy::values_by_with_ids_slot(target_slot),
)?
.into_values_with_ids::<E>()
.map_err(QueryError::execute)?;
Self::project_terminal_items(projection, values, |projection, (id, value)| {
Ok((id, projection.apply_value(value)?))
})
})
}
pub fn explain_values_by_with_ids(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.explain_prepared_projection_non_paged_terminal(
&PreparedFluentProjectionStrategy::values_by_with_ids_slot(target_slot),
)
})
}
pub fn first_value_by(&self, field: impl AsRef<str>) -> Result<Option<Value>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.map_prepared_projection_terminal_output(
PreparedFluentProjectionStrategy::first_value_by_slot(target_slot),
crate::db::executor::ScalarProjectionBoundaryOutput::into_terminal_value,
)
})
}
pub fn project_first_value<P>(&self, projection: &P) -> Result<Option<Value>, QueryError>
where
E: EntityValue,
P: ValueProjectionExpr,
{
self.with_non_paged_slot(projection.field(), |target_slot| {
let value = self
.execute_prepared_projection_terminal_output(
PreparedFluentProjectionStrategy::first_value_by_slot(target_slot),
)?
.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())
})
}
pub fn explain_first_value_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.explain_prepared_projection_non_paged_terminal(
&PreparedFluentProjectionStrategy::first_value_by_slot(target_slot),
)
})
}
pub fn last_value_by(&self, field: impl AsRef<str>) -> Result<Option<Value>, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.map_prepared_projection_terminal_output(
PreparedFluentProjectionStrategy::last_value_by_slot(target_slot),
crate::db::executor::ScalarProjectionBoundaryOutput::into_terminal_value,
)
})
}
pub fn project_last_value<P>(&self, projection: &P) -> Result<Option<Value>, QueryError>
where
E: EntityValue,
P: ValueProjectionExpr,
{
self.with_non_paged_slot(projection.field(), |target_slot| {
let value = self
.execute_prepared_projection_terminal_output(
PreparedFluentProjectionStrategy::last_value_by_slot(target_slot),
)?
.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())
})
}
pub fn explain_last_value_by(
&self,
field: impl AsRef<str>,
) -> Result<ExplainExecutionNodeDescriptor, QueryError>
where
E: EntityValue,
{
self.with_non_paged_slot(field, |target_slot| {
self.explain_prepared_projection_non_paged_terminal(
&PreparedFluentProjectionStrategy::last_value_by_slot(target_slot),
)
})
}
pub fn first(&self) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
self.map_prepared_order_sensitive_terminal_output(
PreparedFluentOrderSensitiveTerminalStrategy::first(),
ScalarTerminalBoundaryOutput::into_id,
)
}
pub fn explain_first(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.explain_prepared_aggregate_non_paged_terminal(
&PreparedFluentOrderSensitiveTerminalStrategy::first(),
)
}
pub fn last(&self) -> Result<Option<Id<E>>, QueryError>
where
E: EntityValue,
{
self.map_prepared_order_sensitive_terminal_output(
PreparedFluentOrderSensitiveTerminalStrategy::last(),
ScalarTerminalBoundaryOutput::into_id,
)
}
pub fn explain_last(&self) -> Result<ExplainAggregateTerminalPlan, QueryError>
where
E: EntityValue,
{
self.explain_prepared_aggregate_non_paged_terminal(
&PreparedFluentOrderSensitiveTerminalStrategy::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(())
}
}
impl From<PreparedFluentScalarTerminalRuntimeRequest> for ScalarTerminalBoundaryRequest {
fn from(value: PreparedFluentScalarTerminalRuntimeRequest) -> Self {
match value {
PreparedFluentScalarTerminalRuntimeRequest::IdTerminal { kind } => {
Self::IdTerminal { kind }
}
PreparedFluentScalarTerminalRuntimeRequest::IdBySlot { kind, target_field } => {
Self::IdBySlot { kind, target_field }
}
}
}
}
impl From<PreparedFluentExistingRowsTerminalRuntimeRequest> for ScalarTerminalBoundaryRequest {
fn from(value: PreparedFluentExistingRowsTerminalRuntimeRequest) -> Self {
match value {
PreparedFluentExistingRowsTerminalRuntimeRequest::CountRows => Self::Count,
PreparedFluentExistingRowsTerminalRuntimeRequest::ExistsRows => Self::Exists,
}
}
}
impl From<PreparedFluentNumericFieldRuntimeRequest> for ScalarNumericFieldBoundaryRequest {
fn from(value: PreparedFluentNumericFieldRuntimeRequest) -> Self {
match value {
PreparedFluentNumericFieldRuntimeRequest::Sum => Self::Sum,
PreparedFluentNumericFieldRuntimeRequest::SumDistinct => Self::SumDistinct,
PreparedFluentNumericFieldRuntimeRequest::Avg => Self::Avg,
PreparedFluentNumericFieldRuntimeRequest::AvgDistinct => Self::AvgDistinct,
}
}
}
impl From<PreparedFluentOrderSensitiveTerminalRuntimeRequest> for ScalarTerminalBoundaryRequest {
fn from(value: PreparedFluentOrderSensitiveTerminalRuntimeRequest) -> Self {
match value {
PreparedFluentOrderSensitiveTerminalRuntimeRequest::ResponseOrder { kind } => {
Self::IdTerminal { kind }
}
PreparedFluentOrderSensitiveTerminalRuntimeRequest::NthBySlot { target_field, nth } => {
Self::NthBySlot { target_field, nth }
}
PreparedFluentOrderSensitiveTerminalRuntimeRequest::MedianBySlot { target_field } => {
Self::MedianBySlot { target_field }
}
PreparedFluentOrderSensitiveTerminalRuntimeRequest::MinMaxBySlot { target_field } => {
Self::MinMaxBySlot { target_field }
}
}
}
}
impl From<PreparedFluentProjectionRuntimeRequest> for ScalarProjectionBoundaryRequest {
fn from(value: PreparedFluentProjectionRuntimeRequest) -> Self {
match value {
PreparedFluentProjectionRuntimeRequest::Values => Self::Values,
PreparedFluentProjectionRuntimeRequest::DistinctValues => Self::DistinctValues,
PreparedFluentProjectionRuntimeRequest::CountDistinct => Self::CountDistinct,
PreparedFluentProjectionRuntimeRequest::ValuesWithIds => Self::ValuesWithIds,
PreparedFluentProjectionRuntimeRequest::TerminalValue { terminal_kind } => {
Self::TerminalValue { terminal_kind }
}
}
}
}