Skip to main content

graphrecords_query/operations/
mod.rs

1mod aggregation;
2mod argument;
3mod arithmetic;
4mod cache;
5mod comparison;
6mod conversion;
7mod errors;
8mod grouping;
9mod indexing;
10mod is_type;
11mod kernel;
12mod logic;
13mod membership;
14mod numeric;
15mod on_missing;
16mod ordering;
17pub mod policy;
18mod string_operations;
19mod structure;
20mod traversal;
21mod uniqueness;
22
23use crate::{
24    EvaluateContext, EvaluateOperand, Explain, Operand, QueryResult,
25    execution::EvaluationCache,
26    explain::ExplainFormatter,
27    optimizer::{
28        EmptyRule, Estimate, Estimated, MatchInputs, OperationInputs, OptimizePlan, OptimizerHints,
29        PlanInputs, PlanNode, Session, Stats, Transformed,
30    },
31    registry::OperationManifest,
32    sealed::Sealed,
33};
34pub use aggregation::{
35    AllOperation, AnyOperation, CountOperation, MaximumOperation, MeanOperation, MedianOperation,
36    MinimumOperation, ModeOperation, ProductOperation, RandomOperation, StandardDeviationOperation,
37    SumOperation, UniqueCountOperation, VarianceOperation,
38};
39pub use argument::{
40    AlignableArity, Alignment, Argument, ArgumentPlan, ArgumentSource, EnumerableArity,
41    IndexedElementContainer, IndexedElementSource, IntoArgument, Keyed, Lookup, Prepare,
42    PreparedArgument, PreparedArity, PreparedIndexedMultiple, SetArity, SetSource, SourceDomain,
43    Unaligned,
44};
45pub use arithmetic::{
46    AddOperation, DivideOperation, ModuloOperation, MultiplyOperation, PowerOperation,
47    SubtractOperation,
48};
49pub use cache::CacheContext;
50pub use comparison::{
51    EqualToOperation, GreaterThanOperation, GreaterThanOrEqualToOperation, LessThanOperation,
52    LessThanOrEqualToOperation, NotEqualToOperation,
53};
54pub use conversion::{
55    CastOperation, DiscardIndexOperation, DiscardValueOperation, EnumerateOperation,
56    ExpandToOperation, TransitionOperation,
57};
58pub use errors::{
59    DropErrorsIn, DropErrorsOf, DropErrorsWithCause, ErrorKindNameOperation, ErrorKindOperation,
60    ErrorPolicy, ErrorPolicyIn, ErrorPolicyOf, ErrorPolicyWithCause, ErrorsOperation,
61    HasErrorCauseOperation, InErrorGroupOperation, IsErrorKindOperation, RaiseErrorsIn,
62    RaiseErrorsOf, RaiseErrorsWithCause, RaiseWhenErrorsIn, RaiseWhenErrorsOf,
63    RaiseWhenErrorsWithCause, ReplaceErrorsIn, ReplaceErrorsOf, ReplaceErrorsWithCause,
64};
65use graphrecords_core::GraphRecord;
66pub use graphrecords_macros::Operation;
67pub use grouping::{
68    BroadcastOperation, BroadcastViaOperation, BucketErrorPolicy, BucketErrorPolicyIn,
69    BucketErrorPolicyOf, BucketErrorPolicyWithCause, BucketErrorsOperation, BucketFailureArity,
70    DropBucketErrors, DropBucketErrorsIn, DropBucketErrorsOf, DropBucketErrorsWithCause,
71    DropKeyErrors, DropKeyErrorsIn, DropKeyErrorsOf, DropKeyErrorsWithCause, GroupByOperation,
72    HavingOperation, KeyErrorPolicy, KeyErrorPolicyIn, KeyErrorPolicyOf, KeyErrorPolicyWithCause,
73    KeyErrorsOperation, KeysOperation, RaiseBucketErrors, RaiseBucketErrorsIn, RaiseBucketErrorsOf,
74    RaiseBucketErrorsWithCause, RaiseKeyErrors, RaiseKeyErrorsIn, RaiseKeyErrorsOf,
75    RaiseKeyErrorsWithCause, UngroupKeyedOperation, UngroupOperation,
76};
77pub use indexing::{
78    ChildIndexOperation, IndexOperation, ParentIndexOperation, ResolveOperation, SelectOperation,
79};
80pub use is_type::{
81    IsBoolOperation, IsDateTimeOperation, IsDurationOperation, IsFloatOperation, IsIntOperation,
82    IsNullOperation, IsStringOperation,
83};
84pub use kernel::{
85    BareStream, ElementKernel, ElementPipeline, GroupKernel, KeyedStream, LaneKernel,
86};
87pub use logic::{AndOperation, ExclusiveOrOperation, NotOperation, OrOperation};
88pub use membership::IsInOperation;
89pub use numeric::{
90    AbsoluteOperation, CeilOperation, ClipOperation, CubeRootOperation, ExponentialOperation,
91    FloorOperation, LogarithmOperation, NegateOperation, RoundOperation, SignOperation,
92    SquareRootOperation,
93};
94pub use on_missing::{MaybeAbsent, MissingPolicy, WithMissing};
95pub use ordering::{
96    FirstOperation, LastOperation, ReverseOrderOperation, ShuffleOperation, SortByOperation,
97    SortOperation, TakeOperation, UnorderOperation,
98};
99use std::{
100    any::Any,
101    fmt,
102    hash::{Hash, Hasher},
103};
104pub use string_operations::{
105    ContainsOperation, EndsWithOperation, LengthOperation, LowercaseOperation, MatchesOperation,
106    PadEndOperation, PadStartOperation, ReplaceAllOperation, ReplaceOperation, ReverseOperation,
107    SliceOperation, SplitOperation, StartsWithOperation, StripPrefixOperation,
108    StripSuffixOperation, TrimEndOperation, TrimOperation, TrimStartOperation, UppercaseOperation,
109};
110pub use structure::{
111    AttributeOperation, AttributesOperation, FilterOperation, HasAttributeOperation,
112    InGroupOperation,
113};
114pub use traversal::{
115    EdgeDirection, EdgesOperation, EndpointOperation, NeighborsOperation, NodesOperation,
116    ViaEdgesOperation, ViaNeighborsOperation, ViaNodesOperation,
117};
118pub use uniqueness::{DropDuplicatesOperation, IsDuplicatedOperation, UniqueOperation};
119
120pub trait OperationScope: Sealed + 'static {}
121
122pub struct Element;
123pub struct Lane;
124pub struct Group;
125
126impl Sealed for Element {}
127impl Sealed for Lane {}
128impl Sealed for Group {}
129
130impl OperationScope for Element {}
131impl OperationScope for Lane {}
132impl OperationScope for Group {}
133
134pub trait Operation: Prepare + OperationInputs + Explain {
135    type Scope: OperationScope;
136}
137
138pub trait Apply<P: Operation<Scope = S>, S: OperationScope = <P as Operation>::Scope>:
139    Operand
140{
141    type Output: Operand;
142
143    fn apply<'a>(
144        graphrecord: &'a GraphRecord,
145        values: Self::ReturnValue<'a>,
146        prepared: P::Prepared<'a>,
147    ) -> QueryResult<<Self::Output as EvaluateOperand>::ReturnValue<'a>>
148    where
149        Self: 'a;
150
151    fn estimate(operation: &P, input: Estimate, stats: &Stats) -> Estimate;
152}
153
154pub struct OperationContext<I: Apply<P>, P: Operation> {
155    input: I,
156    operation: P,
157}
158
159impl<I: Apply<P>, P: Operation> OperationContext<I, P> {
160    #[must_use]
161    pub const fn new(input: I, operation: P) -> Self {
162        Self { input, operation }
163    }
164
165    #[must_use]
166    pub const fn operation(&self) -> &P {
167        &self.operation
168    }
169}
170
171impl<I: Apply<P>, P: Operation> MatchInputs for OperationContext<I, P> {
172    type Inputs<'a> = P::Inputs<'a, I>;
173
174    fn inputs(&self) -> Self::Inputs<'_> {
175        OperationInputs::inputs(&self.operation, &self.input)
176    }
177}
178
179impl<I: Apply<P>, P: Operation> PlanNode for OperationContext<I, P> {
180    fn inputs(&self) -> Vec<&dyn PlanNode> {
181        let mut inputs = vec![self.input.as_plan_node()];
182        inputs.extend(PlanInputs::inputs(&self.operation));
183
184        inputs
185    }
186
187    fn dyn_eq(&self, other: &dyn PlanNode) -> bool {
188        let Some(other) = other.downcast::<Self>() else {
189            return false;
190        };
191
192        self.operation.identity_eq(&other.operation)
193            && self.input.as_plan_node().dyn_eq(other.input.as_plan_node())
194    }
195
196    fn dyn_hash(&self, mut state: &mut dyn Hasher) {
197        self.type_id().hash(&mut state);
198        self.operation.identity_hash(&mut state);
199        self.input.as_plan_node().dyn_hash(state);
200    }
201}
202
203impl<I: Apply<P>, P: Operation> OptimizerHints for OperationContext<I, P> {
204    fn commutes_with_filter(&self) -> bool {
205        self.operation.commutes_with_filter()
206    }
207
208    fn allows_limit_pushdown(&self) -> bool {
209        self.operation.allows_limit_pushdown()
210    }
211
212    fn is_volatile(&self) -> bool {
213        self.operation.is_volatile()
214    }
215
216    fn empty_rule(&self) -> EmptyRule {
217        self.operation.empty_rule()
218    }
219}
220
221impl<I: Apply<P>, P: Operation> Explain for OperationContext<I, P> {
222    fn describe<'a>(&'a self, formatter: &mut ExplainFormatter<'a, '_>) -> fmt::Result {
223        formatter.child(&self.input);
224        self.operation.describe(formatter)?;
225
226        Ok(())
227    }
228}
229
230impl<I: Apply<P>, P: Operation> EvaluateContext for OperationContext<I, P> {
231    type Operand = I::Output;
232
233    fn evaluate<'a>(
234        &'a self,
235        graphrecord: &'a GraphRecord,
236        cache: &'a EvaluationCache<'a>,
237    ) -> QueryResult<<Self::Operand as EvaluateOperand>::ReturnValue<'a>> {
238        let values = self.input.evaluate(graphrecord, cache)?;
239        let prepared = self.operation.prepare(graphrecord, cache)?;
240
241        I::apply(graphrecord, values, prepared)
242    }
243}
244
245impl<I: Apply<P>, P: Operation> Estimated for OperationContext<I, P> {
246    fn estimate(&self, stats: &Stats) -> Estimate {
247        I::estimate(&self.operation, self.input.context().estimate(stats), stats)
248    }
249}
250
251impl<I: Apply<P>, P: Operation> OptimizePlan for OperationContext<I, P> {
252    type Output = I::Output;
253
254    fn optimize(&self, original: &Self::Output, session: &Session) -> Transformed<Self::Output> {
255        let input = session.optimize(&self.input);
256        let operation = self.operation.optimize(session);
257
258        if !input.is_changed() && !operation.is_changed() {
259            return Transformed::unchanged(original.clone());
260        }
261
262        let input = input.into_parts().0;
263        let operation = operation.into_parts().0;
264
265        Transformed::changed(Self::Output::new(Self { input, operation }))
266    }
267}
268
269pub(crate) fn operation_manifests() -> Vec<OperationManifest> {
270    aggregation::operation_manifests()
271        .into_iter()
272        .chain(arithmetic::operation_manifests())
273        .chain(comparison::operation_manifests())
274        .chain(conversion::operation_manifests())
275        .chain(errors::operation_manifests())
276        .chain(grouping::operation_manifests())
277        .chain(indexing::operation_manifests())
278        .chain(is_type::operation_manifests())
279        .chain(logic::operation_manifests())
280        .chain(membership::operation_manifests())
281        .chain(numeric::operation_manifests())
282        .chain(ordering::operation_manifests())
283        .chain(string_operations::operation_manifests())
284        .chain(structure::operation_manifests())
285        .chain(traversal::operation_manifests())
286        .chain(uniqueness::operation_manifests())
287        .collect()
288}