Skip to main content

linkrs_query/executor/expression/evaluator/
traits.rs

1//! The `trait` for defining the context in which expressions are evaluated
2//!
3//! Provide a unified context interface for evaluating expressions in graph databases.
4//!
5//! Note: This trait is used for the evaluation of runtime expressions.
6//! For compilation-time analysis, please use `ExpressionAnalysisContext`.
7
8use crate::executor::expression::evaluation_context::graph_storage::GraphStorageRef;
9use crate::executor::expression::evaluator::expression_evaluator::ExpressionEvaluator;
10use crate::executor::expression::functions::OwnedFunctionRef;
11use crate::executor::expression::ExpressionError;
12use crate::executor::streaming::slot::SlotId;
13use linkrs_core::types::expr::{Expression, SubqueryBody};
14use linkrs_core::value::list::List;
15use linkrs_core::value::NullType;
16use linkrs_core::vertex_edge_path::{Path, Step};
17use linkrs_core::Value;
18
19/// The "expression evaluation context trait"
20///
21/// Provide a unified context interface for evaluating graph database expressions.
22///
23/// Note: This trait is used for the evaluation of runtime expressions.
24/// For compilation-time analysis, please use `ExpressionAnalysisContext`.
25pub trait ExpressionContext {
26    /// Obtain the value of the variable
27    fn get_variable(&self, name: &str) -> Option<Value>;
28
29    /// Obtain the value of a query parameter (`@name`).
30    /// Default implementation returns `None` — contexts without parameter
31    /// support simply ignore parameter references.
32    fn get_parameter(&self, _name: &str) -> Option<Value> {
33        None
34    }
35
36    /// Obtain the value of a session variable (`$name`).
37    ///
38    /// Default implementation reports an error: an undefined session
39    /// variable is a query error, not NULL. Row contexts backed by a
40    /// session snapshot override this to return the actual value.
41    fn get_session_variable(&self, name: &str) -> Result<Value, ExpressionError> {
42        Err(ExpressionError::type_error(format!(
43            "Session variable `{}` is not defined in this context",
44            name
45        )))
46    }
47
48    /// Obtain the value of a variable by slot ID (fast path).
49    /// Default implementation falls back to name-based lookup.
50    fn get_variable_by_slot(&self, _slot: SlotId) -> Option<Value> {
51        None
52    }
53
54    /// Setting variable values
55    fn set_variable(&mut self, name: String, value: Value);
56
57    /// Obtain a function reference
58    fn get_function(&self, name: &str) -> Option<OwnedFunctionRef> {
59        let _ = name;
60        None
61    }
62
63    /// Obtain the graph storage accessor for graph algorithm functions
64    fn get_graph_storage(&self) -> Option<GraphStorageRef> {
65        None
66    }
67
68    /// Obtain the sequence catalog manager for `curr_val` / `next_val`.
69    /// Default implementation returns `None` — contexts without sequence
70    /// support report the functions' missing-context error.
71    fn get_sequence_manager(
72        &self,
73    ) -> Option<std::sync::Arc<linkrs_core::metadata::SequenceManager>> {
74        None
75    }
76
77    /// Check whether the context supports caching.
78    fn supports_cache(&self) -> bool {
79        false
80    }
81
82    /// Obtain the cache manager (if available).
83    ///
84    /// The caching function has been removed; the result is "None".
85    fn get_cache(&mut self) -> Option<&mut ()> {
86        None
87    }
88
89    /// Execute a subquery body and return the result values.
90    ///
91    /// Used for EXISTS and IN subquery expressions.
92    /// Returns the list of values from the subquery's RETURN clause.
93    fn execute_subquery(&mut self, body: &SubqueryBody) -> Result<Vec<Value>, ExpressionError> {
94        let _ = body;
95        Err(ExpressionError::type_error(
96            "Subquery execution not supported in this context",
97        ))
98    }
99
100    /// EXISTS semantics: whether the subquery produces at least one row.
101    ///
102    /// Default implementation runs [`Self::execute_subquery`] and tests for
103    /// a non-empty result; streaming contexts override it to short-circuit
104    /// and to cache non-correlated results.
105    fn execute_exists(&mut self, body: &SubqueryBody) -> Result<bool, ExpressionError> {
106        let results = self.execute_subquery(body)?;
107        Ok(!results.is_empty())
108    }
109    /// IN semantics: whether `value` occurs in the subquery result.
110    ///
111    /// A NULL left operand, or NULL values inside the result set, never
112    /// match — consistent with the conjunctive `keys_match` path.
113    fn contains_subquery(
114        &mut self,
115        body: &SubqueryBody,
116        value: &Value,
117    ) -> Result<Value, ExpressionError> {
118        if value.is_null() {
119            return Ok(Value::Bool(false));
120        }
121        let results = self.execute_subquery(body)?;
122        Ok(Value::Bool(
123            results.iter().any(|v| !v.is_null() && v == value),
124        ))
125    }
126
127    /// Scalar-subquery semantics: the first result value, or NULL when the
128    /// subquery returns no rows. Streaming contexts override this to dispatch
129    /// through the hosting operator's subquery executor.
130    fn execute_scalar_subquery(&mut self, body: &SubqueryBody) -> Result<Value, ExpressionError> {
131        let _ = body;
132        Err(ExpressionError::type_error(
133            "Scalar subquery execution not supported in this context",
134        ))
135    }
136
137    /// Evaluate a `:Label` expression against the current row binding.
138    ///
139    /// The label expression is a bare tag reference with no bound variable,
140    /// so contexts that cannot resolve it report an error naming the label
141    /// instead of a generic "require runtime context" message.
142    fn evaluate_label(&self, label: &str) -> Result<Value, ExpressionError> {
143        Err(ExpressionError::type_error(format!(
144            "Label expression `:{}` requires runtime context to resolve",
145            label
146        )))
147    }
148
149    /// Evaluate a list comprehension `[variable IN source WHERE filter | map]`.
150    ///
151    /// Generic default: iterates the source collection, binds `variable` in
152    /// this context, and applies the optional filter and map expressions.
153    /// Works with any context that supports `get_variable`/`set_variable`.
154    fn evaluate_list_comprehension(
155        &mut self,
156        variable: &str,
157        source: &Expression,
158        filter: Option<&Expression>,
159        map: Option<&Expression>,
160    ) -> Result<Value, ExpressionError>
161    where
162        Self: Sized,
163    {
164        let source_value = ExpressionEvaluator::evaluate(source, self)?;
165        let elements = match source_value {
166            Value::List(list) => list.values,
167            Value::Null(_) => return Ok(Value::Null(NullType::Null)),
168            other => {
169                return Err(ExpressionError::type_error(format!(
170                    "List comprehension source must be a list, got {:?}",
171                    other.get_type()
172                )))
173            }
174        };
175        let mut results = Vec::with_capacity(elements.len());
176        for element in elements {
177            self.set_variable(variable.to_string(), element);
178            if let Some(filter_expr) = filter {
179                match ExpressionEvaluator::evaluate(filter_expr, self)? {
180                    Value::Bool(true) => {}
181                    Value::Bool(false) | Value::Null(_) => continue,
182                    other => {
183                        return Err(ExpressionError::type_error(format!(
184                            "List comprehension filter must evaluate to a boolean, got {:?}",
185                            other.get_type()
186                        )))
187                    }
188                }
189            }
190            let mapped = match map {
191                Some(map_expr) => ExpressionEvaluator::evaluate(map_expr, self)?,
192                None => self
193                    .get_variable(variable)
194                    .unwrap_or(Value::Null(NullType::Null)),
195            };
196            results.push(mapped);
197        }
198        Ok(Value::list(List::from(results)))
199    }
200
201    /// Evaluate a dynamic tag-property access `tag.property` where `tag` is
202    /// itself an expression.
203    ///
204    /// Generic default: evaluates the tag expression and performs property
205    /// access on the resulting vertex value.
206    fn evaluate_label_tag_property(
207        &mut self,
208        tag: &Expression,
209        property: &str,
210    ) -> Result<Value, ExpressionError>
211    where
212        Self: Sized,
213    {
214        let tag_value = ExpressionEvaluator::evaluate(tag, self)?;
215        crate::executor::expression::evaluator::collection_operations::CollectionOperationEvaluator::eval_property_access(
216            &tag_value,
217            property,
218        )
219    }
220
221    /// Evaluate a predicate expression `func(variable IN list WHERE predicate)`
222    /// with `func` in {ALL, ANY, SINGLE, NONE}.
223    ///
224    /// Generic default: iterates the collection, binds `variable`, and applies
225    /// the predicate with the standard Cypher quantifier semantics.
226    fn evaluate_predicate(
227        &mut self,
228        func: &str,
229        args: &[Expression],
230    ) -> Result<Value, ExpressionError>
231    where
232        Self: Sized,
233    {
234        let func_upper = func.to_uppercase();
235        let variable = match args.first() {
236            Some(Expression::Variable(name)) => name.clone(),
237            _ => {
238                return Err(ExpressionError::type_error(format!(
239                    "Predicate `{}` requires a variable as its first argument",
240                    func
241                )))
242            }
243        };
244        let source = args
245            .get(1)
246            .ok_or_else(|| ExpressionError::argument_count_error(3, args.len()))?;
247        let predicate = args
248            .get(2)
249            .ok_or_else(|| ExpressionError::argument_count_error(3, args.len()))?;
250
251        let source_value = ExpressionEvaluator::evaluate(source, self)?;
252        let elements = match source_value {
253            Value::List(list) => list.values,
254            Value::Null(_) => return Ok(Value::Null(NullType::Null)),
255            other => {
256                return Err(ExpressionError::type_error(format!(
257                    "Predicate `{}` source must be a list, got {:?}",
258                    func,
259                    other.get_type()
260                )))
261            }
262        };
263
264        let mut matched = 0usize;
265        let total = elements.len();
266        for element in elements {
267            self.set_variable(variable.clone(), element);
268            match ExpressionEvaluator::evaluate(predicate, self)? {
269                Value::Bool(true) => matched += 1,
270                Value::Bool(false) | Value::Null(_) => {}
271                other => {
272                    return Err(ExpressionError::type_error(format!(
273                        "Predicate `{}` condition must evaluate to a boolean, got {:?}",
274                        func,
275                        other.get_type()
276                    )))
277                }
278            }
279        }
280        match func_upper.as_str() {
281            // ALL: every element satisfies the predicate; vacuously true for
282            // an empty collection.
283            "ALL" => Ok(Value::Bool(matched == total)),
284            // ANY: at least one element satisfies the predicate.
285            "ANY" => Ok(Value::Bool(matched > 0)),
286            // SINGLE: exactly one element satisfies the predicate.
287            "SINGLE" => Ok(Value::Bool(matched == 1)),
288            // NONE: no element satisfies the predicate.
289            "NONE" => Ok(Value::Bool(matched == 0)),
290            _ => Err(ExpressionError::type_error(format!(
291                "Unknown predicate function: {}",
292                func
293            ))),
294        }
295    }
296
297    /// Evaluate a REDUCE expression
298    /// `reduce(acc = initial, variable IN source | mapping)`.
299    ///
300    /// Generic default: seeds `accumulator` with `initial`, iterates the
301    /// source collection binding `variable`, and accumulates the result of
302    /// evaluating `mapping` at each step.
303    fn evaluate_reduce(
304        &mut self,
305        accumulator: &str,
306        initial: &Expression,
307        variable: &str,
308        source: &Expression,
309        mapping: &Expression,
310    ) -> Result<Value, ExpressionError>
311    where
312        Self: Sized,
313    {
314        let mut acc = ExpressionEvaluator::evaluate(initial, self)?;
315        let source_value = ExpressionEvaluator::evaluate(source, self)?;
316        let elements = match source_value {
317            Value::List(list) => list.values,
318            Value::Null(_) => return Ok(Value::Null(NullType::Null)),
319            other => {
320                return Err(ExpressionError::type_error(format!(
321                    "REDUCE source must be a list, got {:?}",
322                    other.get_type()
323                )))
324            }
325        };
326        for element in elements {
327            self.set_variable(variable.to_string(), element);
328            self.set_variable(accumulator.to_string(), acc.clone());
329            acc = ExpressionEvaluator::evaluate(mapping, self)?;
330        }
331        Ok(acc)
332    }
333
334    /// Evaluate a path construction expression `path(v1, e1, v2, e2, v3)`.
335    ///
336    /// Generic default: evaluates each element and assembles a [`Path`] from
337    /// an alternating vertex/edge sequence starting with a vertex.
338    fn evaluate_path_build(&mut self, items: &[Expression]) -> Result<Value, ExpressionError>
339    where
340        Self: Sized,
341    {
342        let mut iter = items.iter();
343        let src_value = match iter.next() {
344            Some(expr) => ExpressionEvaluator::evaluate(expr, self)?,
345            None => {
346                return Err(ExpressionError::path_error(
347                    "Path construction requires at least one vertex",
348                ))
349            }
350        };
351        let Value::Vertex(src) = src_value else {
352            return Err(ExpressionError::path_error(
353                "Path construction must start with a vertex",
354            ));
355        };
356        let mut path = Path::new(*src);
357        let mut remaining = iter;
358        while let Some(edge_expr) = remaining.next() {
359            let vertex_expr = remaining.next().ok_or_else(|| {
360                ExpressionError::path_error(
361                    "Path construction requires alternating edge/vertex elements",
362                )
363            })?;
364            let edge_value = ExpressionEvaluator::evaluate(edge_expr, self)?;
365            let vertex_value = ExpressionEvaluator::evaluate(vertex_expr, self)?;
366            let Value::Edge(edge) = edge_value else {
367                return Err(ExpressionError::path_error(
368                    "Path construction edge element must be an edge",
369                ));
370            };
371            let Value::Vertex(vertex) = vertex_value else {
372                return Err(ExpressionError::path_error(
373                    "Path construction vertex element must be a vertex",
374                ));
375            };
376            path.add_step(Step { dst: vertex, edge });
377        }
378        Ok(Value::Path(Box::new(path)))
379    }
380}
381
382#[cfg(test)]
383mod tests {
384    use super::*;
385    use crate::executor::expression::evaluation_context::DefaultExpressionContext;
386    use linkrs_core::types::expr::Expression;
387    use linkrs_core::types::VertexId;
388    use linkrs_core::vertex_edge_path::{Edge, Tag};
389    use std::collections::HashMap;
390
391    fn context() -> DefaultExpressionContext {
392        DefaultExpressionContext::new()
393    }
394
395    fn test_vertex() -> Value {
396        let tag = Tag::new(
397            "person".to_string(),
398            HashMap::from([("name".into(), Value::string("Alice"))]),
399        );
400        Value::Vertex(Box::new(linkrs_core::vertex_edge_path::Vertex::new(
401            VertexId::try_from_int64(1).expect("valid vertex id"),
402            tag,
403        )))
404    }
405
406    fn test_edge() -> Value {
407        Value::Edge(Box::new(Edge::new(
408            VertexId::try_from_int64(1).expect("valid vertex id"),
409            VertexId::try_from_int64(2).expect("valid vertex id"),
410            "knows".to_string(),
411            0,
412            HashMap::new(),
413        )))
414    }
415
416    #[test]
417    fn evaluate_label_default_reports_label() {
418        let mut ctx = context();
419        let err = ExpressionEvaluator::evaluate(&Expression::label("Person"), &mut ctx)
420            .expect_err("label expression cannot be resolved without context");
421        assert!(
422            err.message.contains("Person"),
423            "error should name the label: {}",
424            err.message
425        );
426    }
427
428    #[test]
429    fn evaluate_list_comprehension_with_filter_and_map() {
430        let mut ctx = context();
431        // [x IN [1,2,3,4] WHERE x > 2 | x * 10]
432        let expr = Expression::list_comprehension(
433            "x",
434            Expression::list(vec![
435                Expression::Literal(Value::Int(1)),
436                Expression::Literal(Value::Int(2)),
437                Expression::Literal(Value::Int(3)),
438                Expression::Literal(Value::Int(4)),
439            ]),
440            Some(Expression::binary(
441                Expression::variable("x"),
442                linkrs_core::types::operators::BinaryOperator::GreaterThan,
443                Expression::Literal(Value::Int(2)),
444            )),
445            Some(Expression::binary(
446                Expression::variable("x"),
447                linkrs_core::types::operators::BinaryOperator::Multiply,
448                Expression::Literal(Value::Int(10)),
449            )),
450        );
451        let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("comprehension");
452        assert_eq!(
453            result,
454            Value::list(List::from(vec![Value::Int(30), Value::Int(40)]))
455        );
456    }
457
458    #[test]
459    fn evaluate_list_comprehension_empty_and_null() {
460        let mut ctx = context();
461        // Empty source list yields an empty result list.
462        let empty = Expression::list_comprehension(
463            "x",
464            Expression::list(Vec::new()),
465            None,
466            Some(Expression::variable("x")),
467        );
468        assert_eq!(
469            ExpressionEvaluator::evaluate(&empty, &mut ctx).expect("empty comprehension"),
470            Value::list(List::from(Vec::<Value>::new()))
471        );
472        // Null source propagates null.
473        let null_src = Expression::list_comprehension(
474            "x",
475            Expression::Literal(Value::Null(NullType::Null)),
476            None,
477            None,
478        );
479        assert_eq!(
480            ExpressionEvaluator::evaluate(&null_src, &mut ctx).expect("null comprehension"),
481            Value::Null(NullType::Null)
482        );
483        // Non-list source is a type error.
484        let bad =
485            Expression::list_comprehension("x", Expression::Literal(Value::Int(5)), None, None);
486        assert!(ExpressionEvaluator::evaluate(&bad, &mut ctx).is_err());
487    }
488
489    #[test]
490    fn evaluate_predicate_quantifiers() {
491        let list = Expression::list(vec![
492            Expression::Literal(Value::Int(1)),
493            Expression::Literal(Value::Int(2)),
494            Expression::Literal(Value::Int(3)),
495        ]);
496        let gt_two = Expression::binary(
497            Expression::variable("x"),
498            linkrs_core::types::operators::BinaryOperator::GreaterThan,
499            Expression::Literal(Value::Int(2)),
500        );
501        let args = vec![Expression::variable("x"), list.clone(), gt_two.clone()];
502        let mut ctx = context();
503        let all =
504            ExpressionEvaluator::evaluate(&Expression::predicate("all", args.clone()), &mut ctx)
505                .expect("all");
506        assert_eq!(all, Value::Bool(false));
507
508        let mut ctx = context();
509        let any =
510            ExpressionEvaluator::evaluate(&Expression::predicate("any", args.clone()), &mut ctx)
511                .expect("any");
512        assert_eq!(any, Value::Bool(true));
513
514        let mut ctx = context();
515        let single =
516            ExpressionEvaluator::evaluate(&Expression::predicate("single", args.clone()), &mut ctx)
517                .expect("single");
518        assert_eq!(single, Value::Bool(true));
519
520        let mut ctx = context();
521        let none =
522            ExpressionEvaluator::evaluate(&Expression::predicate("none", args.clone()), &mut ctx)
523                .expect("none");
524        assert_eq!(none, Value::Bool(false));
525
526        // All elements satisfy x > 0.
527        let gt_zero = Expression::binary(
528            Expression::variable("x"),
529            linkrs_core::types::operators::BinaryOperator::GreaterThan,
530            Expression::Literal(Value::Int(0)),
531        );
532        let mut ctx = context();
533        let all_true = ExpressionEvaluator::evaluate(
534            &Expression::predicate(
535                "all",
536                vec![Expression::variable("x"), list.clone(), gt_zero],
537            ),
538            &mut ctx,
539        )
540        .expect("all true");
541        assert_eq!(all_true, Value::Bool(true));
542
543        // Unknown quantifier is an error.
544        let mut ctx = context();
545        assert!(
546            ExpressionEvaluator::evaluate(&Expression::predicate("maybe", args), &mut ctx,)
547                .is_err()
548        );
549    }
550
551    #[test]
552    fn evaluate_reduce_accumulates() {
553        let mut ctx = context();
554        // reduce(acc = 0, x IN [1,2,3] | acc + x)
555        let expr = Expression::reduce(
556            "acc",
557            Expression::Literal(Value::Int(0)),
558            "x",
559            Expression::list(vec![
560                Expression::Literal(Value::Int(1)),
561                Expression::Literal(Value::Int(2)),
562                Expression::Literal(Value::Int(3)),
563            ]),
564            Expression::binary(
565                Expression::variable("acc"),
566                linkrs_core::types::operators::BinaryOperator::Add,
567                Expression::variable("x"),
568            ),
569        );
570        let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("reduce");
571        assert_eq!(result, Value::Int(6));
572    }
573
574    #[test]
575    fn evaluate_label_tag_property_accesses_vertex_property() {
576        let mut ctx = context().add_variable("n".into(), test_vertex());
577        // Dynamic tag access: (n).name
578        let expr = Expression::label_tag_property(Expression::variable("n"), "name");
579        let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("tag property");
580        assert_eq!(result, Value::string("Alice"));
581    }
582
583    #[test]
584    fn evaluate_path_build_assembles_path() {
585        let mut ctx = context();
586        let expr = Expression::path_build(vec![
587            Expression::Literal(test_vertex()),
588            Expression::Literal(test_edge()),
589            Expression::Literal(test_vertex()),
590        ]);
591        let result = ExpressionEvaluator::evaluate(&expr, &mut ctx).expect("path build");
592        let Value::Path(path) = result else {
593            panic!("expected a path value");
594        };
595        assert_eq!(path.len(), 1);
596        assert_eq!(
597            path.src.vid,
598            VertexId::try_from_int64(1).expect("valid vertex id")
599        );
600
601        // Non-vertex start is a path error.
602        let bad = Expression::path_build(vec![Expression::Literal(Value::Int(1))]);
603        let err = ExpressionEvaluator::evaluate(&bad, &mut ctx).expect_err("path build error");
604        assert!(err.message.contains("vertex"), "{}", err.message);
605    }
606}