1use anyhow::{Result, anyhow};
10use std::cmp::Ordering;
11use uni_common::{TemporalValue, Value};
12
13use crate::query::datetime::{
14 CypherDuration, TemporalType, add_cypher_duration_to_date, add_cypher_duration_to_datetime,
15 add_cypher_duration_to_localdatetime, add_cypher_duration_to_localtime,
16 add_cypher_duration_to_time, classify_temporal, eval_datetime_function, is_duration_value,
17 parse_datetime_utc, parse_duration_from_value, parse_duration_to_cypher,
18};
19use crate::query::spatial::eval_spatial_function;
20use uni_cypher::ast::BinaryOp;
21
22pub fn eval_binary_op(left: &Value, op: &BinaryOp, right: &Value) -> Result<Value> {
27 if !matches!(op, BinaryOp::And | BinaryOp::Or) && (left.is_null() || right.is_null()) {
29 return Ok(Value::Null);
30 }
31
32 match op {
33 BinaryOp::Eq => Ok(match cypher_eq(left, right) {
34 Some(b) => Value::Bool(b),
35 None => Value::Null,
36 }),
37 BinaryOp::NotEq => Ok(match cypher_eq(left, right) {
38 Some(b) => Value::Bool(!b),
39 None => Value::Null,
40 }),
41 BinaryOp::And => {
42 match (left.as_bool(), right.as_bool()) {
44 (Some(false), _) | (_, Some(false)) => Ok(Value::Bool(false)),
45 (Some(true), Some(true)) => Ok(Value::Bool(true)),
46 _ if left.is_null() || right.is_null() => Ok(Value::Null),
47 _ => Err(anyhow!(
48 "InvalidArgumentType: Expected bool for AND operands"
49 )),
50 }
51 }
52 BinaryOp::Or => {
53 match (left.as_bool(), right.as_bool()) {
55 (Some(true), _) | (_, Some(true)) => Ok(Value::Bool(true)),
56 (Some(false), Some(false)) => Ok(Value::Bool(false)),
57 _ if left.is_null() || right.is_null() => Ok(Value::Null),
58 _ => Err(anyhow!(
59 "InvalidArgumentType: Expected bool for OR operands"
60 )),
61 }
62 }
63 BinaryOp::Xor => {
64 match (left.as_bool(), right.as_bool()) {
66 (Some(l), Some(r)) => Ok(Value::Bool(l ^ r)),
67 _ if left.is_null() || right.is_null() => Ok(Value::Null),
68 _ => Err(anyhow!(
69 "InvalidArgumentType: Expected bool for XOR operands"
70 )),
71 }
72 }
73 BinaryOp::Gt => eval_comparison(left, right, |ordering| ordering.is_gt()),
74 BinaryOp::Lt => eval_comparison(left, right, |ordering| ordering.is_lt()),
75 BinaryOp::GtEq => eval_comparison(left, right, |ordering| ordering.is_ge()),
76 BinaryOp::LtEq => eval_comparison(left, right, |ordering| ordering.is_le()),
77 BinaryOp::Contains => eval_string_predicate(left, right, "CONTAINS", |l, r| l.contains(r)),
78 BinaryOp::StartsWith => {
79 eval_string_predicate(left, right, "STARTS WITH", |l, r| l.starts_with(r))
80 }
81 BinaryOp::EndsWith => {
82 eval_string_predicate(left, right, "ENDS WITH", |l, r| l.ends_with(r))
83 }
84 BinaryOp::Add => eval_add(left, right),
85 BinaryOp::Sub => eval_sub(left, right),
86 BinaryOp::Mul => eval_mul(left, right),
87 BinaryOp::Div => eval_div(left, right),
88 BinaryOp::Mod => eval_numeric_op(left, right, |a, b| a % b),
89 BinaryOp::Pow => eval_numeric_op(left, right, |a, b| a.powf(b)),
90 BinaryOp::Regex => {
91 let l = left
92 .as_str()
93 .ok_or_else(|| anyhow!("Left operand of =~ must be a string"))?;
94 let pattern = right
95 .as_str()
96 .ok_or_else(|| anyhow!("Right operand of =~ must be a regex pattern string"))?;
97 let re = regex::Regex::new(pattern)
98 .map_err(|e| anyhow!("Invalid regex pattern '{}': {}", pattern, e))?;
99 Ok(Value::Bool(re.is_match(l)))
100 }
101 BinaryOp::ApproxEq => eval_vector_similarity(left, right),
102 }
103}
104
105pub fn cypher_eq(left: &Value, right: &Value) -> Option<bool> {
108 if left.is_null() || right.is_null() {
109 return None;
110 }
111
112 if let (Some(l), Some(r)) = (left.as_i64(), right.as_i64()) {
114 return Some(l == r);
115 }
116
117 if let (Some(l), Some(r)) = (left.as_f64(), right.as_f64()) {
119 if l.is_nan() || r.is_nan() {
120 return Some(false);
121 }
122 return Some(l == r);
123 }
124
125 if let (Value::List(l), Value::List(r)) = (left, right) {
127 if l.len() != r.len() {
128 return Some(false);
129 }
130 let mut has_null = false;
131 for (lv, rv) in l.iter().zip(r.iter()) {
132 match cypher_eq(lv, rv) {
133 Some(false) => return Some(false),
134 None => has_null = true,
135 Some(true) => {}
136 }
137 }
138 return if has_null { None } else { Some(true) };
139 }
140
141 if let (Value::Map(l), Value::Map(r)) = (left, right) {
143 if let (Some(vid_l), Some(vid_r)) = (l.get("_vid"), r.get("_vid")) {
145 return Some(vid_l == vid_r);
146 }
147 if let (Some(eid_l), Some(eid_r)) = (l.get("_eid"), r.get("_eid")) {
149 return Some(eid_l == eid_r);
150 }
151
152 if l.len() != r.len() {
153 return Some(false);
154 }
155
156 let mut has_null = false;
157 for (k, lv) in l {
158 if let Some(rv) = r.get(k) {
159 match cypher_eq(lv, rv) {
160 Some(false) => return Some(false),
161 None => has_null = true,
162 Some(true) => {}
163 }
164 } else {
165 return Some(false);
166 }
167 }
168 return if has_null { None } else { Some(true) };
169 }
170
171 Some(left == right)
173}
174
175pub fn eval_in_op(left: &Value, right: &Value) -> Result<Value> {
177 if let Value::List(arr) = right {
178 let mut has_null = false;
179 for item in arr {
181 match cypher_eq(left, item) {
182 Some(true) => return Ok(Value::Bool(true)),
183 None => has_null = true,
184 _ => {}
185 }
186 }
187
188 if let Value::Map(map) = left
192 && let Some(vid_val) = map.get("_vid")
193 && let Some(vid_u64) = vid_val.as_u64()
194 {
195 let vid = uni_common::core::id::Vid::from(vid_u64);
196 let vid_str = vid.to_string();
197 for item in arr {
198 match item {
199 Value::String(s) if s == &vid_str => return Ok(Value::Bool(true)),
200 Value::Int(n) if *n as u64 == vid_u64 => return Ok(Value::Bool(true)),
201 _ => {}
202 }
203 }
204 }
205
206 if has_null {
207 Ok(Value::Null)
208 } else {
209 Ok(Value::Bool(false))
210 }
211 } else {
212 Err(anyhow!("Right side of IN must be a list"))
213 }
214}
215
216fn eval_string_predicate(
217 left: &Value,
218 right: &Value,
219 op_name: &str,
220 check: fn(&str, &str) -> bool,
221) -> Result<Value> {
222 let l = left
223 .as_str()
224 .ok_or_else(|| anyhow!("Left side of {} must be a string", op_name))?;
225 let r = right
226 .as_str()
227 .ok_or_else(|| anyhow!("Right side of {} must be a string", op_name))?;
228 Ok(Value::Bool(check(l, r)))
229}
230
231fn eval_numeric_op<F>(left: &Value, right: &Value, op: F) -> Result<Value>
232where
233 F: Fn(f64, f64) -> f64,
234{
235 if left.is_null() || right.is_null() {
237 return Ok(Value::Null);
238 }
239 let (l, r) = match (left.as_f64(), right.as_f64()) {
240 (Some(l), Some(r)) => (l, r),
241 _ => return Err(anyhow!("Arithmetic operation requires numbers")),
242 };
243 let result = op(l, r);
244 if !result.is_nan()
246 && !result.is_infinite()
247 && result.fract() == 0.0
248 && left.is_i64()
249 && right.is_i64()
250 {
251 Ok(Value::Int(result as i64))
252 } else {
253 Ok(Value::Float(result))
254 }
255}
256
257fn add_temporal_duration_to_value(val: &Value, dur: &CypherDuration) -> Result<Value> {
264 match val {
265 Value::Temporal(tv) => add_temporal_duration_typed(tv, dur),
266 Value::Map(map) => {
267 if let Some(tv) = temporal_from_map_wrapper(map) {
268 add_temporal_duration_typed(&tv, dur)
269 } else {
270 Err(anyhow!("Expected temporal value for duration arithmetic"))
271 }
272 }
273 Value::String(s) => {
274 if let Some(tv) = temporal_from_json_wrapper_str(s) {
275 return add_temporal_duration_typed(&tv, dur);
276 }
277 let ttype = classify_temporal(s)
278 .ok_or_else(|| anyhow!("Cannot classify temporal value: {}", s))?;
279 let result_str = match ttype {
280 TemporalType::Date => add_cypher_duration_to_date(s, dur)?,
281 TemporalType::LocalTime => add_cypher_duration_to_localtime(s, dur)?,
282 TemporalType::Time => add_cypher_duration_to_time(s, dur)?,
283 TemporalType::LocalDateTime => add_cypher_duration_to_localdatetime(s, dur)?,
284 TemporalType::DateTime => add_cypher_duration_to_datetime(s, dur)?,
285 TemporalType::Duration => {
286 return Err(anyhow!("Cannot add duration to duration this way"));
287 }
288 };
289 Ok(Value::String(result_str))
290 }
291 _ => Err(anyhow!("Expected temporal value for duration arithmetic")),
292 }
293}
294
295fn add_temporal_duration_typed(tv: &TemporalValue, dur: &CypherDuration) -> Result<Value> {
297 let s = tv.to_string();
300 let ttype = tv.temporal_type();
301 let result_str = match ttype {
302 TemporalType::Date => add_cypher_duration_to_date(&s, dur)?,
303 TemporalType::LocalTime => add_cypher_duration_to_localtime(&s, dur)?,
304 TemporalType::Time => add_cypher_duration_to_time(&s, dur)?,
305 TemporalType::LocalDateTime => add_cypher_duration_to_localdatetime(&s, dur)?,
306 TemporalType::DateTime => add_cypher_duration_to_datetime(&s, dur)?,
307 TemporalType::Duration => return Err(anyhow!("Cannot add duration to duration this way")),
308 };
309 let args = [Value::String(result_str)];
311 match ttype {
312 TemporalType::Date => eval_datetime_function("DATE", &args),
313 TemporalType::LocalTime => eval_datetime_function("LOCALTIME", &args),
314 TemporalType::Time => eval_datetime_function("TIME", &args),
315 TemporalType::LocalDateTime => eval_datetime_function("LOCALDATETIME", &args),
316 TemporalType::DateTime => eval_datetime_function("DATETIME", &args),
317 TemporalType::Duration => unreachable!(),
318 }
319}
320
321fn eval_add(left: &Value, right: &Value) -> Result<Value> {
323 if left.is_null() || right.is_null() {
325 return Ok(Value::Null);
326 }
327
328 match (left, right) {
330 (Value::List(l), Value::List(r)) => {
331 let mut result = l.clone();
332 result.extend(r.iter().cloned());
333 return Ok(Value::List(result));
334 }
335 (Value::List(l), _) => {
336 let mut result = l.clone();
337 result.push(right.clone());
338 return Ok(Value::List(result));
339 }
340 (_, Value::List(r)) => {
341 let mut result = vec![left.clone()];
342 result.extend(r.iter().cloned());
343 return Ok(Value::List(result));
344 }
345 _ => {}
346 }
347
348 if let (Some(l), Some(r)) = (left.as_f64(), right.as_f64()) {
350 if left.is_i64() && right.is_i64() {
351 return Ok(Value::Int(left.as_i64().unwrap() + right.as_i64().unwrap()));
352 }
353 return Ok(Value::Float(l + r));
354 }
355
356 if let Value::String(s) = left
358 && classify_temporal(s).is_some_and(|t| t != TemporalType::Duration)
359 && let Ok(dur) = parse_duration_from_value(right)
360 {
361 return add_temporal_duration_to_value(left, &dur);
362 }
363 if let Value::String(s) = right
364 && classify_temporal(s).is_some_and(|t| t != TemporalType::Duration)
365 && let Ok(dur) = parse_duration_from_value(left)
366 {
367 return add_temporal_duration_to_value(right, &dur);
368 }
369
370 if let Some(tv) = temporal_from_value(left)
372 && !matches!(tv, TemporalValue::Duration { .. })
373 && (is_duration_value(right) || right.is_number())
374 {
375 let dur = parse_duration_from_value(right)?;
376 return add_temporal_duration_typed(&tv, &dur);
377 }
378 if let Some(tv) = temporal_from_value(right)
380 && !matches!(tv, TemporalValue::Duration { .. })
381 && (is_duration_value(left) || left.is_number())
382 {
383 let dur = parse_duration_from_value(left)?;
384 return add_temporal_duration_typed(&tv, &dur);
385 }
386 if let (
388 Some(TemporalValue::Duration {
389 months: m1,
390 days: d1,
391 nanos: n1,
392 }),
393 Some(TemporalValue::Duration {
394 months: m2,
395 days: d2,
396 nanos: n2,
397 }),
398 ) = (temporal_from_value(left), temporal_from_value(right))
399 {
400 return Ok(Value::Temporal(TemporalValue::Duration {
401 months: m1 + m2,
402 days: d1 + d2,
403 nanos: n1 + n2,
404 }));
405 }
406
407 if let (Value::String(l), Value::String(r)) = (left, right) {
409 let l_type = classify_temporal(l);
410 let r_type = classify_temporal(r);
411
412 match (l_type, r_type) {
413 (Some(lt), Some(TemporalType::Duration)) if lt != TemporalType::Duration => {
415 let dur = parse_duration_to_cypher(r)?;
416 return add_temporal_duration_to_value(left, &dur);
417 }
418 (Some(TemporalType::Duration), Some(rt)) if rt != TemporalType::Duration => {
420 let dur = parse_duration_to_cypher(l)?;
421 return add_temporal_duration_to_value(right, &dur);
422 }
423 (Some(TemporalType::Duration), Some(TemporalType::Duration)) => {
425 let d1 = parse_duration_to_cypher(l)?;
426 let d2 = parse_duration_to_cypher(r)?;
427 return Ok(Value::String(d1.add(&d2).to_iso8601()));
428 }
429 _ => return Ok(Value::String(format!("{}{}", l, r))),
431 }
432 }
433
434 if let Value::String(_) = left
436 && right.is_number()
437 && classify_value_temporal(left).is_some_and(|t| t != TemporalType::Duration)
438 {
439 let dur = parse_duration_from_value(right)?;
440 return add_temporal_duration_to_value(left, &dur);
441 }
442 if let Value::String(_) = right
444 && left.is_number()
445 && classify_value_temporal(right).is_some_and(|t| t != TemporalType::Duration)
446 {
447 let dur = parse_duration_from_value(left)?;
448 return add_temporal_duration_to_value(right, &dur);
449 }
450
451 Err(anyhow!(
452 "Invalid types for addition: left={:?}, right={:?}",
453 left,
454 right
455 ))
456}
457
458fn classify_value_temporal(val: &Value) -> Option<TemporalType> {
460 match val {
461 Value::Temporal(tv) => Some(tv.temporal_type()),
462 Value::String(s) => classify_temporal(s),
463 _ => None,
464 }
465}
466
467fn eval_sub(left: &Value, right: &Value) -> Result<Value> {
469 if left.is_null() || right.is_null() {
471 return Ok(Value::Null);
472 }
473
474 if let Value::Temporal(tv) = left
476 && !matches!(tv, TemporalValue::Duration { .. })
477 {
478 if let Value::Temporal(TemporalValue::Duration {
479 months,
480 days,
481 nanos,
482 }) = right
483 {
484 let dur = CypherDuration::new(-months, -days, -nanos);
485 return add_temporal_duration_typed(tv, &dur);
486 }
487 if is_duration_value(right) || right.is_number() {
488 let dur = parse_duration_from_value(right)?.negate();
489 return add_temporal_duration_typed(tv, &dur);
490 }
491 }
492 if let (
494 Value::Temporal(TemporalValue::Duration {
495 months: m1,
496 days: d1,
497 nanos: n1,
498 }),
499 Value::Temporal(TemporalValue::Duration {
500 months: m2,
501 days: d2,
502 nanos: n2,
503 }),
504 ) = (left, right)
505 {
506 return Ok(Value::Temporal(TemporalValue::Duration {
507 months: m1 - m2,
508 days: d1 - d2,
509 nanos: n1 - n2,
510 }));
511 }
512 if let (Value::Temporal(l), Value::Temporal(r)) = (left, right)
514 && l.temporal_type() == r.temporal_type()
515 && l.temporal_type() != TemporalType::Duration
516 {
517 let args = [left.clone(), right.clone()];
518 return crate::query::datetime::eval_datetime_function("DURATION.BETWEEN", &args);
519 }
520
521 if let (Value::String(l), Value::String(r)) = (left, right) {
523 let l_type = classify_temporal(l);
524 let r_type = classify_temporal(r);
525
526 match (l_type, r_type) {
527 (Some(lt), Some(TemporalType::Duration)) if lt != TemporalType::Duration => {
528 let dur = parse_duration_to_cypher(r)?.negate();
529 return add_temporal_duration_to_value(left, &dur);
530 }
531 (Some(TemporalType::Duration), Some(TemporalType::Duration)) => {
532 let d1 = parse_duration_to_cypher(l)?;
533 let d2 = parse_duration_to_cypher(r)?;
534 return Ok(Value::String(d1.sub(&d2).to_iso8601()));
535 }
536 (Some(lt), Some(rt))
537 if lt != TemporalType::Duration && rt != TemporalType::Duration && lt == rt =>
538 {
539 let args = [left.clone(), right.clone()];
540 return crate::query::datetime::eval_datetime_function("DURATION.BETWEEN", &args);
541 }
542 _ => {}
543 }
544 }
545
546 if let Value::String(_) = left
548 && right.is_number()
549 && classify_value_temporal(left).is_some_and(|t| t != TemporalType::Duration)
550 {
551 let dur = parse_duration_from_value(right)?.negate();
552 return add_temporal_duration_to_value(left, &dur);
553 }
554
555 eval_numeric_op(left, right, |a, b| a - b)
556}
557
558fn extract_cypher_duration(val: &Value) -> Option<Result<(CypherDuration, bool)>> {
562 match val {
563 Value::Temporal(TemporalValue::Duration {
564 months,
565 days,
566 nanos,
567 }) => Some(Ok((CypherDuration::new(*months, *days, *nanos), true))),
568 Value::String(s) if is_duration_value(val) => {
569 Some(parse_duration_to_cypher(s).map(|d| (d, false)))
570 }
571 _ => None,
572 }
573}
574
575fn duration_to_value(result: CypherDuration, is_temporal: bool) -> Value {
580 if is_temporal {
581 result.to_temporal_value()
582 } else {
583 Value::String(result.to_iso8601())
584 }
585}
586
587fn eval_mul(left: &Value, right: &Value) -> Result<Value> {
589 if left.is_null() || right.is_null() {
590 return Ok(Value::Null);
591 }
592
593 if let Some(dur_result) = extract_cypher_duration(left)
595 && let Some(factor) = right.as_f64()
596 {
597 let (dur, is_temporal) = dur_result?;
598 return Ok(duration_to_value(dur.multiply(factor), is_temporal));
599 }
600 if let Some(dur_result) = extract_cypher_duration(right)
601 && let Some(factor) = left.as_f64()
602 {
603 let (dur, is_temporal) = dur_result?;
604 return Ok(duration_to_value(dur.multiply(factor), is_temporal));
605 }
606
607 eval_numeric_op(left, right, |a, b| a * b)
608}
609
610fn eval_div(left: &Value, right: &Value) -> Result<Value> {
612 if left.is_null() || right.is_null() {
613 return Ok(Value::Null);
614 }
615
616 if let Some(dur_result) = extract_cypher_duration(left)
618 && let Some(divisor) = right.as_f64()
619 {
620 let (dur, is_temporal) = dur_result?;
621 return Ok(duration_to_value(dur.divide(divisor), is_temporal));
622 }
623
624 if let (Value::Int(l), Value::Int(r)) = (left, right) {
626 return if *r == 0 {
627 Err(anyhow!("Division by zero"))
628 } else {
629 Ok(Value::Int(l / r))
630 };
631 }
632
633 eval_numeric_op(left, right, |a, b| a / b)
634}
635
636fn eval_comparison<F>(left: &Value, right: &Value, check: F) -> Result<Value>
642where
643 F: Fn(Ordering) -> bool,
644{
645 if left.is_null() || right.is_null() {
647 return Ok(Value::Null);
648 }
649
650 let left_nan = left.as_f64().is_some_and(|f| f.is_nan());
652 let right_nan = right.as_f64().is_some_and(|f| f.is_nan());
653 if left_nan || right_nan {
654 if left_nan && right_nan {
655 return Ok(Value::Bool(false));
656 }
657 let other = if left_nan { right } else { left };
658 if other.as_f64().is_some() {
659 return Ok(Value::Bool(false)); }
661 return Ok(Value::Null); }
663
664 let ord = cypher_partial_cmp(left, right);
665 match ord {
666 Some(o) => Ok(Value::Bool(check(o))),
667 None => Ok(Value::Null),
668 }
669}
670
671fn cypher_partial_cmp(left: &Value, right: &Value) -> Option<Ordering> {
673 if left.is_null() || right.is_null() {
674 return None;
675 }
676
677 let left_temporal = temporal_from_value(left);
678 let right_temporal = temporal_from_value(right);
679 if let (Some(l), Some(r)) = (&left_temporal, &right_temporal) {
680 return temporal_partial_cmp(l, r);
681 }
682 if let (Some(_), Value::String(rs)) = (&left_temporal, right) {
683 let ls = left.to_string();
684 if let (Some(lt), Some(rt)) = (classify_temporal(&ls), classify_temporal(rs))
685 && lt == rt
686 {
687 return temporal_string_cmp(&ls, rs, lt);
688 }
689 return None;
690 }
691 if let (Value::String(ls), Some(_)) = (left, &right_temporal) {
692 let rs = right.to_string();
693 if let (Some(lt), Some(rt)) = (classify_temporal(ls), classify_temporal(&rs))
694 && lt == rt
695 {
696 return temporal_string_cmp(ls, &rs, lt);
697 }
698 return None;
699 }
700
701 if let (Some(l), Some(r)) = (left.as_i64(), right.as_i64()) {
703 return Some(l.cmp(&r));
704 }
705
706 if let (Some(l), Some(r)) = (left.as_f64(), right.as_f64()) {
708 return l.partial_cmp(&r);
709 }
710
711 if let (Some(l), Some(r)) = (left.as_str(), right.as_str()) {
713 if let (Some(lt), Some(rt)) = (classify_temporal(l), classify_temporal(r))
715 && lt == rt
716 {
717 let res = temporal_string_cmp(l, r, lt);
718 if res.is_some() {
719 return res;
720 }
721 }
722 return l.partial_cmp(r);
723 }
724
725 if let (Some(l), Some(r)) = (left.as_bool(), right.as_bool()) {
727 return l.partial_cmp(&r);
728 }
729
730 if let (Value::List(l), Value::List(r)) = (left, right) {
732 for (lv, rv) in l.iter().zip(r.iter()) {
733 match cypher_partial_cmp(lv, rv) {
734 Some(Ordering::Equal) => continue,
735 other => return other,
736 }
737 }
738 return l.len().partial_cmp(&r.len());
739 }
740
741 None
743}
744
745fn temporal_partial_cmp(left: &TemporalValue, right: &TemporalValue) -> Option<Ordering> {
747 match (left, right) {
748 (
749 TemporalValue::Date {
750 days_since_epoch: l,
751 },
752 TemporalValue::Date {
753 days_since_epoch: r,
754 },
755 ) => Some(l.cmp(r)),
756 (
757 TemporalValue::LocalTime {
758 nanos_since_midnight: l,
759 },
760 TemporalValue::LocalTime {
761 nanos_since_midnight: r,
762 },
763 ) => Some(l.cmp(r)),
764 (
765 TemporalValue::Time {
766 nanos_since_midnight: lm,
767 offset_seconds: lo,
768 },
769 TemporalValue::Time {
770 nanos_since_midnight: rm,
771 offset_seconds: ro,
772 },
773 ) => {
774 let l_utc = *lm as i128 - (*lo as i128) * 1_000_000_000;
776 let r_utc = *rm as i128 - (*ro as i128) * 1_000_000_000;
777 Some(l_utc.cmp(&r_utc))
778 }
779 (
780 TemporalValue::LocalDateTime {
781 nanos_since_epoch: l,
782 },
783 TemporalValue::LocalDateTime {
784 nanos_since_epoch: r,
785 },
786 ) => Some(l.cmp(r)),
787 (
788 TemporalValue::DateTime {
789 nanos_since_epoch: l,
790 ..
791 },
792 TemporalValue::DateTime {
793 nanos_since_epoch: r,
794 ..
795 },
796 ) => {
797 Some(l.cmp(r))
799 }
800 (TemporalValue::Duration { .. }, TemporalValue::Duration { .. }) => None,
802 _ => None,
804 }
805}
806
807fn temporal_from_value(v: &Value) -> Option<TemporalValue> {
808 match v {
809 Value::Temporal(tv) => Some(tv.clone()),
810 Value::Map(map) => temporal_from_map_wrapper(map),
811 Value::String(s) => temporal_from_json_wrapper_str(s),
812 _ => None,
813 }
814}
815
816fn temporal_from_map_wrapper(
817 map: &std::collections::HashMap<String, Value>,
818) -> Option<TemporalValue> {
819 if map.len() != 1 {
820 return None;
821 }
822
823 let as_i32 = |v: &Value| v.as_i64().and_then(|n| i32::try_from(n).ok());
824 let as_i64 = |v: &Value| v.as_i64();
825
826 if let Some(Value::Map(inner)) = map.get("Date") {
827 let days = inner.get("days_since_epoch").and_then(as_i32)?;
828 return Some(TemporalValue::Date {
829 days_since_epoch: days,
830 });
831 }
832 if let Some(Value::Map(inner)) = map.get("LocalTime") {
833 let nanos = inner.get("nanos_since_midnight").and_then(as_i64)?;
834 return Some(TemporalValue::LocalTime {
835 nanos_since_midnight: nanos,
836 });
837 }
838 if let Some(Value::Map(inner)) = map.get("Time") {
839 let nanos = inner.get("nanos_since_midnight").and_then(as_i64)?;
840 let offset = inner.get("offset_seconds").and_then(as_i32)?;
841 return Some(TemporalValue::Time {
842 nanos_since_midnight: nanos,
843 offset_seconds: offset,
844 });
845 }
846 if let Some(Value::Map(inner)) = map.get("LocalDateTime") {
847 let nanos = inner.get("nanos_since_epoch").and_then(as_i64)?;
848 return Some(TemporalValue::LocalDateTime {
849 nanos_since_epoch: nanos,
850 });
851 }
852 if let Some(Value::Map(inner)) = map.get("DateTime") {
853 let nanos = inner.get("nanos_since_epoch").and_then(as_i64)?;
854 let offset = inner.get("offset_seconds").and_then(as_i32)?;
855 let timezone_name = match inner.get("timezone_name") {
856 Some(Value::String(s)) => Some(s.clone()),
857 _ => None,
858 };
859 return Some(TemporalValue::DateTime {
860 nanos_since_epoch: nanos,
861 offset_seconds: offset,
862 timezone_name,
863 });
864 }
865 if let Some(Value::Map(inner)) = map.get("Duration") {
866 let months = inner.get("months").and_then(as_i64)?;
867 let days = inner.get("days").and_then(as_i64)?;
868 let nanos = inner.get("nanos").and_then(as_i64)?;
869 return Some(TemporalValue::Duration {
870 months,
871 days,
872 nanos,
873 });
874 }
875 None
876}
877
878fn temporal_from_json_wrapper_str(s: &str) -> Option<TemporalValue> {
879 let parsed: serde_json::Value = serde_json::from_str(s).ok()?;
880 let obj = parsed.as_object()?;
881 if obj.len() != 1 {
882 return None;
883 }
884
885 let as_i32 = |o: &serde_json::Map<String, serde_json::Value>, key: &str| {
886 o.get(key)
887 .and_then(serde_json::Value::as_i64)
888 .and_then(|n| i32::try_from(n).ok())
889 };
890 let as_i64 = |o: &serde_json::Map<String, serde_json::Value>, key: &str| {
891 o.get(key).and_then(serde_json::Value::as_i64)
892 };
893
894 if let Some(inner) = obj.get("Date").and_then(serde_json::Value::as_object) {
895 return Some(TemporalValue::Date {
896 days_since_epoch: as_i32(inner, "days_since_epoch")?,
897 });
898 }
899 if let Some(inner) = obj.get("LocalTime").and_then(serde_json::Value::as_object) {
900 return Some(TemporalValue::LocalTime {
901 nanos_since_midnight: as_i64(inner, "nanos_since_midnight")?,
902 });
903 }
904 if let Some(inner) = obj.get("Time").and_then(serde_json::Value::as_object) {
905 return Some(TemporalValue::Time {
906 nanos_since_midnight: as_i64(inner, "nanos_since_midnight")?,
907 offset_seconds: as_i32(inner, "offset_seconds")?,
908 });
909 }
910 if let Some(inner) = obj
911 .get("LocalDateTime")
912 .and_then(serde_json::Value::as_object)
913 {
914 return Some(TemporalValue::LocalDateTime {
915 nanos_since_epoch: as_i64(inner, "nanos_since_epoch")?,
916 });
917 }
918 if let Some(inner) = obj.get("DateTime").and_then(serde_json::Value::as_object) {
919 return Some(TemporalValue::DateTime {
920 nanos_since_epoch: as_i64(inner, "nanos_since_epoch")?,
921 offset_seconds: as_i32(inner, "offset_seconds")?,
922 timezone_name: inner
923 .get("timezone_name")
924 .and_then(serde_json::Value::as_str)
925 .map(str::to_string),
926 });
927 }
928 if let Some(inner) = obj.get("Duration").and_then(serde_json::Value::as_object) {
929 return Some(TemporalValue::Duration {
930 months: as_i64(inner, "months")?,
931 days: as_i64(inner, "days")?,
932 nanos: as_i64(inner, "nanos")?,
933 });
934 }
935 None
936}
937
938fn temporal_string_cmp(l: &str, r: &str, ttype: TemporalType) -> Option<Ordering> {
940 match ttype {
941 TemporalType::Date => {
942 let ld = chrono::NaiveDate::parse_from_str(l, "%Y-%m-%d").ok();
943 let rd = chrono::NaiveDate::parse_from_str(r, "%Y-%m-%d").ok();
944 ld.and_then(|l| rd.map(|r| l.cmp(&r)))
945 }
946 TemporalType::LocalTime => {
947 let lt = parse_time_for_cmp(l).ok();
948 let rt = parse_time_for_cmp(r).ok();
949 lt.and_then(|l| rt.map(|r| l.cmp(&r)))
950 }
951 TemporalType::Time => {
952 let ln = time_with_tz_to_utc_nanos(l).ok();
953 let rn = time_with_tz_to_utc_nanos(r).ok();
954 ln.and_then(|l| rn.map(|r| l.cmp(&r)))
955 }
956 TemporalType::LocalDateTime => {
957 let ldt = parse_local_datetime_for_cmp(l).ok();
958 let rdt = parse_local_datetime_for_cmp(r).ok();
959 ldt.and_then(|l| rdt.map(|r| l.cmp(&r)))
960 }
961 TemporalType::DateTime => {
962 let ldt = parse_datetime_utc(l).ok();
963 let rdt = parse_datetime_utc(r).ok();
964 ldt.and_then(|l| rdt.map(|r| l.cmp(&r)))
965 }
966 TemporalType::Duration => None, }
968}
969
970fn parse_time_for_cmp(s: &str) -> Result<chrono::NaiveTime> {
972 chrono::NaiveTime::parse_from_str(s, "%H:%M:%S%.f")
973 .or_else(|_| chrono::NaiveTime::parse_from_str(s, "%H:%M:%S"))
974 .or_else(|_| chrono::NaiveTime::parse_from_str(s, "%H:%M"))
975 .map_err(|_| anyhow!("Cannot parse time: {}", s))
976}
977
978fn parse_local_datetime_for_cmp(s: &str) -> Result<chrono::NaiveDateTime> {
980 chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S%.f")
981 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S"))
982 .or_else(|_| chrono::NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M"))
983 .map_err(|_| anyhow!("Cannot parse localdatetime: {}", s))
984}
985
986const NANOS_PER_SECOND_CMP: i64 = 1_000_000_000;
987
988fn time_with_tz_to_utc_nanos(s: &str) -> Result<i64> {
990 use chrono::Timelike;
991 let (_, time, tz_info) = crate::query::datetime::parse_datetime_with_tz(s)?;
992 let local_nanos = time.hour() as i64 * 3_600 * NANOS_PER_SECOND_CMP
993 + time.minute() as i64 * 60 * NANOS_PER_SECOND_CMP
994 + time.second() as i64 * NANOS_PER_SECOND_CMP
995 + time.nanosecond() as i64;
996
997 let offset_secs: i64 = match tz_info {
999 Some(ref tz) => {
1000 let today = chrono::NaiveDate::from_ymd_opt(2000, 1, 1).unwrap();
1001 let ndt = chrono::NaiveDateTime::new(today, time);
1002 tz.offset_for_local(&ndt)?.local_minus_utc() as i64
1003 }
1004 None => 0,
1005 };
1006
1007 Ok(local_nanos - offset_secs * NANOS_PER_SECOND_CMP)
1008}
1009
1010fn eval_size(arg: &Value) -> Result<Value> {
1015 match arg {
1016 Value::List(arr) => Ok(Value::Int(arr.len() as i64)),
1017 Value::Map(map) => Ok(Value::Int(map.len() as i64)),
1018 Value::String(s) => Ok(Value::Int(s.len() as i64)),
1019 Value::Null => Ok(Value::Null),
1020 _ => Err(anyhow!("size() expects a List, Map, or String")),
1021 }
1022}
1023
1024fn eval_keys(arg: &Value) -> Result<Value> {
1025 match arg {
1026 Value::Map(map) => {
1027 let is_entity =
1031 map.contains_key("_vid") || map.contains_key("_eid") || map.contains_key("_labels");
1032 let mut keys: Vec<&String> = map
1033 .iter()
1034 .filter(|(k, v)| !k.starts_with('_') && (!is_entity || !v.is_null()))
1035 .map(|(k, _)| k)
1036 .collect();
1037 keys.sort();
1038 Ok(Value::List(
1039 keys.into_iter().map(|k| Value::String(k.clone())).collect(),
1040 ))
1041 }
1042 Value::Null => Ok(Value::Null),
1043 _ => Err(anyhow!("keys() expects a Map")),
1044 }
1045}
1046
1047fn eval_head(arg: &Value) -> Result<Value> {
1048 match arg {
1049 Value::List(arr) => Ok(arr.first().cloned().unwrap_or(Value::Null)),
1050 Value::Null => Ok(Value::Null),
1051 _ => Err(anyhow!("head() expects a List")),
1052 }
1053}
1054
1055fn eval_tail(arg: &Value) -> Result<Value> {
1056 match arg {
1057 Value::List(arr) => Ok(Value::List(arr.get(1..).unwrap_or_default().to_vec())),
1058 Value::Null => Ok(Value::Null),
1059 _ => Err(anyhow!("tail() expects a List")),
1060 }
1061}
1062
1063fn eval_last(arg: &Value) -> Result<Value> {
1064 match arg {
1065 Value::List(arr) => Ok(arr.last().cloned().unwrap_or(Value::Null)),
1066 Value::Null => Ok(Value::Null),
1067 _ => Err(anyhow!("last() expects a List")),
1068 }
1069}
1070
1071fn eval_length(arg: &Value) -> Result<Value> {
1072 match arg {
1073 Value::List(arr) => Ok(Value::Int(arr.len() as i64)),
1074 Value::String(s) => Ok(Value::Int(s.len() as i64)),
1075 Value::Path(p) => Ok(Value::Int(p.edges.len() as i64)),
1076 Value::Map(map) => {
1077 if map.contains_key("nodes")
1079 && map.contains_key("relationships")
1080 && let Some(Value::List(rels)) = map.get("relationships")
1081 {
1082 return Ok(Value::Int(rels.len() as i64));
1083 }
1084 Ok(Value::Null)
1085 }
1086 Value::Null => Ok(Value::Null),
1087 _ => Err(anyhow!("length() expects a List, String, or Path")),
1088 }
1089}
1090
1091fn eval_nodes(arg: &Value) -> Result<Value> {
1092 match arg {
1093 Value::Path(p) => Ok(Value::List(
1094 p.nodes.iter().map(|n| Value::Node(n.clone())).collect(),
1095 )),
1096 Value::Map(map) => {
1097 if let Some(nodes) = map.get("nodes") {
1098 Ok(nodes.clone())
1099 } else {
1100 Ok(Value::Null)
1101 }
1102 }
1103 Value::Null => Ok(Value::Null),
1104 _ => Err(anyhow!("nodes() expects a Path")),
1105 }
1106}
1107
1108fn eval_relationships(arg: &Value) -> Result<Value> {
1109 match arg {
1110 Value::Path(p) => Ok(Value::List(
1111 p.edges.iter().map(|e| Value::Edge(e.clone())).collect(),
1112 )),
1113 Value::Map(map) => {
1114 if let Some(rels) = map.get("relationships") {
1115 Ok(rels.clone())
1116 } else {
1117 Ok(Value::Null)
1118 }
1119 }
1120 Value::Null => Ok(Value::Null),
1121 _ => Err(anyhow!("relationships() expects a Path")),
1122 }
1123}
1124
1125fn eval_list_function(name: &str, args: &[Value]) -> Result<Value> {
1127 if args.len() != 1 {
1128 return Err(anyhow!("{}() requires 1 argument", name));
1129 }
1130 match name {
1131 "SIZE" => eval_size(&args[0]),
1132 "KEYS" => eval_keys(&args[0]),
1133 "HEAD" => eval_head(&args[0]),
1134 "TAIL" => eval_tail(&args[0]),
1135 "LAST" => eval_last(&args[0]),
1136 "LENGTH" => eval_length(&args[0]),
1137 "NODES" => eval_nodes(&args[0]),
1138 "RELATIONSHIPS" => eval_relationships(&args[0]),
1139 _ => Err(anyhow!("Unknown list function: {}", name)),
1140 }
1141}
1142
1143fn eval_tointeger(arg: &Value) -> Result<Value> {
1148 match arg {
1149 Value::Int(i) => Ok(Value::Int(*i)),
1150 Value::Float(f) => Ok(Value::Int(*f as i64)),
1151 Value::String(s) => Ok(s.parse::<i64>().map(Value::Int).unwrap_or(Value::Null)),
1152 Value::Null => Ok(Value::Null),
1153 _ => Err(anyhow!(
1154 "InvalidArgumentValue: toInteger() cannot convert type"
1155 )),
1156 }
1157}
1158
1159fn eval_tofloat(arg: &Value) -> Result<Value> {
1160 match arg {
1161 Value::Int(i) => Ok(Value::Float(*i as f64)),
1162 Value::Float(f) => Ok(Value::Float(*f)),
1163 Value::String(s) => Ok(s.parse::<f64>().map(Value::Float).unwrap_or(Value::Null)),
1164 Value::Null => Ok(Value::Null),
1165 _ => Err(anyhow!(
1166 "InvalidArgumentValue: toFloat() cannot convert type"
1167 )),
1168 }
1169}
1170
1171fn eval_tostring(arg: &Value) -> Result<Value> {
1172 match arg {
1173 Value::String(s) => Ok(Value::String(s.clone())),
1174 Value::Int(i) => Ok(Value::String(i.to_string())),
1175 Value::Float(f) => {
1176 if f.fract() == 0.0 && f.is_finite() {
1178 Ok(Value::String(format!("{f:.1}")))
1179 } else {
1180 Ok(Value::String(f.to_string()))
1181 }
1182 }
1183 Value::Bool(b) => Ok(Value::String(b.to_string())),
1184 Value::Null => Ok(Value::Null),
1185 other => Ok(Value::String(other.to_string())),
1186 }
1187}
1188
1189fn eval_toboolean(arg: &Value) -> Result<Value> {
1190 match arg {
1191 Value::Bool(b) => Ok(Value::Bool(*b)),
1192 Value::String(s) => {
1193 let lower = s.to_lowercase();
1194 if lower == "true" {
1195 Ok(Value::Bool(true))
1196 } else if lower == "false" {
1197 Ok(Value::Bool(false))
1198 } else {
1199 Ok(Value::Null)
1200 }
1201 }
1202 Value::Null => Ok(Value::Null),
1203 _ => Err(anyhow!(
1204 "InvalidArgumentValue: toBoolean() cannot convert type"
1205 )),
1206 }
1207}
1208
1209fn eval_type_function(name: &str, args: &[Value]) -> Result<Value> {
1211 if args.len() != 1 {
1212 return Err(anyhow!("{}() requires 1 argument", name));
1213 }
1214 match name {
1215 "TOINTEGER" | "TOINT" => eval_tointeger(&args[0]),
1216 "TOFLOAT" => eval_tofloat(&args[0]),
1217 "TOSTRING" => eval_tostring(&args[0]),
1218 "TOBOOLEAN" | "TOBOOL" => eval_toboolean(&args[0]),
1219 _ => Err(anyhow!("Unknown type function: {}", name)),
1220 }
1221}
1222
1223fn eval_abs(arg: &Value) -> Result<Value> {
1228 match arg {
1229 Value::Int(i) => Ok(Value::Int(i.abs())),
1230 Value::Float(f) => Ok(Value::Float(f.abs())),
1231 Value::Null => Ok(Value::Null),
1232 _ => Err(anyhow!("abs() expects a number")),
1233 }
1234}
1235
1236fn eval_sqrt(arg: &Value) -> Result<Value> {
1237 match arg {
1238 v if v.is_number() => {
1239 let f = v.as_f64().unwrap();
1240 if f < 0.0 {
1241 Ok(Value::Null)
1242 } else {
1243 Ok(Value::Float(f.sqrt()))
1244 }
1245 }
1246 Value::Null => Ok(Value::Null),
1247 _ => Err(anyhow!("sqrt() expects a number")),
1248 }
1249}
1250
1251fn eval_sign(arg: &Value) -> Result<Value> {
1252 match arg {
1253 Value::Int(i) => Ok(Value::Int(i.signum())),
1254 Value::Float(f) => Ok(Value::Int(f.signum() as i64)),
1255 Value::Null => Ok(Value::Null),
1256 _ => Err(anyhow!("sign() expects a number")),
1257 }
1258}
1259
1260fn eval_power(args: &[Value]) -> Result<Value> {
1261 if args.len() != 2 {
1262 return Err(anyhow!("power() requires 2 arguments"));
1263 }
1264 match (&args[0], &args[1]) {
1265 (a, b) if a.is_number() && b.is_number() => {
1266 let base = a.as_f64().unwrap();
1267 let exp = b.as_f64().unwrap();
1268 Ok(Value::Float(base.powf(exp)))
1269 }
1270 (Value::Null, _) | (_, Value::Null) => Ok(Value::Null),
1271 _ => Err(anyhow!("power() expects numeric arguments")),
1272 }
1273}
1274
1275fn eval_unary_numeric_op<F>(arg: &Value, func_name: &str, op: F) -> Result<Value>
1277where
1278 F: Fn(f64) -> f64,
1279{
1280 match arg {
1281 Value::Int(i) => Ok(Value::Float(op(*i as f64))),
1282 Value::Float(f) => Ok(Value::Float(op(*f))),
1283 Value::Null => Ok(Value::Null),
1284 _ => Err(anyhow!("{}() expects a number", func_name)),
1285 }
1286}
1287
1288fn eval_atan2(args: &[Value]) -> Result<Value> {
1289 if args.len() != 2 {
1290 return Err(anyhow!("atan2() requires 2 arguments"));
1291 }
1292 match (&args[0], &args[1]) {
1293 (a, b) if a.is_number() && b.is_number() => {
1294 let y_val = a.as_f64().unwrap();
1295 let x_val = b.as_f64().unwrap();
1296 Ok(Value::Float(y_val.atan2(x_val)))
1297 }
1298 (Value::Null, _) | (_, Value::Null) => Ok(Value::Null),
1299 _ => Err(anyhow!("atan2() expects numeric arguments")),
1300 }
1301}
1302
1303fn require_one_arg<'a>(name: &str, args: &'a [Value]) -> Result<&'a Value> {
1305 if args.len() != 1 {
1306 return Err(anyhow!("{} requires 1 argument", name));
1307 }
1308 Ok(&args[0])
1309}
1310
1311fn eval_math_function(name: &str, args: &[Value]) -> Result<Value> {
1316 match name {
1317 "ABS" => eval_abs(require_one_arg(name, args)?),
1319 "CEIL" => eval_unary_numeric_op(require_one_arg(name, args)?, "ceil", f64::ceil),
1320 "FLOOR" => eval_unary_numeric_op(require_one_arg(name, args)?, "floor", f64::floor),
1321 "ROUND" => eval_unary_numeric_op(require_one_arg(name, args)?, "round", f64::round),
1322 "SQRT" => eval_sqrt(require_one_arg(name, args)?),
1323 "SIGN" => eval_sign(require_one_arg(name, args)?),
1324 "LOG" => eval_unary_numeric_op(require_one_arg(name, args)?, "log", f64::ln),
1325 "LOG10" => eval_unary_numeric_op(require_one_arg(name, args)?, "log10", f64::log10),
1326 "EXP" => eval_unary_numeric_op(require_one_arg(name, args)?, "exp", f64::exp),
1327 "SIN" => eval_unary_numeric_op(require_one_arg(name, args)?, "sin", f64::sin),
1328 "COS" => eval_unary_numeric_op(require_one_arg(name, args)?, "cos", f64::cos),
1329 "TAN" => eval_unary_numeric_op(require_one_arg(name, args)?, "tan", f64::tan),
1330 "ASIN" => eval_unary_numeric_op(require_one_arg(name, args)?, "asin", f64::asin),
1331 "ACOS" => eval_unary_numeric_op(require_one_arg(name, args)?, "acos", f64::acos),
1332 "ATAN" => eval_unary_numeric_op(require_one_arg(name, args)?, "atan", f64::atan),
1333 "DEGREES" => {
1334 eval_unary_numeric_op(require_one_arg(name, args)?, "degrees", f64::to_degrees)
1335 }
1336 "RADIANS" => {
1337 eval_unary_numeric_op(require_one_arg(name, args)?, "radians", f64::to_radians)
1338 }
1339 "HAVERSIN" => eval_unary_numeric_op(require_one_arg(name, args)?, "haversin", |f| {
1340 (1.0 - f.cos()) / 2.0
1341 }),
1342 "POWER" | "POW" => eval_power(args),
1344 "ATAN2" => eval_atan2(args),
1345 "PI" => {
1347 if !args.is_empty() {
1348 return Err(anyhow!("PI takes no arguments"));
1349 }
1350 Ok(Value::Float(std::f64::consts::PI))
1351 }
1352 "E" => {
1353 if !args.is_empty() {
1354 return Err(anyhow!("E takes no arguments"));
1355 }
1356 Ok(Value::Float(std::f64::consts::E))
1357 }
1358 "RAND" => {
1359 if !args.is_empty() {
1360 return Err(anyhow!("RAND takes no arguments"));
1361 }
1362 use rand::Rng;
1363 let mut rng = rand::thread_rng();
1364 Ok(Value::Float(rng.gen_range(0.0..1.0)))
1365 }
1366 _ => Err(anyhow!("Unknown math function: {}", name)),
1367 }
1368}
1369
1370fn eval_unary_string_op<F>(arg: &Value, func_name: &str, op: F) -> Result<Value>
1376where
1377 F: FnOnce(&str) -> String,
1378{
1379 match arg {
1380 Value::String(s) => Ok(Value::String(op(s))),
1381 Value::Null => Ok(Value::Null),
1382 _ => Err(anyhow!("{}() expects a string", func_name)),
1383 }
1384}
1385
1386fn eval_toupper(args: &[Value]) -> Result<Value> {
1387 let arg = require_one_arg("toUpper", args)?;
1388 eval_unary_string_op(arg, "toUpper", |s| s.to_uppercase())
1389}
1390
1391fn eval_tolower(args: &[Value]) -> Result<Value> {
1392 let arg = require_one_arg("toLower", args)?;
1393 eval_unary_string_op(arg, "toLower", |s| s.to_lowercase())
1394}
1395
1396fn eval_trim(args: &[Value]) -> Result<Value> {
1397 let arg = require_one_arg("trim", args)?;
1398 eval_unary_string_op(arg, "trim", |s| s.trim().to_string())
1399}
1400
1401fn eval_ltrim(args: &[Value]) -> Result<Value> {
1402 let arg = require_one_arg("ltrim", args)?;
1403 eval_unary_string_op(arg, "ltrim", |s| s.trim_start().to_string())
1404}
1405
1406fn eval_rtrim(args: &[Value]) -> Result<Value> {
1407 let arg = require_one_arg("rtrim", args)?;
1408 eval_unary_string_op(arg, "rtrim", |s| s.trim_end().to_string())
1409}
1410
1411fn eval_reverse(args: &[Value]) -> Result<Value> {
1412 let arg = require_one_arg("reverse", args)?;
1413 match arg {
1414 Value::String(s) => Ok(Value::String(s.chars().rev().collect())),
1415 Value::List(arr) => Ok(Value::List(arr.iter().rev().cloned().collect())),
1416 Value::Null => Ok(Value::Null),
1417 _ => Err(anyhow!("reverse() expects a string or list")),
1418 }
1419}
1420
1421fn eval_replace(args: &[Value]) -> Result<Value> {
1422 if args.len() != 3 {
1423 return Err(anyhow!("replace() requires 3 arguments"));
1424 }
1425 match (&args[0], &args[1], &args[2]) {
1426 (Value::String(s), Value::String(search), Value::String(replacement)) => Ok(Value::String(
1427 s.replace(search.as_str(), replacement.as_str()),
1428 )),
1429 (Value::Null, _, _) => Ok(Value::Null),
1430 _ => Err(anyhow!("replace() expects string arguments")),
1431 }
1432}
1433
1434pub(crate) fn eval_split(args: &[Value]) -> Result<Value> {
1435 if args.len() != 2 {
1436 return Err(anyhow!("split() requires 2 arguments"));
1437 }
1438 match (&args[0], &args[1]) {
1439 (Value::String(s), Value::String(delimiter)) => {
1440 let parts: Vec<Value> = s
1441 .split(delimiter.as_str())
1442 .map(|p| Value::String(p.to_string()))
1443 .collect();
1444 Ok(Value::List(parts))
1445 }
1446 (Value::Null, _) => Ok(Value::Null),
1447 _ => Err(anyhow!("split() expects string arguments")),
1448 }
1449}
1450
1451fn eval_substring(args: &[Value]) -> Result<Value> {
1452 if args.len() < 2 || args.len() > 3 {
1453 return Err(anyhow!("substring() requires 2 or 3 arguments"));
1454 }
1455 match &args[0] {
1456 Value::String(s) => {
1457 let start = args[1]
1458 .as_i64()
1459 .ok_or_else(|| anyhow!("substring() start must be an integer"))?
1460 as usize;
1461 let len = if args.len() == 3 {
1462 args[2]
1463 .as_i64()
1464 .ok_or_else(|| anyhow!("substring() length must be an integer"))?
1465 as usize
1466 } else {
1467 s.len().saturating_sub(start)
1468 };
1469 let chars: Vec<char> = s.chars().collect();
1470 let end = (start + len).min(chars.len());
1471 let result: String = chars[start.min(chars.len())..end].iter().collect();
1472 Ok(Value::String(result))
1473 }
1474 Value::Null => Ok(Value::Null),
1475 _ => Err(anyhow!("substring() expects a string")),
1476 }
1477}
1478
1479fn eval_left(args: &[Value]) -> Result<Value> {
1480 if args.len() != 2 {
1481 return Err(anyhow!("left() requires 2 arguments"));
1482 }
1483 match (&args[0], &args[1]) {
1484 (Value::String(s), n) if n.is_number() => {
1485 let len = n.as_i64().unwrap_or(0) as usize;
1486 Ok(Value::String(s.chars().take(len).collect()))
1487 }
1488 (Value::Null, _) => Ok(Value::Null),
1489 _ => Err(anyhow!("left() expects a string and integer")),
1490 }
1491}
1492
1493fn eval_right(args: &[Value]) -> Result<Value> {
1494 if args.len() != 2 {
1495 return Err(anyhow!("right() requires 2 arguments"));
1496 }
1497 match (&args[0], &args[1]) {
1498 (Value::String(s), n) if n.is_number() => {
1499 let len = n.as_i64().unwrap_or(0) as usize;
1500 let chars: Vec<char> = s.chars().collect();
1501 let start = chars.len().saturating_sub(len);
1502 Ok(Value::String(chars[start..].iter().collect()))
1503 }
1504 (Value::Null, _) => Ok(Value::Null),
1505 _ => Err(anyhow!("right() expects a string and integer")),
1506 }
1507}
1508
1509fn eval_pad(func_name: &str, args: &[Value], pad_left: bool) -> Result<Value> {
1511 if args.len() < 2 || args.len() > 3 {
1512 return Err(anyhow!("{}() requires 2 or 3 arguments", func_name));
1513 }
1514 let s = match &args[0] {
1515 Value::String(s) => s,
1516 Value::Null => return Ok(Value::Null),
1517 _ => {
1518 return Err(anyhow!(
1519 "{}() expects a string as first argument",
1520 func_name
1521 ));
1522 }
1523 };
1524 let len = match &args[1] {
1525 Value::Int(n) => *n as usize,
1526 Value::Float(f) => *f as i64 as usize,
1527 Value::Null => return Ok(Value::Null),
1528 _ => {
1529 return Err(anyhow!(
1530 "{}() expects an integer as second argument",
1531 func_name
1532 ));
1533 }
1534 };
1535 if len > 1_000_000 {
1536 return Err(anyhow!(
1537 "{}() length exceeds maximum limit of 1,000,000",
1538 func_name
1539 ));
1540 }
1541 let pad_str = if args.len() == 3 {
1542 match &args[2] {
1543 Value::String(p) => p.as_str(),
1544 Value::Null => return Ok(Value::Null),
1545 _ => {
1546 return Err(anyhow!(
1547 "{}() expects a string as third argument",
1548 func_name
1549 ));
1550 }
1551 }
1552 } else {
1553 " "
1554 };
1555
1556 let s_chars: Vec<char> = s.chars().collect();
1557 if s_chars.len() >= len {
1558 return Ok(Value::String(s_chars.into_iter().take(len).collect()));
1559 }
1560
1561 let pad_chars: Vec<char> = pad_str.chars().collect();
1562 if pad_chars.is_empty() {
1563 return Ok(Value::String(s.clone()));
1564 }
1565
1566 let needed = len - s_chars.len();
1567 let full_pads = needed / pad_chars.len();
1568 let partial_pad = needed % pad_chars.len();
1569
1570 let mut padding = String::with_capacity(needed);
1571 for _ in 0..full_pads {
1572 padding.push_str(pad_str);
1573 }
1574 padding.extend(pad_chars.into_iter().take(partial_pad));
1575
1576 let result = if pad_left {
1577 format!("{}{}", padding, s)
1578 } else {
1579 format!("{}{}", s, padding)
1580 };
1581 Ok(Value::String(result))
1582}
1583
1584fn eval_lpad(args: &[Value]) -> Result<Value> {
1585 eval_pad("lpad", args, true)
1586}
1587
1588fn eval_rpad(args: &[Value]) -> Result<Value> {
1589 eval_pad("rpad", args, false)
1590}
1591
1592fn eval_string_function(name: &str, args: &[Value]) -> Result<Value> {
1594 match name {
1595 "TOUPPER" | "UPPER" => eval_toupper(args),
1596 "TOLOWER" | "LOWER" => eval_tolower(args),
1597 "TRIM" => eval_trim(args),
1598 "LTRIM" => eval_ltrim(args),
1599 "RTRIM" => eval_rtrim(args),
1600 "REVERSE" => eval_reverse(args),
1601 "REPLACE" => eval_replace(args),
1602 "SPLIT" => eval_split(args),
1603 "SUBSTRING" => eval_substring(args),
1604 "LEFT" => eval_left(args),
1605 "RIGHT" => eval_right(args),
1606 "LPAD" => eval_lpad(args),
1607 "RPAD" => eval_rpad(args),
1608 _ => Err(anyhow!("Unknown string function: {}", name)),
1609 }
1610}
1611
1612fn eval_range_function(args: &[Value]) -> Result<Value> {
1614 if args.len() < 2 || args.len() > 3 {
1615 return Err(anyhow!("range() requires 2 or 3 arguments"));
1616 }
1617 let start = args[0]
1618 .as_i64()
1619 .ok_or_else(|| anyhow!("range() start must be an integer"))?;
1620 let end = args[1]
1621 .as_i64()
1622 .ok_or_else(|| anyhow!("range() end must be an integer"))?;
1623 let step = if args.len() == 3 {
1624 args[2]
1625 .as_i64()
1626 .ok_or_else(|| anyhow!("range() step must be an integer"))?
1627 } else {
1628 1
1629 };
1630 if step == 0 {
1631 return Err(anyhow!("range() step cannot be zero"));
1632 }
1633 let mut result = Vec::new();
1634 let mut i = start;
1635 if step > 0 {
1636 while i <= end {
1637 result.push(Value::Int(i));
1638 i += step;
1639 }
1640 } else {
1641 while i >= end {
1642 result.push(Value::Int(i));
1643 i += step;
1644 }
1645 }
1646 Ok(Value::List(result))
1647}
1648
1649pub fn eval_scalar_function(name: &str, args: &[Value]) -> Result<Value> {
1654 let name_upper = name.to_uppercase();
1655
1656 match name_upper.as_str() {
1657 "COALESCE" => {
1659 for arg in args {
1660 if !arg.is_null() {
1661 return Ok(arg.clone());
1662 }
1663 }
1664 Ok(Value::Null)
1665 }
1666 "NULLIF" => {
1667 if args.len() != 2 {
1668 return Err(anyhow!("NULLIF requires 2 arguments"));
1669 }
1670 if args[0] == args[1] {
1671 Ok(Value::Null)
1672 } else {
1673 Ok(args[0].clone())
1674 }
1675 }
1676
1677 "SIZE" | "KEYS" | "HEAD" | "TAIL" | "LAST" | "LENGTH" | "NODES" | "RELATIONSHIPS" => {
1679 eval_list_function(&name_upper, args)
1680 }
1681
1682 "TOINTEGER" | "TOINT" | "TOFLOAT" | "TOSTRING" | "TOBOOLEAN" | "TOBOOL" => {
1684 eval_type_function(&name_upper, args)
1685 }
1686
1687 "ABS" | "CEIL" | "FLOOR" | "ROUND" | "SQRT" | "SIGN" | "LOG" | "LOG10" | "EXP"
1689 | "POWER" | "POW" | "SIN" | "COS" | "TAN" | "ASIN" | "ACOS" | "ATAN" | "ATAN2"
1690 | "DEGREES" | "RADIANS" | "HAVERSIN" | "PI" | "E" | "RAND" => {
1691 eval_math_function(&name_upper, args)
1692 }
1693
1694 "TOUPPER" | "UPPER" | "TOLOWER" | "LOWER" | "TRIM" | "LTRIM" | "RTRIM" | "REVERSE"
1696 | "REPLACE" | "SPLIT" | "SUBSTRING" | "LEFT" | "RIGHT" | "LPAD" | "RPAD" => {
1697 eval_string_function(&name_upper, args)
1698 }
1699
1700 "DATE"
1702 | "TIME"
1703 | "DATETIME"
1704 | "LOCALDATETIME"
1705 | "LOCALTIME"
1706 | "DURATION"
1707 | "YEAR"
1708 | "MONTH"
1709 | "DAY"
1710 | "HOUR"
1711 | "MINUTE"
1712 | "SECOND"
1713 | "DATETIME.FROMEPOCH"
1714 | "DATETIME.FROMEPOCHMILLIS"
1715 | "DATE.TRUNCATE"
1716 | "TIME.TRUNCATE"
1717 | "DATETIME.TRUNCATE"
1718 | "LOCALDATETIME.TRUNCATE"
1719 | "LOCALTIME.TRUNCATE"
1720 | "DATETIME.TRANSACTION"
1721 | "DATETIME.STATEMENT"
1722 | "DATETIME.REALTIME"
1723 | "DATE.TRANSACTION"
1724 | "DATE.STATEMENT"
1725 | "DATE.REALTIME"
1726 | "TIME.TRANSACTION"
1727 | "TIME.STATEMENT"
1728 | "TIME.REALTIME"
1729 | "LOCALTIME.TRANSACTION"
1730 | "LOCALTIME.STATEMENT"
1731 | "LOCALTIME.REALTIME"
1732 | "LOCALDATETIME.TRANSACTION"
1733 | "LOCALDATETIME.STATEMENT"
1734 | "LOCALDATETIME.REALTIME"
1735 | "DURATION.BETWEEN"
1736 | "DURATION.INMONTHS"
1737 | "DURATION.INDAYS"
1738 | "DURATION.INSECONDS" => eval_datetime_function(&name_upper, args),
1739
1740 "POINT" | "DISTANCE" | "POINT.WITHINBBOX" => eval_spatial_function(&name_upper, args),
1742
1743 "RANGE" => eval_range_function(args),
1744
1745 "UNI.TEMPORAL.VALIDAT" => eval_valid_at(args),
1746
1747 "VECTOR_DISTANCE" => {
1748 if args.len() < 2 || args.len() > 3 {
1749 return Err(anyhow!("vector_distance requires 2 or 3 arguments"));
1750 }
1751 let metric = if args.len() == 3 {
1752 args[2].as_str().ok_or(anyhow!("metric must be string"))?
1753 } else {
1754 "cosine"
1755 };
1756 eval_vector_distance(&args[0], &args[1], metric)
1757 }
1758
1759 "UNI_BITWISE_OR"
1761 | "UNI_BITWISE_AND"
1762 | "UNI_BITWISE_XOR"
1763 | "UNI_BITWISE_NOT"
1764 | "UNI_BITWISE_SHIFTLEFT"
1765 | "UNI_BITWISE_SHIFTRIGHT" => eval_bitwise_function(&name_upper, args),
1766
1767 _ => Err(anyhow!("Function {} not implemented or is aggregate", name)),
1768 }
1769}
1770
1771fn eval_valid_at(args: &[Value]) -> Result<Value> {
1779 if args.len() != 4 {
1780 return Err(anyhow!(
1781 "validAt requires 4 arguments: node, start_prop, end_prop, time"
1782 ));
1783 }
1784
1785 let node_map = match &args[0] {
1786 Value::Map(map) => map,
1787 Value::Null => return Ok(Value::Bool(false)),
1788 _ => {
1789 return Err(anyhow!(
1790 "validAt expects a Node or Edge (Object) as first argument"
1791 ));
1792 }
1793 };
1794
1795 let start_prop = args[1]
1796 .as_str()
1797 .ok_or_else(|| anyhow!("start_prop must be a string"))?;
1798 let end_prop = args[2]
1799 .as_str()
1800 .ok_or_else(|| anyhow!("end_prop must be a string"))?;
1801
1802 let time_str = match &args[3] {
1803 Value::String(s) => s,
1804 _ => return Err(anyhow!("time argument must be a datetime string")),
1805 };
1806
1807 let query_time = parse_datetime_utc(time_str)
1808 .map_err(|_| anyhow!("Invalid query time format: {}", time_str))?;
1809
1810 let valid_from_val = node_map.get(start_prop);
1811 let valid_from = match valid_from_val {
1812 Some(Value::String(s)) => parse_datetime_utc(s)
1813 .map_err(|_| anyhow!("Invalid datetime in property {}: {}", start_prop, s))?,
1814 Some(Value::Null) | None => return Ok(Value::Bool(false)),
1815 _ => return Err(anyhow!("Property {} must be a datetime string", start_prop)),
1816 };
1817
1818 let valid_to_val = node_map.get(end_prop);
1819 let valid_to = match valid_to_val {
1820 Some(Value::String(s)) => Some(
1821 parse_datetime_utc(s)
1822 .map_err(|_| anyhow!("Invalid datetime in property {}: {}", end_prop, s))?,
1823 ),
1824 Some(Value::Null) | None => None,
1825 _ => {
1826 return Err(anyhow!(
1827 "Property {} must be a datetime string or null",
1828 end_prop
1829 ));
1830 }
1831 };
1832
1833 let is_valid = valid_from <= query_time && valid_to.map(|vt| query_time < vt).unwrap_or(true);
1835
1836 Ok(Value::Bool(is_valid))
1837}
1838
1839pub fn eval_vector_similarity(v1: &Value, v2: &Value) -> Result<Value> {
1841 let (arr1, arr2) = match (v1, v2) {
1842 (Value::List(a1), Value::List(a2)) => (a1, a2),
1843 _ => return Err(anyhow!("vector_similarity arguments must be arrays")),
1844 };
1845
1846 if arr1.len() != arr2.len() {
1847 return Err(anyhow!(
1848 "Vector dimensions mismatch: {} vs {}",
1849 arr1.len(),
1850 arr2.len()
1851 ));
1852 }
1853
1854 let mut dot = 0.0;
1855 let mut norm1_sq = 0.0;
1856 let mut norm2_sq = 0.0;
1857
1858 for (v1_elem, v2_elem) in arr1.iter().zip(arr2.iter()) {
1859 let f1 = v1_elem
1860 .as_f64()
1861 .ok_or_else(|| anyhow!("Vector element not a number"))?;
1862 let f2 = v2_elem
1863 .as_f64()
1864 .ok_or_else(|| anyhow!("Vector element not a number"))?;
1865 dot += f1 * f2;
1866 norm1_sq += f1 * f1;
1867 norm2_sq += f2 * f2;
1868 }
1869
1870 let mag1 = norm1_sq.sqrt();
1871 let mag2 = norm2_sq.sqrt();
1872
1873 let sim = if mag1 == 0.0 || mag2 == 0.0 {
1874 0.0
1875 } else {
1876 dot / (mag1 * mag2)
1877 };
1878
1879 Ok(Value::Float(sim))
1880}
1881
1882pub fn eval_vector_distance(v1: &Value, v2: &Value, metric: &str) -> Result<Value> {
1884 let (arr1, arr2) = match (v1, v2) {
1885 (Value::List(a1), Value::List(a2)) => (a1, a2),
1886 _ => return Err(anyhow!("vector_distance arguments must be arrays")),
1887 };
1888
1889 if arr1.len() != arr2.len() {
1890 return Err(anyhow!(
1891 "Vector dimensions mismatch: {} vs {}",
1892 arr1.len(),
1893 arr2.len()
1894 ));
1895 }
1896
1897 let iter1 = arr1
1899 .iter()
1900 .map(|v| v.as_f64().ok_or(anyhow!("Vector element not a number")));
1901 let iter2 = arr2
1902 .iter()
1903 .map(|v| v.as_f64().ok_or(anyhow!("Vector element not a number")));
1904
1905 match metric.to_lowercase().as_str() {
1906 "cosine" => {
1907 let mut dot = 0.0;
1909 let mut norm1_sq = 0.0;
1910 let mut norm2_sq = 0.0;
1911
1912 for (r1, r2) in iter1.zip(iter2) {
1913 let f1 = r1?;
1914 let f2 = r2?;
1915 dot += f1 * f2;
1916 norm1_sq += f1 * f1;
1917 norm2_sq += f2 * f2;
1918 }
1919
1920 let mag1 = norm1_sq.sqrt();
1921 let mag2 = norm2_sq.sqrt();
1922
1923 if mag1 == 0.0 || mag2 == 0.0 {
1924 Ok(Value::Float(1.0))
1925 } else {
1926 let sim = dot / (mag1 * mag2);
1927 let sim = sim.clamp(-1.0, 1.0);
1929 Ok(Value::Float(1.0 - sim))
1930 }
1931 }
1932 "euclidean" | "l2" => {
1933 let mut sum_sq_diff = 0.0;
1934 for (r1, r2) in iter1.zip(iter2) {
1935 let f1 = r1?;
1936 let f2 = r2?;
1937 let diff = f1 - f2;
1938 sum_sq_diff += diff * diff;
1939 }
1940 Ok(Value::Float(sum_sq_diff.sqrt()))
1941 }
1942 "dot" | "inner_product" => {
1943 let mut dot = 0.0;
1944 for (r1, r2) in iter1.zip(iter2) {
1945 let f1 = r1?;
1946 let f2 = r2?;
1947 dot += f1 * f2;
1948 }
1949 Ok(Value::Float(1.0 - dot))
1950 }
1951 _ => Err(anyhow!("Unknown metric: {}", metric)),
1952 }
1953}
1954
1955pub fn is_scalar_function(name: &str) -> bool {
1957 let name_upper = name.to_uppercase();
1958 matches!(
1959 name_upper.as_str(),
1960 "COALESCE"
1961 | "NULLIF"
1962 | "SIZE"
1963 | "KEYS"
1964 | "HEAD"
1965 | "TAIL"
1966 | "LAST"
1967 | "LENGTH"
1968 | "NODES"
1969 | "RELATIONSHIPS"
1970 | "TOINTEGER"
1971 | "TOINT"
1972 | "TOFLOAT"
1973 | "TOSTRING"
1974 | "TOBOOLEAN"
1975 | "TOBOOL"
1976 | "ABS"
1977 | "CEIL"
1978 | "FLOOR"
1979 | "ROUND"
1980 | "SQRT"
1981 | "SIGN"
1982 | "LOG"
1983 | "LOG10"
1984 | "EXP"
1985 | "POWER"
1986 | "POW"
1987 | "SIN"
1988 | "COS"
1989 | "TAN"
1990 | "ASIN"
1991 | "ACOS"
1992 | "ATAN"
1993 | "ATAN2"
1994 | "DEGREES"
1995 | "RADIANS"
1996 | "HAVERSIN"
1997 | "PI"
1998 | "E"
1999 | "RAND"
2000 | "TOUPPER"
2001 | "UPPER"
2002 | "TOLOWER"
2003 | "LOWER"
2004 | "TRIM"
2005 | "LTRIM"
2006 | "RTRIM"
2007 | "REVERSE"
2008 | "REPLACE"
2009 | "SPLIT"
2010 | "SUBSTRING"
2011 | "LEFT"
2012 | "RIGHT"
2013 | "LPAD"
2014 | "RPAD"
2015 | "RANGE"
2016 | "UNI.VALIDAT"
2017 | "VALIDAT"
2018 | "VECTOR_SIMILARITY"
2019 | "VECTOR_DISTANCE"
2020 | "DATE"
2021 | "TIME"
2022 | "DATETIME"
2023 | "DURATION"
2024 | "YEAR"
2025 | "MONTH"
2026 | "DAY"
2027 | "HOUR"
2028 | "MINUTE"
2029 | "SECOND"
2030 | "ID"
2031 | "ELEMENTID"
2032 | "TYPE"
2033 | "LABELS"
2034 | "PROPERTIES"
2035 | "STARTNODE"
2036 | "ENDNODE"
2037 | "ANY"
2038 | "ALL"
2039 | "NONE"
2040 | "SINGLE"
2041 )
2042}
2043
2044fn eval_bitwise_function(name: &str, args: &[Value]) -> Result<Value> {
2046 let require_int = |v: &Value, fname: &str| -> Result<i64> {
2047 v.as_i64()
2048 .ok_or_else(|| anyhow!("{} requires integer arguments", fname))
2049 };
2050
2051 let bitwise_binary = |fname: &str, op: fn(i64, i64) -> i64| -> Result<Value> {
2052 if args.len() != 2 {
2053 return Err(anyhow!("{} requires exactly 2 arguments", fname));
2054 }
2055 let l = require_int(&args[0], fname)?;
2056 let r = require_int(&args[1], fname)?;
2057 Ok(Value::Int(op(l, r)))
2058 };
2059
2060 match name {
2061 "UNI_BITWISE_OR" => bitwise_binary("uni_bitwise_or", |l, r| l | r),
2062 "UNI_BITWISE_AND" => bitwise_binary("uni_bitwise_and", |l, r| l & r),
2063 "UNI_BITWISE_XOR" => bitwise_binary("uni_bitwise_xor", |l, r| l ^ r),
2064 "UNI_BITWISE_SHIFTLEFT" => bitwise_binary("uni_bitwise_shiftLeft", |l, r| l << r),
2065 "UNI_BITWISE_SHIFTRIGHT" => bitwise_binary("uni_bitwise_shiftRight", |l, r| l >> r),
2066 "UNI_BITWISE_NOT" => {
2067 if args.len() != 1 {
2068 return Err(anyhow!("uni_bitwise_not requires exactly 1 argument"));
2069 }
2070 Ok(Value::Int(!require_int(&args[0], "uni_bitwise_not")?))
2071 }
2072 _ => Err(anyhow!("Unknown bitwise function: {}", name)),
2073 }
2074}
2075
2076#[cfg(test)]
2077mod tests {
2078 use super::*;
2079 fn s(v: &str) -> Value {
2081 Value::String(v.into())
2082 }
2083 fn i(v: i64) -> Value {
2085 Value::Int(v)
2086 }
2087
2088 #[test]
2089 fn test_binary_op_eq() {
2090 assert_eq!(
2091 eval_binary_op(&i(1), &BinaryOp::Eq, &i(1)).unwrap(),
2092 Value::Bool(true)
2093 );
2094 assert_eq!(
2095 eval_binary_op(&i(1), &BinaryOp::Eq, &i(2)).unwrap(),
2096 Value::Bool(false)
2097 );
2098 }
2099
2100 #[test]
2101 fn test_binary_op_comparison() {
2102 assert_eq!(
2103 eval_binary_op(&i(5), &BinaryOp::Gt, &i(3)).unwrap(),
2104 Value::Bool(true)
2105 );
2106 assert_eq!(
2107 eval_binary_op(&i(5), &BinaryOp::Lt, &i(3)).unwrap(),
2108 Value::Bool(false)
2109 );
2110 }
2111
2112 #[test]
2113 fn test_binary_op_xor() {
2114 assert_eq!(
2116 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2117 Value::Bool(false)
2118 );
2119 assert_eq!(
2121 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(false)).unwrap(),
2122 Value::Bool(true)
2123 );
2124 assert_eq!(
2126 eval_binary_op(&Value::Bool(false), &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2127 Value::Bool(true)
2128 );
2129 assert_eq!(
2131 eval_binary_op(&Value::Bool(false), &BinaryOp::Xor, &Value::Bool(false)).unwrap(),
2132 Value::Bool(false)
2133 );
2134 }
2135
2136 #[test]
2137 fn test_binary_op_contains() {
2138 assert_eq!(
2139 eval_binary_op(&s("hello world"), &BinaryOp::Contains, &s("world")).unwrap(),
2140 Value::Bool(true)
2141 );
2142 }
2143
2144 #[test]
2145 fn test_scalar_function_size() {
2146 assert_eq!(
2147 eval_scalar_function("SIZE", &[Value::List(vec![i(1), i(2), i(3)])]).unwrap(),
2148 Value::Int(3)
2149 );
2150 }
2151
2152 #[test]
2153 fn test_scalar_function_head() {
2154 assert_eq!(
2155 eval_scalar_function("HEAD", &[Value::List(vec![i(1), i(2), i(3)])]).unwrap(),
2156 Value::Int(1)
2157 );
2158 }
2159
2160 #[test]
2161 fn test_scalar_function_coalesce() {
2162 assert_eq!(
2163 eval_scalar_function("COALESCE", &[Value::Null, Value::Int(1), Value::Int(2)]).unwrap(),
2164 Value::Int(1)
2165 );
2166 }
2167
2168 #[test]
2169 fn test_vector_similarity() {
2170 let v1 = Value::List(vec![Value::Float(1.0), Value::Float(0.0)]);
2171 let v2 = Value::List(vec![Value::Float(1.0), Value::Float(0.0)]);
2172 let result = eval_vector_similarity(&v1, &v2).unwrap();
2173 assert_eq!(result.as_f64().unwrap(), 1.0);
2174 }
2175
2176 #[test]
2177 fn test_regex_match() {
2178 assert_eq!(
2180 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("hello.*")).unwrap(),
2181 Value::Bool(true)
2182 );
2183
2184 assert_eq!(
2186 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("^world")).unwrap(),
2187 Value::Bool(false)
2188 );
2189
2190 assert_eq!(
2192 eval_binary_op(&s("Hello"), &BinaryOp::Regex, &s("hello")).unwrap(),
2193 Value::Bool(false)
2194 );
2195
2196 assert_eq!(
2198 eval_binary_op(&s("Hello"), &BinaryOp::Regex, &s("(?i)hello")).unwrap(),
2199 Value::Bool(true)
2200 );
2201 }
2202
2203 #[test]
2204 fn test_regex_null_handling() {
2205 assert_eq!(
2207 eval_binary_op(&Value::Null, &BinaryOp::Regex, &s(".*")).unwrap(),
2208 Value::Null
2209 );
2210
2211 assert_eq!(
2213 eval_binary_op(&s("hello"), &BinaryOp::Regex, &Value::Null).unwrap(),
2214 Value::Null
2215 );
2216 }
2217
2218 #[test]
2219 fn test_regex_invalid_pattern() {
2220 let result = eval_binary_op(&s("hello"), &BinaryOp::Regex, &s("[invalid"));
2222 assert!(result.is_err());
2223 assert!(result.unwrap_err().to_string().contains("Invalid regex"));
2224 }
2225
2226 #[test]
2227 fn test_regex_special_characters() {
2228 assert_eq!(
2230 eval_binary_op(
2231 &s("test@example.com"),
2232 &BinaryOp::Regex,
2233 &s(r"^[\w.-]+@[\w.-]+\.\w+$")
2234 )
2235 .unwrap(),
2236 Value::Bool(true)
2237 );
2238
2239 assert_eq!(
2241 eval_binary_op(
2242 &s("123-456-7890"),
2243 &BinaryOp::Regex,
2244 &s(r"^\d{3}-\d{3}-\d{4}$")
2245 )
2246 .unwrap(),
2247 Value::Bool(true)
2248 );
2249
2250 assert_eq!(
2252 eval_binary_op(
2253 &s("1234567890"),
2254 &BinaryOp::Regex,
2255 &s(r"^\d{3}-\d{3}-\d{4}$")
2256 )
2257 .unwrap(),
2258 Value::Bool(false)
2259 );
2260 }
2261
2262 #[test]
2263 fn test_regex_anchors() {
2264 assert_eq!(
2266 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("^hello")).unwrap(),
2267 Value::Bool(true)
2268 );
2269 assert_eq!(
2270 eval_binary_op(&s("say hello"), &BinaryOp::Regex, &s("^hello")).unwrap(),
2271 Value::Bool(false)
2272 );
2273
2274 assert_eq!(
2276 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("world$")).unwrap(),
2277 Value::Bool(true)
2278 );
2279 assert_eq!(
2280 eval_binary_op(&s("world hello"), &BinaryOp::Regex, &s("world$")).unwrap(),
2281 Value::Bool(false)
2282 );
2283
2284 assert_eq!(
2286 eval_binary_op(&s("hello"), &BinaryOp::Regex, &s("^hello$")).unwrap(),
2287 Value::Bool(true)
2288 );
2289 assert_eq!(
2290 eval_binary_op(&s("hello world"), &BinaryOp::Regex, &s("^hello$")).unwrap(),
2291 Value::Bool(false)
2292 );
2293 }
2294
2295 #[test]
2296 fn test_temporal_arithmetic() {
2297 let dt = s("2024-01-15T10:00:00Z");
2299 let dur = Value::Int(3_600_000_000_i64);
2300 let result = eval_binary_op(&dt, &BinaryOp::Add, &dur).unwrap();
2301 assert!(result.to_string().contains("11:00"));
2302
2303 let d = s("2024-01-01");
2305 let dur_day = Value::Int(86_400_000_000_i64);
2306 let result = eval_binary_op(&d, &BinaryOp::Add, &dur_day).unwrap();
2307 assert_eq!(result.to_string(), "2024-01-02");
2308
2309 let dt1 = s("2024-01-02T00:00:00Z");
2311 let dt2 = s("2024-01-01T00:00:00Z");
2312 let result = eval_binary_op(&dt1, &BinaryOp::Sub, &dt2).unwrap();
2313 let dur_str = result.to_string();
2315 assert!(dur_str.starts_with('P'));
2316 assert!(dur_str.contains("24H")); }
2318
2319 #[test]
2323 fn test_temporal_arithmetic_edge_cases() {
2324 let dt = s("2024-01-15T10:00:00Z");
2326 let neg_dur = Value::Int(-3_600_000_000_i64); let result = eval_binary_op(&dt, &BinaryOp::Add, &neg_dur).unwrap();
2328 assert!(result.to_string().contains("09:00"));
2329
2330 let dur1 = s("PT1H"); let dur2 = s("PT2H"); let result = eval_binary_op(&dur1, &BinaryOp::Sub, &dur2).unwrap();
2334 let dur_str = result.to_string();
2336 assert!(dur_str.starts_with('P') || dur_str.starts_with("-P"));
2337
2338 let dt = s("2024-01-15T10:00:00Z");
2340 let zero_dur = Value::Int(0_i64);
2341 let result = eval_binary_op(&dt, &BinaryOp::Add, &zero_dur).unwrap();
2342 assert!(result.to_string().contains("10:00"));
2343
2344 let d = s("2023-12-31");
2346 let one_day = Value::Int(86_400_000_000_i64);
2347 let result = eval_binary_op(&d, &BinaryOp::Add, &one_day).unwrap();
2348 assert_eq!(result.to_string(), "2024-01-01");
2349
2350 let dt1 = s("2024-01-15T10:00:00Z");
2352 let dt2 = s("2024-01-15T10:00:00Z");
2353 let result = eval_binary_op(&dt1, &BinaryOp::Sub, &dt2).unwrap();
2354 let dur_str = result.to_string();
2356 assert!(dur_str.starts_with('P'));
2357
2358 let leap_day = s("2024-02-28");
2360 let one_day = Value::Int(86_400_000_000_i64);
2361 let result = eval_binary_op(&leap_day, &BinaryOp::Add, &one_day).unwrap();
2362 assert_eq!(result.to_string(), "2024-02-29");
2363 }
2364
2365 #[test]
2366 fn test_regex_empty_string() {
2367 assert_eq!(
2369 eval_binary_op(&s(""), &BinaryOp::Regex, &s("^$")).unwrap(),
2370 Value::Bool(true)
2371 );
2372
2373 assert_eq!(
2375 eval_binary_op(&s(""), &BinaryOp::Regex, &s(".+")).unwrap(),
2376 Value::Bool(false)
2377 );
2378
2379 assert_eq!(
2381 eval_binary_op(&s("hello"), &BinaryOp::Regex, &s(".*")).unwrap(),
2382 Value::Bool(true)
2383 );
2384 }
2385
2386 #[test]
2387 fn test_regex_type_errors() {
2388 let result = eval_binary_op(&Value::Int(123), &BinaryOp::Regex, &s("\\d+"));
2390 assert!(result.is_err());
2391 assert!(result.unwrap_err().to_string().contains("must be a string"));
2392
2393 let result = eval_binary_op(&s("hello"), &BinaryOp::Regex, &Value::Int(123));
2395 assert!(result.is_err());
2396 assert!(result.unwrap_err().to_string().contains("pattern string"));
2397 }
2398
2399 #[test]
2400 fn test_and_null_handling() {
2401 assert_eq!(
2405 eval_binary_op(&Value::Bool(false), &BinaryOp::And, &Value::Null).unwrap(),
2406 Value::Bool(false)
2407 );
2408 assert_eq!(
2409 eval_binary_op(&Value::Null, &BinaryOp::And, &Value::Bool(false)).unwrap(),
2410 Value::Bool(false)
2411 );
2412
2413 assert_eq!(
2415 eval_binary_op(&Value::Bool(true), &BinaryOp::And, &Value::Null).unwrap(),
2416 Value::Null
2417 );
2418 assert_eq!(
2419 eval_binary_op(&Value::Null, &BinaryOp::And, &Value::Bool(true)).unwrap(),
2420 Value::Null
2421 );
2422
2423 assert_eq!(
2425 eval_binary_op(&Value::Null, &BinaryOp::And, &Value::Null).unwrap(),
2426 Value::Null
2427 );
2428
2429 assert_eq!(
2431 eval_binary_op(&Value::Bool(true), &BinaryOp::And, &Value::Bool(true)).unwrap(),
2432 Value::Bool(true)
2433 );
2434 assert_eq!(
2435 eval_binary_op(&Value::Bool(true), &BinaryOp::And, &Value::Bool(false)).unwrap(),
2436 Value::Bool(false)
2437 );
2438 }
2439
2440 #[test]
2441 fn test_or_null_handling() {
2442 assert_eq!(
2446 eval_binary_op(&Value::Bool(true), &BinaryOp::Or, &Value::Null).unwrap(),
2447 Value::Bool(true)
2448 );
2449 assert_eq!(
2450 eval_binary_op(&Value::Null, &BinaryOp::Or, &Value::Bool(true)).unwrap(),
2451 Value::Bool(true)
2452 );
2453
2454 assert_eq!(
2456 eval_binary_op(&Value::Bool(false), &BinaryOp::Or, &Value::Null).unwrap(),
2457 Value::Null
2458 );
2459 assert_eq!(
2460 eval_binary_op(&Value::Null, &BinaryOp::Or, &Value::Bool(false)).unwrap(),
2461 Value::Null
2462 );
2463
2464 assert_eq!(
2466 eval_binary_op(&Value::Null, &BinaryOp::Or, &Value::Null).unwrap(),
2467 Value::Null
2468 );
2469
2470 assert_eq!(
2472 eval_binary_op(&Value::Bool(false), &BinaryOp::Or, &Value::Bool(false)).unwrap(),
2473 Value::Bool(false)
2474 );
2475 assert_eq!(
2476 eval_binary_op(&Value::Bool(true), &BinaryOp::Or, &Value::Bool(false)).unwrap(),
2477 Value::Bool(true)
2478 );
2479 }
2480
2481 #[test]
2482 fn test_nan_comparison_with_non_numeric() {
2483 let nan = Value::Float(f64::NAN);
2484
2485 assert_eq!(
2487 eval_binary_op(&nan, &BinaryOp::Gt, &i(1)).unwrap(),
2488 Value::Bool(false)
2489 );
2490
2491 assert_eq!(
2493 eval_binary_op(&nan, &BinaryOp::Gt, &nan).unwrap(),
2494 Value::Bool(false)
2495 );
2496
2497 assert_eq!(
2499 eval_binary_op(&nan, &BinaryOp::Gt, &s("a")).unwrap(),
2500 Value::Null
2501 );
2502
2503 assert_eq!(
2505 eval_binary_op(&s("a"), &BinaryOp::Lt, &nan).unwrap(),
2506 Value::Null
2507 );
2508 }
2509
2510 #[test]
2511 fn test_nan_equality_with_non_numeric() {
2512 let nan = Value::Float(f64::NAN);
2513
2514 assert_eq!(
2516 eval_binary_op(&nan, &BinaryOp::Eq, &nan).unwrap(),
2517 Value::Bool(false)
2518 );
2519
2520 assert_eq!(
2522 eval_binary_op(&nan, &BinaryOp::NotEq, &nan).unwrap(),
2523 Value::Bool(true)
2524 );
2525
2526 assert_eq!(
2528 eval_binary_op(&nan, &BinaryOp::Eq, &s("a")).unwrap(),
2529 Value::Bool(false)
2530 );
2531
2532 assert_eq!(
2534 eval_binary_op(&nan, &BinaryOp::NotEq, &s("a")).unwrap(),
2535 Value::Bool(true)
2536 );
2537 }
2538
2539 #[test]
2540 fn test_large_integer_equality() {
2541 let a = Value::Int(4611686018427387905_i64);
2543 let b = Value::Int(4611686018427387900_i64);
2544
2545 assert_eq!(
2546 eval_binary_op(&a, &BinaryOp::Eq, &b).unwrap(),
2547 Value::Bool(false)
2548 );
2549 assert_eq!(
2550 eval_binary_op(&a, &BinaryOp::Eq, &a).unwrap(),
2551 Value::Bool(true)
2552 );
2553 }
2554
2555 #[test]
2556 fn test_large_integer_ordering() {
2557 let a = Value::Int(4611686018427387905_i64);
2558 let b = Value::Int(4611686018427387900_i64);
2559
2560 assert_eq!(
2561 eval_binary_op(&a, &BinaryOp::Gt, &b).unwrap(),
2562 Value::Bool(true)
2563 );
2564 assert_eq!(
2565 eval_binary_op(&b, &BinaryOp::Lt, &a).unwrap(),
2566 Value::Bool(true)
2567 );
2568 }
2569
2570 #[test]
2571 fn test_int_float_equality_still_works() {
2572 assert_eq!(
2574 eval_binary_op(&i(1), &BinaryOp::Eq, &Value::Float(1.0)).unwrap(),
2575 Value::Bool(true)
2576 );
2577 assert_eq!(
2578 eval_binary_op(&i(1), &BinaryOp::NotEq, &Value::Float(1.0)).unwrap(),
2579 Value::Bool(false)
2580 );
2581 }
2582
2583 #[test]
2584 fn test_xor_null_handling() {
2585 assert_eq!(
2588 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Null).unwrap(),
2589 Value::Null
2590 );
2591 assert_eq!(
2592 eval_binary_op(&Value::Bool(false), &BinaryOp::Xor, &Value::Null).unwrap(),
2593 Value::Null
2594 );
2595 assert_eq!(
2596 eval_binary_op(&Value::Null, &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2597 Value::Null
2598 );
2599 assert_eq!(
2600 eval_binary_op(&Value::Null, &BinaryOp::Xor, &Value::Null).unwrap(),
2601 Value::Null
2602 );
2603
2604 assert_eq!(
2606 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(false)).unwrap(),
2607 Value::Bool(true)
2608 );
2609 assert_eq!(
2610 eval_binary_op(&Value::Bool(true), &BinaryOp::Xor, &Value::Bool(true)).unwrap(),
2611 Value::Bool(false)
2612 );
2613 }
2614}