Skip to main content

linkrs_query/executor/expression/evaluator/
operations.rs

1use crate::executor::expression::evaluator::collection_operations::CollectionOperationEvaluator;
2use crate::executor::expression::ExpressionError;
3use linkrs_core::types::operators::{BinaryOperator, UnaryOperator};
4use linkrs_core::value::list::List;
5/// Arithmetic and logical operations module
6///
7/// This module is responsible for handling basic arithmetic operations, comparison operations, logical operations, and other fundamental operations involved in the evaluation of expressions.
8use linkrs_core::value::Value;
9
10/// Binary operation evaluator
11pub struct BinaryOperationEvaluator;
12
13impl BinaryOperationEvaluator {
14    /// Evaluating binary operations
15    pub fn evaluate(
16        left: &Value,
17        op: &BinaryOperator,
18        right: &Value,
19    ) -> Result<Value, ExpressionError> {
20        match op {
21            // Arithmetic operations
22            BinaryOperator::Add => Self::eval_add(left, right),
23            BinaryOperator::Subtract => Self::eval_subtract(left, right),
24            BinaryOperator::Multiply => Self::eval_multiply(left, right),
25            BinaryOperator::Divide => Self::eval_divide(left, right),
26            BinaryOperator::Modulo => Self::eval_modulo(left, right),
27            BinaryOperator::Exponent => Self::eval_exponent(left, right),
28
29            // Comparison operations
30            BinaryOperator::Equal => Self::eval_equal(left, right),
31            BinaryOperator::NotEqual => Self::eval_not_equal(left, right),
32            BinaryOperator::LessThan => Self::eval_less_than(left, right),
33            BinaryOperator::LessThanOrEqual => Self::eval_less_than_or_equal(left, right),
34            BinaryOperator::GreaterThan => Self::eval_greater_than(left, right),
35            BinaryOperator::GreaterThanOrEqual => Self::eval_greater_than_or_equal(left, right),
36
37            // Logical operations
38            BinaryOperator::And => Self::eval_and(left, right),
39            BinaryOperator::Or => Self::eval_or(left, right),
40            BinaryOperator::Xor => Self::eval_xor(left, right),
41
42            // String operations
43            BinaryOperator::StringConcat => Self::eval_string_concat(left, right),
44            BinaryOperator::Like => Self::eval_like(left, right),
45            BinaryOperator::In => Self::eval_in(left, right),
46            BinaryOperator::NotIn => Self::eval_not_in(left, right),
47
48            // JSONB operators
49            BinaryOperator::JsonGet => Self::eval_json_get(left, right),
50            BinaryOperator::JsonGetText => Self::eval_json_get_text(left, right),
51            BinaryOperator::JsonPathGet => Self::eval_json_path_get(left, right),
52            BinaryOperator::JsonPathGetText => Self::eval_json_path_get_text(left, right),
53            BinaryOperator::Contains => Self::eval_contains(left, right),
54            BinaryOperator::StartsWith => Self::eval_starts_with(left, right),
55            BinaryOperator::EndsWith => Self::eval_ends_with(left, right),
56
57            // Access operation – delegated to CollectionOperationEvaluator
58            BinaryOperator::Subscript => {
59                CollectionOperationEvaluator::eval_subscript_access(left, right)
60            }
61            BinaryOperator::Attribute => {
62                CollectionOperationEvaluator::eval_attribute_access(left, right)
63            }
64
65            // Set operations
66            BinaryOperator::Union => Self::eval_union(left, right),
67            BinaryOperator::Intersect => Self::eval_intersect(left, right),
68            BinaryOperator::Except => Self::eval_except(left, right),
69
70            // Bitwise operations
71            BinaryOperator::BitwiseOr => left.bit_or(right).map_err(ExpressionError::runtime_error),
72            BinaryOperator::BitwiseAnd => {
73                left.bit_and(right).map_err(ExpressionError::runtime_error)
74            }
75            BinaryOperator::ShiftLeft => {
76                left.bit_shl(right).map_err(ExpressionError::runtime_error)
77            }
78            BinaryOperator::ShiftRight => {
79                left.bit_shr(right).map_err(ExpressionError::runtime_error)
80            }
81        }
82    }
83
84    fn eval_add(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
85        left.add(right).map_err(ExpressionError::runtime_error)
86    }
87
88    fn eval_subtract(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
89        left.sub(right).map_err(ExpressionError::runtime_error)
90    }
91
92    fn eval_multiply(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
93        left.mul(right).map_err(ExpressionError::runtime_error)
94    }
95
96    fn eval_divide(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
97        left.div(right).map_err(ExpressionError::runtime_error)
98    }
99
100    fn eval_modulo(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
101        left.rem(right).map_err(ExpressionError::runtime_error)
102    }
103
104    fn eval_exponent(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
105        left.pow(right).map_err(ExpressionError::runtime_error)
106    }
107
108    fn eval_equal(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
109        Ok(Value::Bool(left == right))
110    }
111
112    fn eval_not_equal(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
113        Ok(Value::Bool(left != right))
114    }
115
116    fn eval_less_than(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
117        Ok(Value::Bool(left < right))
118    }
119
120    fn eval_less_than_or_equal(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
121        Ok(Value::Bool(left <= right))
122    }
123
124    fn eval_greater_than(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
125        Ok(Value::Bool(left > right))
126    }
127
128    fn eval_greater_than_or_equal(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
129        Ok(Value::Bool(left >= right))
130    }
131
132    fn eval_and(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
133        left.and(right).map_err(ExpressionError::runtime_error)
134    }
135
136    fn eval_or(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
137        left.or(right).map_err(ExpressionError::runtime_error)
138    }
139
140    fn eval_xor(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
141        match (left, right) {
142            (Value::Bool(l), Value::Bool(r)) => Ok(Value::Bool(*l ^ *r)),
143            _ => Err(ExpressionError::type_error(
144                "The XOR operation requires boolean values.",
145            )),
146        }
147    }
148
149    fn eval_string_concat(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
150        left.add(right).map_err(ExpressionError::runtime_error)
151    }
152
153    fn eval_like(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
154        if left.is_bad_null() || right.is_bad_null() {
155            return Ok(Value::Null(linkrs_core::value::NullType::BadData));
156        }
157
158        if (!left.is_null() && !left.is_empty() && !matches!(left, Value::String(_)))
159            || (!right.is_null() && !right.is_empty() && !matches!(right, Value::String(_)))
160        {
161            return Ok(Value::Null(linkrs_core::value::NullType::BadData));
162        }
163
164        if left.is_null() || right.is_null() {
165            return Ok(Value::Null(linkrs_core::value::NullType::Null));
166        }
167
168        match (left, right) {
169            (Value::String(l), Value::String(r)) => Self::eval_like_operation(l, r),
170            _ => Ok(Value::Null(linkrs_core::value::NullType::BadData)),
171        }
172    }
173
174    fn eval_like_operation(pattern: &str, text: &str) -> Result<Value, ExpressionError> {
175        let mut pattern_chars = pattern.chars().peekable();
176        let mut text_chars = text.chars().peekable();
177
178        while let Some(p) = pattern_chars.next() {
179            match p {
180                '%' => {
181                    let remaining_pattern: String = pattern_chars.collect();
182                    let remaining_text: String = text_chars.collect();
183
184                    if remaining_pattern.is_empty() {
185                        return Ok(Value::Bool(true));
186                    }
187
188                    for i in 0..=remaining_text.len() {
189                        if let Value::Bool(b) =
190                            Self::eval_like_operation(&remaining_pattern, &remaining_text[i..])?
191                        {
192                            if b {
193                                return Ok(Value::Bool(true));
194                            }
195                        }
196                    }
197
198                    return Ok(Value::Bool(false));
199                }
200                '_' => {
201                    if text_chars.next().is_none() {
202                        return Ok(Value::Bool(false));
203                    }
204                }
205                '\\' => {
206                    if let Some(escaped_char) = pattern_chars.next() {
207                        if text_chars.next() != Some(escaped_char) {
208                            return Ok(Value::Bool(false));
209                        }
210                    }
211                }
212                c => {
213                    if text_chars.next() != Some(c) {
214                        return Ok(Value::Bool(false));
215                    }
216                }
217            }
218        }
219
220        Ok(Value::Bool(text_chars.next().is_none()))
221    }
222
223    fn eval_in(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
224        if left.is_null() || right.is_null() {
225            return Ok(Value::Null(linkrs_core::value::NullType::Null));
226        }
227
228        match right {
229            Value::List(items) => {
230                if items.iter().any(|item| item.is_null()) {
231                    return Ok(Value::Null(linkrs_core::value::NullType::Null));
232                }
233                Ok(Value::Bool(items.contains(left)))
234            }
235            _ => Err(ExpressionError::type_error(
236                "The right side of the IN operation must be a list.",
237            )),
238        }
239    }
240
241    fn eval_not_in(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
242        if left.is_null() || right.is_null() {
243            return Ok(Value::Null(linkrs_core::value::NullType::Null));
244        }
245
246        match right {
247            Value::List(items) => {
248                if items.iter().any(|item| item.is_null()) {
249                    return Ok(Value::Null(linkrs_core::value::NullType::Null));
250                }
251                Ok(Value::Bool(!items.contains(left)))
252            }
253            _ => Err(ExpressionError::type_error(
254                "The right side of the NOT IN operation must be a list.",
255            )),
256        }
257    }
258
259    fn eval_contains(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
260        if left.is_null() || right.is_null() {
261            return Ok(Value::Null(linkrs_core::value::NullType::Null));
262        }
263
264        match (&left, &right) {
265            (Value::String(s), Value::String(sub)) => Ok(Value::Bool(s.contains(&**sub))),
266            (Value::List(items), item) => {
267                if items.iter().any(|i| i.is_null()) {
268                    return Ok(Value::Null(linkrs_core::value::NullType::Null));
269                }
270                Ok(Value::Bool(items.contains(item)))
271            }
272            _ => Err(ExpressionError::type_error(
273                "The CONTAINS operation requires a string or a list.",
274            )),
275        }
276    }
277
278    fn eval_starts_with(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
279        if left.is_null() || right.is_null() {
280            return Ok(Value::Null(linkrs_core::value::NullType::Null));
281        }
282
283        match (&left, &right) {
284            (Value::String(s), Value::String(prefix)) => Ok(Value::Bool(s.starts_with(&**prefix))),
285            _ => Err(ExpressionError::type_error(
286                "The `STARTS WITH` operation requires a string value.",
287            )),
288        }
289    }
290
291    fn eval_ends_with(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
292        if left.is_null() || right.is_null() {
293            return Ok(Value::Null(linkrs_core::value::NullType::Null));
294        }
295
296        match (&left, &right) {
297            (Value::String(s), Value::String(suffix)) => Ok(Value::Bool(s.ends_with(&**suffix))),
298            _ => Err(ExpressionError::type_error(
299                "The ENDS WITH operation requires a string value.",
300            )),
301        }
302    }
303
304    fn eval_union(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
305        if left.is_null() || right.is_null() {
306            return Ok(Value::Null(linkrs_core::value::NullType::Null));
307        }
308
309        match (left, right) {
310            (Value::List(l), Value::List(r)) => {
311                if l.iter().any(|item| item.is_null()) || r.iter().any(|item| item.is_null()) {
312                    return Ok(Value::Null(linkrs_core::value::NullType::Null));
313                }
314                let mut result = (**l).clone();
315                result.extend(r.iter().cloned());
316                Ok(Value::list(result))
317            }
318            _ => Err(ExpressionError::type_error("UNION requires list values")),
319        }
320    }
321
322    fn eval_intersect(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
323        if left.is_null() || right.is_null() {
324            return Ok(Value::Null(linkrs_core::value::NullType::Null));
325        }
326
327        match (left, right) {
328            (Value::List(l), Value::List(r)) => {
329                if l.iter().any(|item| item.is_null()) || r.iter().any(|item| item.is_null()) {
330                    return Ok(Value::Null(linkrs_core::value::NullType::Null));
331                }
332                let result: Vec<Value> =
333                    l.iter().filter(|item| r.contains(item)).cloned().collect();
334                Ok(Value::list(List::from(result)))
335            }
336            _ => Err(ExpressionError::type_error(
337                "INTERSECT requires list values",
338            )),
339        }
340    }
341
342    fn eval_except(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
343        if left.is_null() || right.is_null() {
344            return Ok(Value::Null(linkrs_core::value::NullType::Null));
345        }
346
347        match (left, right) {
348            (Value::List(l), Value::List(r)) => {
349                if l.iter().any(|item| item.is_null()) || r.iter().any(|item| item.is_null()) {
350                    return Ok(Value::Null(linkrs_core::value::NullType::Null));
351                }
352                let result: Vec<Value> =
353                    l.iter().filter(|item| !r.contains(item)).cloned().collect();
354                Ok(Value::list(List::from(result)))
355            }
356            _ => Err(ExpressionError::type_error("EXCEPT requires list values")),
357        }
358    }
359
360    // JSONB operator implementations
361    fn eval_json_get(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
362        Self::eval_json_operator(left, right, false, false)
363    }
364
365    fn eval_json_get_text(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
366        Self::eval_json_operator(left, right, true, false)
367    }
368
369    fn eval_json_path_get(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
370        Self::eval_json_operator(left, right, false, true)
371    }
372
373    fn eval_json_path_get_text(left: &Value, right: &Value) -> Result<Value, ExpressionError> {
374        Self::eval_json_operator(left, right, true, true)
375    }
376
377    fn eval_json_operator(
378        json: &Value,
379        key_or_path: &Value,
380        as_text: bool,
381        is_path: bool,
382    ) -> Result<Value, ExpressionError> {
383        use linkrs_core::value::NullType;
384        use serde_json::Value as JsonValue;
385
386        if json.is_null() || key_or_path.is_null() {
387            return Ok(Value::Null(NullType::Null));
388        }
389
390        let json_value = match json {
391            Value::String(s) => serde_json::from_str(s)
392                .map_err(|e| ExpressionError::type_error(format!("Invalid JSON string: {}", e))),
393            Value::Json(j) => j
394                .to_value()
395                .map_err(|e| ExpressionError::type_error(format!("Invalid JSON: {}", e))),
396            Value::JsonB(j) => Ok(j.as_value().clone()),
397            _ => Err(ExpressionError::type_error(
398                "JSON operator requires JSON/JSONB or JSON string",
399            )),
400        }?;
401
402        let key_str = match key_or_path {
403            Value::String(s) => s.to_string(),
404            _ => {
405                return Err(ExpressionError::type_error(
406                    "JSON operator key/path must be a string",
407                ))
408            }
409        };
410
411        let result = if is_path {
412            // Handle JSON path (e.g., #>, #>>)
413            let path_segments: Vec<&str> = if key_str.starts_with('$') {
414                key_str.split('.').collect()
415            } else {
416                return Err(ExpressionError::type_error("JSON path must start with '$'"));
417            };
418
419            let mut current = &json_value;
420            for segment in path_segments.iter().skip(1) {
421                match current {
422                    JsonValue::Object(map) => {
423                        if let Some(v) = map.get(*segment) {
424                            current = v;
425                        } else {
426                            return Ok(Value::Null(NullType::Null));
427                        }
428                    }
429                    JsonValue::Array(arr) => {
430                        if let Ok(index) = segment.parse::<usize>() {
431                            if index < arr.len() {
432                                current = &arr[index];
433                            } else {
434                                return Ok(Value::Null(NullType::Null));
435                            }
436                        } else {
437                            return Ok(Value::Null(NullType::Null));
438                        }
439                    }
440                    _ => return Ok(Value::Null(NullType::Null)),
441                }
442            }
443            current.clone()
444        } else {
445            // Handle simple key access (e.g., ->, ->>)
446            match &json_value {
447                JsonValue::Object(map) => map.get(&key_str).cloned().unwrap_or(JsonValue::Null),
448                JsonValue::Array(arr) => {
449                    if let Ok(index) = key_str.parse::<usize>() {
450                        if index < arr.len() {
451                            arr[index].clone()
452                        } else {
453                            JsonValue::Null
454                        }
455                    } else {
456                        JsonValue::Null
457                    }
458                }
459                _ => JsonValue::Null,
460            }
461        };
462
463        // Convert result to appropriate Linkrs Value
464        let value_result = if as_text {
465            // Return as string
466            match result {
467                JsonValue::Null => Value::Null(NullType::Null),
468                JsonValue::String(s) => Value::string(s),
469                JsonValue::Number(n) => {
470                    if let Some(i) = n.as_i64() {
471                        Value::BigInt(i)
472                    } else if let Some(f) = n.as_f64() {
473                        Value::Double(f)
474                    } else {
475                        Value::string(n.to_string())
476                    }
477                }
478                JsonValue::Bool(b) => Value::Bool(b),
479                JsonValue::Array(_) | JsonValue::Object(_) => {
480                    Value::string(serde_json::to_string(&result).unwrap_or_default())
481                }
482            }
483        } else {
484            // Return as JSON value
485            match result {
486                JsonValue::Null => Value::Null(NullType::Null),
487                JsonValue::String(s) => Value::string(s),
488                JsonValue::Number(n) => {
489                    if let Some(i) = n.as_i64() {
490                        Value::BigInt(i)
491                    } else if let Some(f) = n.as_f64() {
492                        Value::Double(f)
493                    } else {
494                        Value::string(n.to_string())
495                    }
496                }
497                JsonValue::Bool(b) => Value::Bool(b),
498                JsonValue::Array(arr) => {
499                    let items: Vec<Value> = arr
500                        .into_iter()
501                        .map(|v| match v {
502                            JsonValue::Null => Value::Null(NullType::Null),
503                            JsonValue::String(s) => Value::string(s),
504                            JsonValue::Number(n) => {
505                                if let Some(i) = n.as_i64() {
506                                    Value::BigInt(i)
507                                } else if let Some(f) = n.as_f64() {
508                                    Value::Double(f)
509                                } else {
510                                    Value::string(n.to_string())
511                                }
512                            }
513                            JsonValue::Bool(b) => Value::Bool(b),
514                            JsonValue::Array(_) | JsonValue::Object(_) => {
515                                Value::string(serde_json::to_string(&v).unwrap_or_default())
516                            }
517                        })
518                        .collect();
519                    Value::List(Box::new(linkrs_core::value::list::List::from(items)))
520                }
521                JsonValue::Object(_) => {
522                    Value::string(serde_json::to_string(&result).unwrap_or_default())
523                }
524            }
525        };
526
527        Ok(value_result)
528    }
529}
530
531/// One-element operation evaluator
532pub struct UnaryOperationEvaluator;
533
534impl UnaryOperationEvaluator {
535    /// Evaluating a unary operation
536    pub fn evaluate(op: &UnaryOperator, value: &Value) -> Result<Value, ExpressionError> {
537        match op {
538            // Arithmetic operations
539            UnaryOperator::Plus => Self::eval_plus(value),
540            UnaryOperator::Minus => Self::eval_minus(value),
541
542            // Logical operations
543            UnaryOperator::Not => Self::eval_not(value),
544
545            // Existence check
546            UnaryOperator::IsNull => Self::eval_is_null(value),
547            UnaryOperator::IsNotNull => Self::eval_is_not_null(value),
548            UnaryOperator::IsEmpty => Self::eval_is_empty(value),
549            UnaryOperator::IsNotEmpty => Self::eval_is_not_empty(value),
550        }
551    }
552
553    fn eval_plus(value: &Value) -> Result<Value, ExpressionError> {
554        Ok(value.clone())
555    }
556
557    fn eval_minus(value: &Value) -> Result<Value, ExpressionError> {
558        value.neg().map_err(ExpressionError::runtime_error)
559    }
560
561    fn eval_not(value: &Value) -> Result<Value, ExpressionError> {
562        value.not().map_err(ExpressionError::runtime_error)
563    }
564
565    fn eval_is_null(value: &Value) -> Result<Value, ExpressionError> {
566        Ok(Value::Bool(value.is_null()))
567    }
568
569    fn eval_is_not_null(value: &Value) -> Result<Value, ExpressionError> {
570        Ok(Value::Bool(!value.is_null()))
571    }
572
573    fn eval_is_empty(value: &Value) -> Result<Value, ExpressionError> {
574        match value {
575            Value::String(s) => Ok(Value::Bool(s.is_empty())),
576            Value::List(l) => Ok(Value::Bool(l.is_empty())),
577            Value::Map(m) => Ok(Value::Bool(m.is_empty())),
578            _ => Err(ExpressionError::type_error(
579                "The EMPTY check requires the container type.",
580            )),
581        }
582    }
583
584    fn eval_is_not_empty(value: &Value) -> Result<Value, ExpressionError> {
585        match value {
586            Value::String(s) => Ok(Value::Bool(!s.is_empty())),
587            Value::List(l) => Ok(Value::Bool(!l.is_empty())),
588            Value::Map(m) => Ok(Value::Bool(!m.is_empty())),
589            _ => Err(ExpressionError::type_error(
590                "The EMPTY check requires the container type.",
591            )),
592        }
593    }
594}