graphrecords-query 0.5.0

High-performance graph-based data records
Documentation
mod aggregation;
mod argument;
mod arithmetic;
mod cache;
mod comparison;
mod conversion;
mod errors;
mod grouping;
mod indexing;
mod is_type;
mod kernel;
mod logic;
mod membership;
mod numeric;
mod on_missing;
mod ordering;
pub mod policy;
mod string_operations;
mod structure;
mod traversal;
mod uniqueness;

use crate::{
    EvaluateContext, EvaluateOperand, Explain, Operand, QueryResult,
    execution::EvaluationCache,
    explain::ExplainFormatter,
    optimizer::{
        EmptyRule, Estimate, Estimated, MatchInputs, OperationInputs, OptimizePlan, OptimizerHints,
        PlanInputs, PlanNode, Session, Stats, Transformed,
    },
    registry::OperationManifest,
    sealed::Sealed,
};
pub use aggregation::{
    AllOperation, AnyOperation, CountOperation, MaximumOperation, MeanOperation, MedianOperation,
    MinimumOperation, ModeOperation, ProductOperation, RandomOperation, StandardDeviationOperation,
    SumOperation, UniqueCountOperation, VarianceOperation,
};
pub use argument::{
    AlignableArity, Alignment, Argument, ArgumentPlan, ArgumentSource, EnumerableArity,
    IndexedElementContainer, IndexedElementSource, IntoArgument, Keyed, Lookup, Prepare,
    PreparedArgument, PreparedArity, PreparedIndexedMultiple, SetArity, SetSource, SourceDomain,
    Unaligned,
};
pub use arithmetic::{
    AddOperation, DivideOperation, ModuloOperation, MultiplyOperation, PowerOperation,
    SubtractOperation,
};
pub use cache::CacheContext;
pub use comparison::{
    EqualToOperation, GreaterThanOperation, GreaterThanOrEqualToOperation, LessThanOperation,
    LessThanOrEqualToOperation, NotEqualToOperation,
};
pub use conversion::{
    CastOperation, DiscardIndexOperation, DiscardValueOperation, EnumerateOperation,
    ExpandToOperation, TransitionOperation,
};
pub use errors::{
    DropErrorsIn, DropErrorsOf, DropErrorsWithCause, ErrorKindNameOperation, ErrorKindOperation,
    ErrorPolicy, ErrorPolicyIn, ErrorPolicyOf, ErrorPolicyWithCause, ErrorsOperation,
    HasErrorCauseOperation, InErrorGroupOperation, IsErrorKindOperation, RaiseErrorsIn,
    RaiseErrorsOf, RaiseErrorsWithCause, RaiseWhenErrorsIn, RaiseWhenErrorsOf,
    RaiseWhenErrorsWithCause, ReplaceErrorsIn, ReplaceErrorsOf, ReplaceErrorsWithCause,
};
use graphrecords_core::GraphRecord;
pub use graphrecords_macros::Operation;
pub use grouping::{
    BroadcastOperation, BroadcastViaOperation, BucketErrorPolicy, BucketErrorPolicyIn,
    BucketErrorPolicyOf, BucketErrorPolicyWithCause, BucketErrorsOperation, BucketFailureArity,
    DropBucketErrors, DropBucketErrorsIn, DropBucketErrorsOf, DropBucketErrorsWithCause,
    DropKeyErrors, DropKeyErrorsIn, DropKeyErrorsOf, DropKeyErrorsWithCause, GroupByOperation,
    HavingOperation, KeyErrorPolicy, KeyErrorPolicyIn, KeyErrorPolicyOf, KeyErrorPolicyWithCause,
    KeyErrorsOperation, KeysOperation, RaiseBucketErrors, RaiseBucketErrorsIn, RaiseBucketErrorsOf,
    RaiseBucketErrorsWithCause, RaiseKeyErrors, RaiseKeyErrorsIn, RaiseKeyErrorsOf,
    RaiseKeyErrorsWithCause, UngroupKeyedOperation, UngroupOperation,
};
pub use indexing::{
    ChildIndexOperation, IndexOperation, ParentIndexOperation, ResolveOperation, SelectOperation,
};
pub use is_type::{
    IsBoolOperation, IsDateTimeOperation, IsDurationOperation, IsFloatOperation, IsIntOperation,
    IsNullOperation, IsStringOperation,
};
pub use kernel::{
    BareStream, ElementKernel, ElementPipeline, GroupKernel, KeyedStream, LaneKernel,
};
pub use logic::{AndOperation, ExclusiveOrOperation, NotOperation, OrOperation};
pub use membership::IsInOperation;
pub use numeric::{
    AbsoluteOperation, CeilOperation, ClipOperation, CubeRootOperation, ExponentialOperation,
    FloorOperation, LogarithmOperation, NegateOperation, RoundOperation, SignOperation,
    SquareRootOperation,
};
pub use on_missing::{MaybeAbsent, MissingPolicy, WithMissing};
pub use ordering::{
    FirstOperation, LastOperation, ReverseOrderOperation, ShuffleOperation, SortByOperation,
    SortOperation, TakeOperation, UnorderOperation,
};
use std::{
    any::Any,
    fmt,
    hash::{Hash, Hasher},
};
pub use string_operations::{
    ContainsOperation, EndsWithOperation, LengthOperation, LowercaseOperation, MatchesOperation,
    PadEndOperation, PadStartOperation, ReplaceAllOperation, ReplaceOperation, ReverseOperation,
    SliceOperation, SplitOperation, StartsWithOperation, StripPrefixOperation,
    StripSuffixOperation, TrimEndOperation, TrimOperation, TrimStartOperation, UppercaseOperation,
};
pub use structure::{
    AttributeOperation, AttributesOperation, FilterOperation, HasAttributeOperation,
    InGroupOperation,
};
pub use traversal::{
    EdgeDirection, EdgesOperation, EndpointOperation, NeighborsOperation, NodesOperation,
    ViaEdgesOperation, ViaNeighborsOperation, ViaNodesOperation,
};
pub use uniqueness::{DropDuplicatesOperation, IsDuplicatedOperation, UniqueOperation};

