1use 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
19pub trait ExpressionContext {
26 fn get_variable(&self, name: &str) -> Option<Value>;
28
29 fn get_parameter(&self, _name: &str) -> Option<Value> {
33 None
34 }
35
36 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 fn get_variable_by_slot(&self, _slot: SlotId) -> Option<Value> {
51 None
52 }
53
54 fn set_variable(&mut self, name: String, value: Value);
56
57 fn get_function(&self, name: &str) -> Option<OwnedFunctionRef> {
59 let _ = name;
60 None
61 }
62
63 fn get_graph_storage(&self) -> Option<GraphStorageRef> {
65 None
66 }
67
68 fn get_sequence_manager(
72 &self,
73 ) -> Option<std::sync::Arc<linkrs_core::metadata::SequenceManager>> {
74 None
75 }
76
77 fn supports_cache(&self) -> bool {
79 false
80 }
81
82 fn get_cache(&mut self) -> Option<&mut ()> {
86 None
87 }
88
89 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 fn execute_exists(&mut self, body: &SubqueryBody) -> Result<bool, ExpressionError> {
106 let results = self.execute_subquery(body)?;
107 Ok(!results.is_empty())
108 }
109 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 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 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 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 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 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" => Ok(Value::Bool(matched == total)),
284 "ANY" => Ok(Value::Bool(matched > 0)),
286 "SINGLE" => Ok(Value::Bool(matched == 1)),
288 "NONE" => Ok(Value::Bool(matched == 0)),
290 _ => Err(ExpressionError::type_error(format!(
291 "Unknown predicate function: {}",
292 func
293 ))),
294 }
295 }
296
297 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 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 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 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 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 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 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 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 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 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 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}