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}