pub trait OperationScope: Sealed + 'static {}

pub struct Element;
pub struct Lane;
pub struct Group;

impl Sealed for Element {}
impl Sealed for Lane {}
impl Sealed for Group {}

impl OperationScope for Element {}
impl OperationScope for Lane {}
impl OperationScope for Group {}

pub trait Operation: Prepare + OperationInputs + Explain {
    type Scope: OperationScope;
}

pub trait Apply<P: Operation<Scope = S>, S: OperationScope = <P as Operation>::Scope>:
    Operand
{
    type Output: Operand;

    fn apply<'a>(
        graphrecord: &'a GraphRecord,
        values: Self::ReturnValue<'a>,
        prepared: P::Prepared<'a>,
    ) -> QueryResult<<Self::Output as EvaluateOperand>::ReturnValue<'a>>
    where
        Self: 'a;

    fn estimate(operation: &P, input: Estimate, stats: &Stats) -> Estimate;
}

pub struct OperationContext<I: Apply<P>, P: Operation> {
    input: I,
    operation: P,
}

impl<I: Apply<P>, P: Operation> OperationContext<I, P> {
    #[must_use]
    pub const fn new(input: I, operation: P) -> Self {
        Self { input, operation }
    }

    #[must_use]
    pub const fn operation(&self) -> &P {
        &self.operation
    }
}

impl<I: Apply<P>, P: Operation> MatchInputs for OperationContext<I, P> {
    type Inputs<'a> = P::Inputs<'a, I>;

    fn inputs(&self) -> Self::Inputs<'_> {
        OperationInputs::inputs(&self.operation, &self.input)
    }
}

impl<I: Apply<P>, P: Operation> PlanNode for OperationContext<I, P> {
    fn inputs(&self) -> Vec<&dyn PlanNode> {
        let mut inputs = vec![self.input.as_plan_node()];
        inputs.extend(PlanInputs::inputs(&self.operation));

        inputs
    }

    fn dyn_eq(&self, other: &dyn PlanNode) -> bool {
        let Some(other) = other.downcast::<Self>() else {
            return false;
        };

        self.operation.identity_eq(&other.operation)
            && self.input.as_plan_node().dyn_eq(other.input.as_plan_node())
    }

    fn dyn_hash(&self, mut state: &mut dyn Hasher) {
        self.type_id().hash(&mut state);
        self.operation.identity_hash(&mut state);
        self.input.as_plan_node().dyn_hash(state);
    }
}

impl<I: Apply<P>, P: Operation> OptimizerHints for OperationContext<I, P> {
    fn commutes_with_filter(&self) -> bool {
        self.operation.commutes_with_filter()
    }

    fn allows_limit_pushdown(&self) -> bool {
        self.operation.allows_limit_pushdown()
    }

    fn is_volatile(&self) -> bool {
        self.operation.is_volatile()
    }

    fn empty_rule(&self) -> EmptyRule {
        self.operation.empty_rule()
    }
}

impl<I: Apply<P>, P: Operation> Explain for OperationContext<I, P> {
    fn describe<'a>(&'a self, formatter: &mut ExplainFormatter<'a, '_>) -> fmt::Result {
        formatter.child(&self.input);
        self.operation.describe(formatter)?;

        Ok(())
    }
}

impl<I: Apply<P>, P: Operation> EvaluateContext for OperationContext<I, P> {
    type Operand = I::Output;

    fn evaluate<'a>(
        &'a self,
        graphrecord: &'a GraphRecord,
        cache: &'a EvaluationCache<'a>,
    ) -> QueryResult<<Self::Operand as EvaluateOperand>::ReturnValue<'a>> {
        let values = self.input.evaluate(graphrecord, cache)?;
        let prepared = self.operation.prepare(graphrecord, cache)?;

        I::apply(graphrecord, values, prepared)
    }
}

impl<I: Apply<P>, P: Operation> Estimated for OperationContext<I, P> {
    fn estimate(&self, stats: &Stats) -> Estimate {
        I::estimate(&self.operation, self.input.context().estimate(stats), stats)
    }
}

impl<I: Apply<P>, P: Operation> OptimizePlan for OperationContext<I, P> {
    type Output = I::Output;

    fn optimize(&self, original: &Self::Output, session: &Session) -> Transformed<Self::Output> {
        let input = session.optimize(&self.input);
        let operation = self.operation.optimize(session);

        if !input.is_changed() && !operation.is_changed() {
            return Transformed::unchanged(original.clone());
        }

        let input = input.into_parts().0;
        let operation = operation.into_parts().0;

        Transformed::changed(Self::Output::new(Self { input, operation }))
    }
}

pub(crate) fn operation_manifests() -> Vec<OperationManifest> {
    aggregation::operation_manifests()
        .into_iter()
        .chain(arithmetic::operation_manifests())
        .chain(comparison::operation_manifests())
        .chain(conversion::operation_manifests())
        .chain(errors::operation_manifests())
        .chain(grouping::operation_manifests())
        .chain(indexing::operation_manifests())
        .chain(is_type::operation_manifests())
        .chain(logic::operation_manifests())
        .chain(membership::operation_manifests())
        .chain(numeric::operation_manifests())
        .chain(ordering::operation_manifests())
        .chain(string_operations::operation_manifests())
        .chain(structure::operation_manifests())
        .chain(traversal::operation_manifests())
        .chain(uniqueness::operation_manifests())
        .collect()
}