drasi_core/evaluation/
context.rs

1// Copyright 2024 The Drasi Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use drasi_query_ast::ast::{self, ProjectionClause, QueryPart};
16use hashers::jenkins::spooky_hash::SpookyHasher;
17use std::collections::BTreeMap;
18use std::hash::{Hash, Hasher};
19use std::sync::Arc;
20
21use crate::evaluation::variable_value::VariableValue;
22use crate::interface::QueryClock;
23use crate::models::{Element, ElementReference, ElementTimestamp};
24use crate::path_solver::solution::SolutionSignature;
25
26pub type QueryVariables = BTreeMap<Box<str>, VariableValue>;
27
28#[derive(Debug, Clone)]
29pub enum SideEffects {
30    Apply,
31    RevertForUpdate,
32    RevertForDelete,
33    Snapshot,
34}
35
36#[derive(Debug, Clone, PartialEq)]
37pub enum QueryPartEvaluationContext {
38    Adding {
39        after: QueryVariables,
40    },
41    Updating {
42        before: QueryVariables,
43        after: QueryVariables,
44    },
45    Removing {
46        before: QueryVariables,
47    },
48    Aggregation {
49        before: Option<QueryVariables>,
50        after: QueryVariables,
51        grouping_keys: Vec<String>,
52        default_before: bool,
53        default_after: bool,
54    },
55    Noop,
56}
57
58impl QueryPartEvaluationContext {}
59
60#[derive(Debug, Clone)]
61pub struct ExpressionEvaluationContext<'a> {
62    variables: &'a QueryVariables,
63    side_effects: SideEffects,
64    output_grouping_key: Option<&'a Vec<ast::Expression>>,
65    input_grouping_hash: u64,
66    clock: Arc<dyn QueryClock>,
67    solution_signature: Option<SolutionSignature>,
68    anchor_element: Option<Arc<Element>>,
69}
70
71impl<'a> ExpressionEvaluationContext<'a> {
72    pub fn new(
73        variables: &'a QueryVariables,
74        clock: Arc<dyn QueryClock>,
75    ) -> ExpressionEvaluationContext<'a> {
76        ExpressionEvaluationContext {
77            variables,
78            side_effects: SideEffects::Apply,
79            output_grouping_key: None,
80            input_grouping_hash: u64::default(),
81            clock,
82            solution_signature: None,
83            anchor_element: None,
84        }
85    }
86
87    pub fn from_slot(
88        variables: &'a QueryVariables,
89        clock: Arc<dyn QueryClock>,
90        element_reference: &ElementReference,
91    ) -> ExpressionEvaluationContext<'a> {
92        ExpressionEvaluationContext {
93            variables,
94            side_effects: SideEffects::Apply,
95            output_grouping_key: None,
96            input_grouping_hash: extract_element_reference_hash(element_reference),
97            clock,
98            solution_signature: None,
99            anchor_element: None,
100        }
101    }
102
103    pub fn from_before_change(
104        variables: &'a QueryVariables,
105        side_effect_directive: SideEffects,
106        change_context: &ChangeContext,
107        query_part: &'a QueryPart,
108    ) -> ExpressionEvaluationContext<'a> {
109        ExpressionEvaluationContext {
110            variables,
111            side_effects: side_effect_directive,
112            input_grouping_hash: change_context.before_grouping_hash,
113            clock: change_context.before_clock.clone(),
114            solution_signature: Some(change_context.solution_signature),
115            anchor_element: change_context.before_anchor_element.clone(),
116            output_grouping_key: match &query_part.return_clause {
117                ProjectionClause::GroupBy { grouping, .. } => Some(grouping),
118                _ => None,
119            },
120        }
121    }
122
123    pub fn from_after_change(
124        variables: &'a QueryVariables,
125        change_context: &ChangeContext,
126        query_part: &'a QueryPart,
127    ) -> ExpressionEvaluationContext<'a> {
128        ExpressionEvaluationContext {
129            variables,
130            side_effects: SideEffects::Apply,
131            input_grouping_hash: change_context.after_grouping_hash,
132            clock: change_context.after_clock.clone(),
133            solution_signature: Some(change_context.solution_signature),
134            anchor_element: change_context.after_anchor_element.clone(),
135            output_grouping_key: match &query_part.return_clause {
136                ProjectionClause::GroupBy { grouping, .. } => Some(grouping),
137                _ => None,
138            },
139        }
140    }
141
142    pub fn replace_variables(&mut self, new_data: &'a QueryVariables) {
143        self.variables = new_data;
144    }
145
146    pub fn get_variable(&self, name: Arc<str>) -> Option<&VariableValue> {
147        self.variables.get(&name.to_string().into_boxed_str())
148    }
149
150    pub fn clone_variables(&self) -> QueryVariables {
151        self.variables.clone()
152    }
153
154    pub fn set_side_effects(&mut self, directive: SideEffects) {
155        self.side_effects = directive;
156    }
157
158    pub fn get_side_effects(&self) -> &SideEffects {
159        &self.side_effects
160    }
161
162    pub fn set_output_grouping_key(&mut self, grouping_key: &'a Vec<ast::Expression>) {
163        self.output_grouping_key = Some(grouping_key);
164    }
165
166    pub fn get_output_grouping_key(&self) -> Option<&Vec<ast::Expression>> {
167        self.output_grouping_key
168    }
169
170    pub fn get_transaction_time(&self) -> ElementTimestamp {
171        self.clock.get_transaction_time()
172    }
173
174    pub fn get_realtime(&self) -> ElementTimestamp {
175        self.clock.get_realtime()
176    }
177
178    pub fn get_clock(&self) -> Arc<dyn QueryClock> {
179        self.clock.clone()
180    }
181
182    pub fn get_solution_signature(&self) -> Option<SolutionSignature> {
183        self.solution_signature
184    }
185
186    pub fn get_anchor_element(&self) -> Option<Arc<Element>> {
187        self.anchor_element.clone()
188    }
189
190    pub fn get_input_grouping_hash(&self) -> u64 {
191        self.input_grouping_hash
192    }
193}
194
195#[derive(Debug, Clone)]
196pub struct ChangeContext {
197    pub solution_signature: SolutionSignature,
198    pub before_anchor_element: Option<Arc<Element>>,
199    pub after_anchor_element: Option<Arc<Element>>,
200    pub before_clock: Arc<dyn QueryClock>,
201    pub after_clock: Arc<dyn QueryClock>,
202    pub is_future_reprocess: bool,
203    pub before_grouping_hash: u64,
204    pub after_grouping_hash: u64,
205}
206
207fn extract_element_reference_hash(element_reference: &ElementReference) -> u64 {
208    let mut hasher = SpookyHasher::default();
209    element_reference.source_id.hash(&mut hasher);
210    element_reference.element_id.hash(&mut hasher);
211    hasher.finish()
212}