1use std::collections::HashSet;
7
8use serde_json::{Map, Number, Value};
9
10use crate::ast::Ast;
11use crate::functions::{Function, custom_error, number_value};
12use crate::interpreter::{SearchResult, interpret};
13use crate::registry::register_if_enabled;
14use crate::value_ext::ValueExt;
15use crate::{Context, Runtime, arg, defn, get_expref_id};
16
17fn get_expref_ast<'a>(value: &Value, ctx: &'a Context<'_>) -> Option<&'a Ast> {
19 get_expref_id(value).and_then(|id| ctx.get_expref(id))
20}
21
22fn value_to_string(value: &Value) -> String {
24 match value {
25 Value::String(s) => s.clone(),
26 Value::Number(n) => n.to_string(),
27 Value::Bool(b) => b.to_string(),
28 Value::Null => "null".to_string(),
29 _ => serde_json::to_string(value).unwrap_or_default(),
30 }
31}
32
33fn compare_values(a: &Value, b: &Value) -> std::cmp::Ordering {
35 use std::cmp::Ordering;
36 match (a, b) {
37 (Value::Number(an), Value::Number(bn)) => {
38 let a_f = an.as_f64().unwrap_or(0.0);
39 let b_f = bn.as_f64().unwrap_or(0.0);
40 a_f.partial_cmp(&b_f).unwrap_or(Ordering::Equal)
41 }
42 (Value::String(a_s), Value::String(b_s)) => a_s.cmp(b_s),
43 (Value::Null, Value::Null) => Ordering::Equal,
44 (Value::Null, _) => Ordering::Less,
45 (_, Value::Null) => Ordering::Greater,
46 _ => Ordering::Equal,
47 }
48}
49
50pub fn register_filtered(runtime: &mut Runtime, enabled: &HashSet<&str>) {
52 register_if_enabled(runtime, "map_expr", enabled, Box::new(MapExprFn::new()));
53 register_if_enabled(
54 runtime,
55 "filter_expr",
56 enabled,
57 Box::new(FilterExprFn::new()),
58 );
59 register_if_enabled(runtime, "any_expr", enabled, Box::new(AnyExprFn::new()));
60 register_if_enabled(runtime, "all_expr", enabled, Box::new(AllExprFn::new()));
61 register_if_enabled(runtime, "find_expr", enabled, Box::new(FindExprFn::new()));
62 register_if_enabled(
63 runtime,
64 "find_index_expr",
65 enabled,
66 Box::new(FindIndexExprFn::new()),
67 );
68 register_if_enabled(runtime, "count_expr", enabled, Box::new(CountExprFn::new()));
69 register_if_enabled(
70 runtime,
71 "sort_by_expr",
72 enabled,
73 Box::new(SortByExprFn::new()),
74 );
75 register_if_enabled(
76 runtime,
77 "group_by_expr",
78 enabled,
79 Box::new(GroupByExprFn::new()),
80 );
81 register_if_enabled(
82 runtime,
83 "partition_expr",
84 enabled,
85 Box::new(PartitionExprFn::new()),
86 );
87 register_if_enabled(
88 runtime,
89 "min_by_expr",
90 enabled,
91 Box::new(MinByExprFn::new()),
92 );
93 register_if_enabled(
94 runtime,
95 "max_by_expr",
96 enabled,
97 Box::new(MaxByExprFn::new()),
98 );
99 register_if_enabled(
100 runtime,
101 "unique_by_expr",
102 enabled,
103 Box::new(UniqueByExprFn::new()),
104 );
105 register_if_enabled(
106 runtime,
107 "flat_map_expr",
108 enabled,
109 Box::new(FlatMapExprFn::new()),
110 );
111
112 register_if_enabled(runtime, "mapcat", enabled, Box::new(FlatMapExprFn::new()));
114
115 register_if_enabled(runtime, "some", enabled, Box::new(AnyExprFn::new()));
117 register_if_enabled(runtime, "every", enabled, Box::new(AllExprFn::new()));
118 register_if_enabled(runtime, "reject", enabled, Box::new(RejectFn::new()));
119 register_if_enabled(runtime, "map_keys", enabled, Box::new(MapKeysFn::new()));
120 register_if_enabled(runtime, "map_values", enabled, Box::new(MapValuesFn::new()));
121 register_if_enabled(runtime, "order_by", enabled, Box::new(OrderByFn::new()));
122 register_if_enabled(
123 runtime,
124 "reduce_expr",
125 enabled,
126 Box::new(ReduceExprFn::new()),
127 );
128 register_if_enabled(runtime, "scan_expr", enabled, Box::new(ScanExprFn::new()));
129 register_if_enabled(runtime, "fold", enabled, Box::new(ReduceExprFn::new()));
131 register_if_enabled(runtime, "reductions", enabled, Box::new(ScanExprFn::new()));
133 register_if_enabled(runtime, "none", enabled, Box::new(NoneFn::new()));
135 register_if_enabled(runtime, "count_by", enabled, Box::new(CountByFn::new()));
136
137 register_if_enabled(runtime, "partial", enabled, Box::new(PartialFn::new()));
139 register_if_enabled(runtime, "apply", enabled, Box::new(ApplyFn::new()));
140
141 register_if_enabled(runtime, "take_while", enabled, Box::new(TakeWhileFn::new()));
143 register_if_enabled(runtime, "drop_while", enabled, Box::new(DropWhileFn::new()));
144 register_if_enabled(runtime, "zip_with", enabled, Box::new(ZipWithFn::new()));
145
146 register_if_enabled(runtime, "walk", enabled, Box::new(WalkFn::new()));
148
149 register_if_enabled(runtime, "recurse", enabled, Box::new(RecurseFn::new()));
151 register_if_enabled(
152 runtime,
153 "recurse_with",
154 enabled,
155 Box::new(RecurseWithFn::new()),
156 );
157
158 register_if_enabled(runtime, "while_expr", enabled, Box::new(WhileExprFn::new()));
160 register_if_enabled(runtime, "until_expr", enabled, Box::new(UntilExprFn::new()));
161
162 register_if_enabled(runtime, "rank", enabled, Box::new(RankFn::new()));
164 register_if_enabled(runtime, "dense_rank", enabled, Box::new(DenseRankFn::new()));
165
166 register_if_enabled(runtime, "pivot", enabled, Box::new(PivotFn::new()));
168 register_if_enabled(runtime, "unpivot", enabled, Box::new(UnpivotFn::new()));
169}
170
171defn!(MapExprFn, vec![arg!(expref), arg!(array)], None);
176
177impl Function for MapExprFn {
178 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
179 self.signature.validate(args, ctx)?;
180
181 let ast = get_expref_ast(&args[0], ctx)
182 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
183 .clone();
184 let arr = args[1].as_array().unwrap();
185
186 let mut results = Vec::with_capacity(arr.len());
187 for item in arr {
188 results.push(interpret(item, &ast, ctx)?);
189 }
190
191 Ok(Value::Array(results))
192 }
193}
194
195defn!(FilterExprFn, vec![arg!(expref), arg!(array)], None);
200
201impl Function for FilterExprFn {
202 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
203 self.signature.validate(args, ctx)?;
204
205 let ast = get_expref_ast(&args[0], ctx)
206 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
207 .clone();
208 let arr = args[1].as_array().unwrap();
209
210 let mut results = Vec::new();
211 for item in arr {
212 let result = interpret(item, &ast, ctx)?;
213 if result.is_truthy() {
214 results.push(item.clone());
215 }
216 }
217
218 Ok(Value::Array(results))
219 }
220}
221
222defn!(AnyExprFn, vec![arg!(expref), arg!(array)], None);
227
228impl Function for AnyExprFn {
229 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
230 self.signature.validate(args, ctx)?;
231
232 let ast = get_expref_ast(&args[0], ctx)
233 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
234 .clone();
235 let arr = args[1].as_array().unwrap();
236
237 for item in arr {
238 let result = interpret(item, &ast, ctx)?;
239 if result.is_truthy() {
240 return Ok(Value::Bool(true));
241 }
242 }
243
244 Ok(Value::Bool(false))
245 }
246}
247
248defn!(NoneFn, vec![arg!(expref), arg!(array)], None);
253
254impl Function for NoneFn {
255 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
256 self.signature.validate(args, ctx)?;
257
258 let ast = get_expref_ast(&args[0], ctx)
259 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
260 .clone();
261 let arr = args[1].as_array().unwrap();
262
263 if arr.is_empty() {
265 return Ok(Value::Bool(true));
266 }
267
268 for item in arr {
269 let result = interpret(item, &ast, ctx)?;
270 if result.is_truthy() {
271 return Ok(Value::Bool(false));
272 }
273 }
274
275 Ok(Value::Bool(true))
276 }
277}
278
279defn!(AllExprFn, vec![arg!(expref), arg!(array)], None);
284
285impl Function for AllExprFn {
286 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
287 self.signature.validate(args, ctx)?;
288
289 let ast = get_expref_ast(&args[0], ctx)
290 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
291 .clone();
292 let arr = args[1].as_array().unwrap();
293
294 if arr.is_empty() {
296 return Ok(Value::Bool(true));
297 }
298
299 for item in arr {
300 let result = interpret(item, &ast, ctx)?;
301 if !result.is_truthy() {
302 return Ok(Value::Bool(false));
303 }
304 }
305
306 Ok(Value::Bool(true))
307 }
308}
309
310defn!(FindExprFn, vec![arg!(expref), arg!(array)], None);
315
316impl Function for FindExprFn {
317 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
318 self.signature.validate(args, ctx)?;
319
320 let ast = get_expref_ast(&args[0], ctx)
321 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
322 .clone();
323 let arr = args[1].as_array().unwrap();
324
325 for item in arr {
326 let result = interpret(item, &ast, ctx)?;
327 if result.is_truthy() {
328 return Ok(item.clone());
329 }
330 }
331
332 Ok(Value::Null)
333 }
334}
335
336defn!(FindIndexExprFn, vec![arg!(expref), arg!(array)], None);
341
342impl Function for FindIndexExprFn {
343 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
344 self.signature.validate(args, ctx)?;
345
346 let ast = get_expref_ast(&args[0], ctx)
347 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
348 .clone();
349 let arr = args[1].as_array().unwrap();
350
351 for (i, item) in arr.iter().enumerate() {
352 let result = interpret(item, &ast, ctx)?;
353 if result.is_truthy() {
354 return Ok(number_value(i as f64));
355 }
356 }
357
358 Ok(number_value(-1.0))
359 }
360}
361
362defn!(CountExprFn, vec![arg!(expref), arg!(array)], None);
367
368impl Function for CountExprFn {
369 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
370 self.signature.validate(args, ctx)?;
371
372 let ast = get_expref_ast(&args[0], ctx)
373 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
374 .clone();
375 let arr = args[1].as_array().unwrap();
376
377 let mut count = 0i64;
378 for item in arr {
379 let result = interpret(item, &ast, ctx)?;
380 if result.is_truthy() {
381 count += 1;
382 }
383 }
384
385 Ok(Value::Number(Number::from(count)))
386 }
387}
388
389defn!(SortByExprFn, vec![arg!(expref), arg!(array)], None);
394
395impl Function for SortByExprFn {
396 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
397 self.signature.validate(args, ctx)?;
398
399 let ast = get_expref_ast(&args[0], ctx)
400 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
401 .clone();
402 let arr = args[1].as_array().unwrap();
403
404 if arr.is_empty() {
405 return Ok(Value::Array(vec![]));
406 }
407
408 let mut keyed: Vec<(Value, Value)> = Vec::with_capacity(arr.len());
410 for item in arr {
411 let key = interpret(item, &ast, ctx)?;
412 keyed.push((item.clone(), key));
413 }
414
415 keyed.sort_by(|a, b| compare_values(&a.1, &b.1));
417
418 let results: Vec<Value> = keyed.into_iter().map(|(item, _)| item).collect();
419 Ok(Value::Array(results))
420 }
421}
422
423defn!(GroupByExprFn, vec![arg!(expref), arg!(array)], None);
428
429impl Function for GroupByExprFn {
430 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
431 self.signature.validate(args, ctx)?;
432
433 let ast = get_expref_ast(&args[0], ctx)
434 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
435 .clone();
436 let arr = args[1].as_array().unwrap();
437
438 let mut group_keys: Vec<String> = Vec::new();
440 let mut group_map: std::collections::HashMap<String, Vec<Value>> =
441 std::collections::HashMap::new();
442
443 for item in arr {
444 let key_val = interpret(item, &ast, ctx)?;
445 let key = value_to_string(&key_val);
446 if !group_map.contains_key(&key) {
447 group_keys.push(key.clone());
448 }
449 group_map.entry(key).or_default().push(item.clone());
450 }
451
452 let mut result = Map::new();
453 for key in group_keys {
454 if let Some(items) = group_map.remove(&key) {
455 result.insert(key, Value::Array(items));
456 }
457 }
458
459 Ok(Value::Object(result))
460 }
461}
462
463defn!(CountByFn, vec![arg!(expref), arg!(array)], None);
468
469impl Function for CountByFn {
470 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
471 self.signature.validate(args, ctx)?;
472
473 let ast = get_expref_ast(&args[0], ctx)
474 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
475 .clone();
476 let arr = args[1].as_array().unwrap();
477
478 let mut count_keys: Vec<String> = Vec::new();
479 let mut counts: std::collections::HashMap<String, i64> = std::collections::HashMap::new();
480
481 for item in arr {
482 let key_val = interpret(item, &ast, ctx)?;
483 let key = value_to_string(&key_val);
484 if !counts.contains_key(&key) {
485 count_keys.push(key.clone());
486 }
487 *counts.entry(key).or_insert(0) += 1;
488 }
489
490 let mut result = Map::new();
491 for key in count_keys {
492 if let Some(&count) = counts.get(&key) {
493 result.insert(key, Value::Number(Number::from(count)));
494 }
495 }
496
497 Ok(Value::Object(result))
498 }
499}
500
501defn!(PartitionExprFn, vec![arg!(expref), arg!(array)], None);
506
507impl Function for PartitionExprFn {
508 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
509 self.signature.validate(args, ctx)?;
510
511 let ast = get_expref_ast(&args[0], ctx)
512 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
513 .clone();
514 let arr = args[1].as_array().unwrap();
515
516 let mut matches = Vec::new();
517 let mut non_matches = Vec::new();
518
519 for item in arr {
520 let result = interpret(item, &ast, ctx)?;
521 if result.is_truthy() {
522 matches.push(item.clone());
523 } else {
524 non_matches.push(item.clone());
525 }
526 }
527
528 Ok(Value::Array(vec![
529 Value::Array(matches),
530 Value::Array(non_matches),
531 ]))
532 }
533}
534
535defn!(MinByExprFn, vec![arg!(expref), arg!(array)], None);
540
541impl Function for MinByExprFn {
542 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
543 self.signature.validate(args, ctx)?;
544
545 let ast = get_expref_ast(&args[0], ctx)
546 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
547 .clone();
548 let arr = args[1].as_array().unwrap();
549
550 if arr.is_empty() {
551 return Ok(Value::Null);
552 }
553
554 let mut min_item = arr[0].clone();
555 let mut min_key = interpret(&arr[0], &ast, ctx)?;
556
557 for item in arr.iter().skip(1) {
558 let key = interpret(item, &ast, ctx)?;
559 if compare_values(&key, &min_key) == std::cmp::Ordering::Less {
560 min_item = item.clone();
561 min_key = key;
562 }
563 }
564
565 Ok(min_item)
566 }
567}
568
569defn!(MaxByExprFn, vec![arg!(expref), arg!(array)], None);
574
575impl Function for MaxByExprFn {
576 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
577 self.signature.validate(args, ctx)?;
578
579 let ast = get_expref_ast(&args[0], ctx)
580 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
581 .clone();
582 let arr = args[1].as_array().unwrap();
583
584 if arr.is_empty() {
585 return Ok(Value::Null);
586 }
587
588 let mut max_item = arr[0].clone();
589 let mut max_key = interpret(&arr[0], &ast, ctx)?;
590
591 for item in arr.iter().skip(1) {
592 let key = interpret(item, &ast, ctx)?;
593 if compare_values(&key, &max_key) == std::cmp::Ordering::Greater {
594 max_item = item.clone();
595 max_key = key;
596 }
597 }
598
599 Ok(max_item)
600 }
601}
602
603defn!(UniqueByExprFn, vec![arg!(expref), arg!(array)], None);
608
609impl Function for UniqueByExprFn {
610 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
611 self.signature.validate(args, ctx)?;
612
613 let ast = get_expref_ast(&args[0], ctx)
614 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
615 .clone();
616 let arr = args[1].as_array().unwrap();
617
618 let mut seen: HashSet<String> = HashSet::new();
619 let mut results = Vec::new();
620
621 for item in arr {
622 let key_val = interpret(item, &ast, ctx)?;
623 let key = value_to_string(&key_val);
624 if seen.insert(key) {
625 results.push(item.clone());
626 }
627 }
628
629 Ok(Value::Array(results))
630 }
631}
632
633defn!(FlatMapExprFn, vec![arg!(expref), arg!(array)], None);
638
639impl Function for FlatMapExprFn {
640 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
641 self.signature.validate(args, ctx)?;
642
643 let ast = get_expref_ast(&args[0], ctx)
644 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
645 .clone();
646 let arr = args[1].as_array().unwrap();
647
648 let mut results = Vec::new();
649 for item in arr {
650 let result = interpret(item, &ast, ctx)?;
651 match result {
652 Value::Array(inner) => {
653 results.extend(inner);
654 }
655 Value::Null => {
656 }
658 _ => {
659 results.push(result);
660 }
661 }
662 }
663
664 Ok(Value::Array(results))
665 }
666}
667
668defn!(RejectFn, vec![arg!(expref), arg!(array)], None);
673
674impl Function for RejectFn {
675 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
676 self.signature.validate(args, ctx)?;
677
678 let ast = get_expref_ast(&args[0], ctx)
679 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
680 .clone();
681 let arr = args[1].as_array().unwrap();
682
683 let mut results = Vec::new();
684 for item in arr {
685 let result = interpret(item, &ast, ctx)?;
686 if !result.is_truthy() {
688 results.push(item.clone());
689 }
690 }
691
692 Ok(Value::Array(results))
693 }
694}
695
696defn!(MapKeysFn, vec![arg!(expref), arg!(object)], None);
701
702impl Function for MapKeysFn {
703 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
704 self.signature.validate(args, ctx)?;
705
706 let ast = get_expref_ast(&args[0], ctx)
707 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
708 .clone();
709 let obj = args[1].as_object().unwrap();
710
711 let mut result = Map::new();
712 for (key, value) in obj.iter() {
713 let key_val = Value::String(key.clone());
715 let new_key_val = interpret(&key_val, &ast, ctx)?;
716
717 let new_key_str = match &new_key_val {
718 Value::String(s) => s.clone(),
719 Value::Number(n) => n.to_string(),
720 _ => key.clone(), };
722
723 result.insert(new_key_str, value.clone());
724 }
725
726 Ok(Value::Object(result))
727 }
728}
729
730defn!(MapValuesFn, vec![arg!(expref), arg!(object)], None);
735
736impl Function for MapValuesFn {
737 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
738 self.signature.validate(args, ctx)?;
739
740 let ast = get_expref_ast(&args[0], ctx)
741 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
742 .clone();
743 let obj = args[1].as_object().unwrap();
744
745 let mut result = Map::new();
746 for (key, value) in obj.iter() {
747 let new_value = interpret(value, &ast, ctx)?;
748 result.insert(key.clone(), new_value);
749 }
750
751 Ok(Value::Object(result))
752 }
753}
754
755defn!(OrderByFn, vec![arg!(array), arg!(array)], None);
760
761impl Function for OrderByFn {
762 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
763 self.signature.validate(args, ctx)?;
764
765 let arr = args[0].as_array().unwrap();
766 let criteria = args[1].as_array().unwrap();
767
768 if arr.is_empty() {
769 return Ok(Value::Array(vec![]));
770 }
771
772 let mut sort_specs: Vec<(String, bool)> = Vec::new(); for criterion in criteria {
775 let crit_arr = criterion.as_array().ok_or_else(|| {
776 custom_error(ctx, "Each criterion must be an array [field, direction]")
777 })?;
778
779 if crit_arr.len() < 2 {
780 return Err(custom_error(
781 ctx,
782 "Each criterion must have [field, direction]",
783 ));
784 }
785
786 let field = crit_arr[0]
787 .as_str()
788 .ok_or_else(|| custom_error(ctx, "Field name must be a string"))?;
789
790 let direction = crit_arr[1]
791 .as_str()
792 .ok_or_else(|| custom_error(ctx, "Direction must be 'asc' or 'desc'"))?;
793
794 let ascending = match direction.to_lowercase().as_str() {
795 "asc" | "ascending" => true,
796 "desc" | "descending" => false,
797 _ => {
798 return Err(custom_error(ctx, "Direction must be 'asc' or 'desc'"));
799 }
800 };
801
802 sort_specs.push((field.to_string(), ascending));
803 }
804
805 let mut result: Vec<Value> = arr.clone();
807 result.sort_by(|a, b| {
808 for (field, ascending) in &sort_specs {
809 let a_val = a
810 .as_object()
811 .and_then(|o| o.get(field.as_str()))
812 .unwrap_or(&Value::Null);
813 let b_val = b
814 .as_object()
815 .and_then(|o| o.get(field.as_str()))
816 .unwrap_or(&Value::Null);
817
818 let cmp = compare_values(a_val, b_val);
819 if cmp != std::cmp::Ordering::Equal {
820 return if *ascending { cmp } else { cmp.reverse() };
821 }
822 }
823 std::cmp::Ordering::Equal
824 });
825
826 Ok(Value::Array(result))
827 }
828}
829
830defn!(
835 ReduceExprFn,
836 vec![arg!(expref), arg!(array), arg!(any)],
837 None
838);
839
840impl Function for ReduceExprFn {
841 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
842 self.signature.validate(args, ctx)?;
843
844 let ast = get_expref_ast(&args[0], ctx)
845 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
846 .clone();
847 let arr = args[1].as_array().unwrap();
848 let initial = args[2].clone();
849
850 if arr.is_empty() {
851 return Ok(initial);
852 }
853
854 let mut accumulator = initial;
855
856 for (idx, item) in arr.iter().enumerate() {
857 let mut context_map = Map::new();
859 context_map.insert("accumulator".to_string(), accumulator.clone());
860 context_map.insert("current".to_string(), item.clone());
861 context_map.insert("index".to_string(), Value::Number(Number::from(idx as i64)));
862 let context_val = Value::Object(context_map);
863
864 accumulator = interpret(&context_val, &ast, ctx)?;
865 }
866
867 Ok(accumulator)
868 }
869}
870
871defn!(ScanExprFn, vec![arg!(expref), arg!(array), arg!(any)], None);
876
877impl Function for ScanExprFn {
878 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
879 self.signature.validate(args, ctx)?;
880
881 let ast = get_expref_ast(&args[0], ctx)
882 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
883 .clone();
884 let arr = args[1].as_array().unwrap();
885 let initial = args[2].clone();
886
887 if arr.is_empty() {
888 return Ok(Value::Array(vec![]));
889 }
890
891 let mut accumulator = initial;
892 let mut results: Vec<Value> = Vec::with_capacity(arr.len());
893
894 for (idx, item) in arr.iter().enumerate() {
895 let mut context_map = Map::new();
897 context_map.insert("accumulator".to_string(), accumulator.clone());
898 context_map.insert("current".to_string(), item.clone());
899 context_map.insert("index".to_string(), Value::Number(Number::from(idx as i64)));
900 let context_val = Value::Object(context_map);
901
902 accumulator = interpret(&context_val, &ast, ctx)?;
903 results.push(accumulator.clone());
904 }
905
906 Ok(Value::Array(results))
907 }
908}
909
910defn!(PartialFn, vec![arg!(string)], Some(arg!(any)));
915
916impl Function for PartialFn {
917 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
918 self.signature.validate(args, ctx)?;
919
920 let fn_name = args[0].as_str().ok_or_else(|| {
921 custom_error(
922 ctx,
923 "partial() first argument must be a function name string",
924 )
925 })?;
926
927 let prefilled_args: Vec<Value> = args[1..].to_vec();
929
930 let mut partial_obj = Map::new();
932 partial_obj.insert("__partial__".to_string(), Value::Bool(true));
933 partial_obj.insert("fn".to_string(), Value::String(fn_name.to_string()));
934 partial_obj.insert("args".to_string(), Value::Array(prefilled_args));
935
936 Ok(Value::Object(partial_obj))
937 }
938}
939
940defn!(ApplyFn, vec![arg!(any)], Some(arg!(any)));
945
946impl Function for ApplyFn {
947 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
948 self.signature.validate(args, ctx)?;
949
950 let first_arg = &args[0];
951 let additional_args = &args[1..];
952
953 if let Some(obj) = first_arg.as_object()
955 && obj.get("__partial__").and_then(|v| v.as_bool()) == Some(true)
956 {
957 let fn_name = obj
959 .get("fn")
960 .and_then(|v| v.as_str())
961 .ok_or_else(|| custom_error(ctx, "Invalid partial object: missing 'fn' field"))?;
962
963 let prefilled = obj
964 .get("args")
965 .and_then(|v| v.as_array())
966 .ok_or_else(|| custom_error(ctx, "Invalid partial object: missing 'args' field"))?;
967
968 return invoke_function(fn_name, prefilled, additional_args, ctx);
969 }
970
971 if let Some(fn_name) = first_arg.as_str() {
973 return invoke_function(fn_name, &[], additional_args, ctx);
974 }
975
976 Err(custom_error(
977 ctx,
978 "apply() first argument must be a partial object or function name string",
979 ))
980 }
981}
982
983fn invoke_function(
985 fn_name: &str,
986 prefilled: &[Value],
987 additional: &[Value],
988 ctx: &mut Context<'_>,
989) -> SearchResult {
990 let mut all_args_json: Vec<String> = Vec::new();
992
993 for a in prefilled {
995 all_args_json.push(format!("`{}`", serde_json::to_string(a).unwrap()));
996 }
997
998 for a in additional {
1000 all_args_json.push(format!("`{}`", serde_json::to_string(a).unwrap()));
1001 }
1002
1003 let expr_str = format!("{}({})", fn_name, all_args_json.join(", "));
1005
1006 let compiled = ctx.runtime.compile(&expr_str).map_err(|_| {
1007 custom_error(
1008 ctx,
1009 &format!("Failed to compile function call '{}'", expr_str),
1010 )
1011 })?;
1012
1013 compiled
1014 .search(&Value::Null)
1015 .map_err(|_| custom_error(ctx, &format!("Failed to execute '{}'", fn_name)))
1016}
1017
1018defn!(TakeWhileFn, vec![arg!(expref), arg!(array)], None);
1023
1024impl Function for TakeWhileFn {
1025 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1026 self.signature.validate(args, ctx)?;
1027
1028 let ast = get_expref_ast(&args[0], ctx)
1029 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
1030 .clone();
1031 let arr = args[1].as_array().unwrap();
1032
1033 let mut results = Vec::new();
1034 for item in arr {
1035 let result = interpret(item, &ast, ctx)?;
1036 if result.is_truthy() {
1037 results.push(item.clone());
1038 } else {
1039 break;
1040 }
1041 }
1042
1043 Ok(Value::Array(results))
1044 }
1045}
1046
1047defn!(DropWhileFn, vec![arg!(expref), arg!(array)], None);
1052
1053impl Function for DropWhileFn {
1054 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1055 self.signature.validate(args, ctx)?;
1056
1057 let ast = get_expref_ast(&args[0], ctx)
1058 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
1059 .clone();
1060 let arr = args[1].as_array().unwrap();
1061
1062 let mut dropping = true;
1063 let mut results = Vec::new();
1064 for item in arr {
1065 if dropping {
1066 let result = interpret(item, &ast, ctx)?;
1067 if !result.is_truthy() {
1068 dropping = false;
1069 results.push(item.clone());
1070 }
1071 } else {
1072 results.push(item.clone());
1073 }
1074 }
1075
1076 Ok(Value::Array(results))
1077 }
1078}
1079
1080defn!(
1085 ZipWithFn,
1086 vec![arg!(expref), arg!(array), arg!(array)],
1087 None
1088);
1089
1090impl Function for ZipWithFn {
1091 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1092 self.signature.validate(args, ctx)?;
1093
1094 let ast = get_expref_ast(&args[0], ctx)
1095 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
1096 .clone();
1097 let arr1 = args[1].as_array().unwrap();
1098 let arr2 = args[2].as_array().unwrap();
1099
1100 let min_len = arr1.len().min(arr2.len());
1101 let mut results = Vec::with_capacity(min_len);
1102
1103 for i in 0..min_len {
1104 let pair = Value::Array(vec![arr1[i].clone(), arr2[i].clone()]);
1106 let result = interpret(&pair, &ast, ctx)?;
1107 results.push(result);
1108 }
1109
1110 Ok(Value::Array(results))
1111 }
1112}
1113
1114defn!(WalkFn, vec![arg!(expref), arg!(any)], None);
1119
1120fn walk_value(value: &Value, ast: &Ast, ctx: &mut Context<'_>) -> SearchResult {
1122 match value {
1123 Value::Array(arr) => {
1124 let walked_elements: Result<Vec<Value>, _> =
1126 arr.iter().map(|elem| walk_value(elem, ast, ctx)).collect();
1127 let new_array = Value::Array(walked_elements?);
1128 interpret(&new_array, ast, ctx)
1130 }
1131 Value::Object(obj) => {
1132 let mut walked_obj = Map::new();
1134 for (k, v) in obj.iter() {
1135 walked_obj.insert(k.clone(), walk_value(v, ast, ctx)?);
1136 }
1137 let new_object = Value::Object(walked_obj);
1138 interpret(&new_object, ast, ctx)
1140 }
1141 _ => interpret(value, ast, ctx),
1143 }
1144}
1145
1146impl Function for WalkFn {
1147 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1148 self.signature.validate(args, ctx)?;
1149
1150 let ast = get_expref_ast(&args[0], ctx)
1151 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
1152 .clone();
1153
1154 walk_value(&args[1], &ast, ctx)
1155 }
1156}
1157
1158defn!(RecurseFn, vec![arg!(any)], None);
1163
1164fn collect_recursive(value: &Value, results: &mut Vec<Value>) {
1166 results.push(value.clone());
1167 match value {
1168 Value::Array(arr) => {
1169 for elem in arr {
1170 collect_recursive(elem, results);
1171 }
1172 }
1173 Value::Object(obj) => {
1174 for (_, v) in obj.iter() {
1175 collect_recursive(v, results);
1176 }
1177 }
1178 _ => {}
1179 }
1180}
1181
1182impl Function for RecurseFn {
1183 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1184 self.signature.validate(args, ctx)?;
1185
1186 let mut results = Vec::new();
1187 collect_recursive(&args[0], &mut results);
1188
1189 Ok(Value::Array(results))
1190 }
1191}
1192
1193defn!(RecurseWithFn, vec![arg!(any), arg!(expref)], None);
1198
1199impl Function for RecurseWithFn {
1200 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1201 self.signature.validate(args, ctx)?;
1202
1203 let ast = get_expref_ast(&args[1], ctx)
1204 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
1205 .clone();
1206
1207 let mut results = Vec::new();
1208 let mut queue = vec![args[0].clone()];
1209 let max_iterations = 10000; let mut iterations = 0;
1211
1212 while let Some(current) = queue.pop() {
1213 if iterations >= max_iterations {
1214 return Err(custom_error(
1215 ctx,
1216 "recurse_with exceeded maximum iterations",
1217 ));
1218 }
1219 iterations += 1;
1220
1221 if current.is_null() {
1223 continue;
1224 }
1225
1226 results.push(current.clone());
1227
1228 let next = interpret(¤t, &ast, ctx)?;
1230
1231 match next {
1232 Value::Null => {}
1233 Value::Array(arr) => {
1234 for elem in arr.into_iter().rev() {
1236 if !elem.is_null() {
1237 queue.push(elem);
1238 }
1239 }
1240 }
1241 _ => {
1242 queue.push(next);
1243 }
1244 }
1245 }
1246
1247 Ok(Value::Array(results))
1248 }
1249}
1250
1251defn!(
1256 WhileExprFn,
1257 vec![arg!(any), arg!(expref), arg!(expref)],
1258 None
1259);
1260
1261impl Function for WhileExprFn {
1262 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1263 self.signature.validate(args, ctx)?;
1264
1265 let cond_ast = get_expref_ast(&args[1], ctx)
1266 .ok_or_else(|| custom_error(ctx, "Expected expref for condition"))?
1267 .clone();
1268 let update_ast = get_expref_ast(&args[2], ctx)
1269 .ok_or_else(|| custom_error(ctx, "Expected expref for update"))?
1270 .clone();
1271
1272 let mut current = args[0].clone();
1273 let max_iterations = 100000; let mut iterations = 0;
1275
1276 loop {
1277 if iterations >= max_iterations {
1278 return Err(custom_error(ctx, "while_expr exceeded maximum iterations"));
1279 }
1280 iterations += 1;
1281
1282 let cond_result = interpret(¤t, &cond_ast, ctx)?;
1284 if !cond_result.is_truthy() {
1285 break;
1286 }
1287
1288 current = interpret(¤t, &update_ast, ctx)?;
1290 }
1291
1292 Ok(current)
1293 }
1294}
1295
1296defn!(
1301 UntilExprFn,
1302 vec![arg!(any), arg!(expref), arg!(expref)],
1303 None
1304);
1305
1306impl Function for UntilExprFn {
1307 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1308 self.signature.validate(args, ctx)?;
1309
1310 let cond_ast = get_expref_ast(&args[1], ctx)
1311 .ok_or_else(|| custom_error(ctx, "Expected expref for condition"))?
1312 .clone();
1313 let update_ast = get_expref_ast(&args[2], ctx)
1314 .ok_or_else(|| custom_error(ctx, "Expected expref for update"))?
1315 .clone();
1316
1317 let mut current = args[0].clone();
1318 let max_iterations = 100000; let mut iterations = 0;
1320
1321 loop {
1322 if iterations >= max_iterations {
1323 return Err(custom_error(ctx, "until_expr exceeded maximum iterations"));
1324 }
1325 iterations += 1;
1326
1327 let cond_result = interpret(¤t, &cond_ast, ctx)?;
1329 if cond_result.is_truthy() {
1330 break;
1331 }
1332
1333 current = interpret(¤t, &update_ast, ctx)?;
1335 }
1336
1337 Ok(current)
1338 }
1339}
1340
1341defn!(RankFn, vec![arg!(expref), arg!(array)], None);
1346
1347impl Function for RankFn {
1348 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1349 self.signature.validate(args, ctx)?;
1350 let ast = get_expref_ast(&args[0], ctx)
1351 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
1352 .clone();
1353 let arr = args[1].as_array().unwrap();
1354
1355 let mut keyed: Vec<(usize, Value)> = Vec::with_capacity(arr.len());
1357 for (i, item) in arr.iter().enumerate() {
1358 let key = interpret(item, &ast, ctx)?;
1359 keyed.push((i, key));
1360 }
1361
1362 keyed.sort_by(|a, b| compare_values(&b.1, &a.1));
1364
1365 let mut ranks = vec![0usize; arr.len()];
1366 let mut current_rank = 1;
1367 for i in 0..keyed.len() {
1368 if i > 0 && compare_values(&keyed[i].1, &keyed[i - 1].1) != std::cmp::Ordering::Equal {
1369 current_rank = i + 1;
1370 }
1371 ranks[keyed[i].0] = current_rank;
1372 }
1373
1374 Ok(Value::Array(
1375 ranks
1376 .into_iter()
1377 .map(|r| Value::Number(Number::from(r)))
1378 .collect(),
1379 ))
1380 }
1381}
1382
1383defn!(PivotFn, vec![arg!(array), arg!(expref), arg!(expref)], None);
1388
1389impl Function for PivotFn {
1390 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1391 self.signature.validate(args, ctx)?;
1392
1393 let arr = args[0].as_array().unwrap();
1394 let key_ast = get_expref_ast(&args[1], ctx)
1395 .ok_or_else(|| custom_error(ctx, "Expected expref for key"))?
1396 .clone();
1397 let val_ast = get_expref_ast(&args[2], ctx)
1398 .ok_or_else(|| custom_error(ctx, "Expected expref for value"))?
1399 .clone();
1400
1401 let mut result = Map::new();
1402 for item in arr {
1403 let key = interpret(item, &key_ast, ctx)?;
1404 let key_str = match &key {
1405 Value::String(s) => s.clone(),
1406 _ => serde_json::to_string(&key).unwrap_or_default(),
1407 };
1408 let val = interpret(item, &val_ast, ctx)?;
1409 result.insert(key_str, val);
1410 }
1411
1412 Ok(Value::Object(result))
1413 }
1414}
1415
1416defn!(DenseRankFn, vec![arg!(expref), arg!(array)], None);
1421
1422impl Function for DenseRankFn {
1423 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1424 self.signature.validate(args, ctx)?;
1425 let ast = get_expref_ast(&args[0], ctx)
1426 .ok_or_else(|| custom_error(ctx, "Expected expref"))?
1427 .clone();
1428 let arr = args[1].as_array().unwrap();
1429
1430 let mut keyed: Vec<(usize, Value)> = Vec::with_capacity(arr.len());
1431 for (i, item) in arr.iter().enumerate() {
1432 let key = interpret(item, &ast, ctx)?;
1433 keyed.push((i, key));
1434 }
1435
1436 keyed.sort_by(|a, b| compare_values(&b.1, &a.1));
1437
1438 let mut ranks = vec![0usize; arr.len()];
1439 let mut current_rank = 1;
1440 for i in 0..keyed.len() {
1441 if i > 0 && compare_values(&keyed[i].1, &keyed[i - 1].1) != std::cmp::Ordering::Equal {
1442 current_rank += 1;
1443 }
1444 ranks[keyed[i].0] = current_rank;
1445 }
1446
1447 Ok(Value::Array(
1448 ranks
1449 .into_iter()
1450 .map(|r| Value::Number(Number::from(r)))
1451 .collect(),
1452 ))
1453 }
1454}
1455
1456defn!(UnpivotFn, vec![arg!(object)], None);
1461
1462impl Function for UnpivotFn {
1463 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
1464 self.signature.validate(args, ctx)?;
1465
1466 let obj = args[0].as_object().unwrap();
1467 let result: Vec<Value> = obj
1468 .iter()
1469 .map(|(k, v)| {
1470 let mut map = Map::new();
1471 map.insert("key".to_string(), Value::String(k.clone()));
1472 map.insert("value".to_string(), v.clone());
1473 Value::Object(map)
1474 })
1475 .collect();
1476
1477 Ok(Value::Array(result))
1478 }
1479}
1480
1481#[cfg(test)]
1482mod tests {
1483 use crate::Runtime;
1484 use serde_json::json;
1485
1486 fn setup_runtime() -> Runtime {
1487 Runtime::builder()
1488 .with_standard()
1489 .with_all_extensions()
1490 .build()
1491 }
1492
1493 #[test]
1494 fn test_map_expr_field() {
1495 let runtime = setup_runtime();
1496 let data = json!([{"name": "Alice"}, {"name": "Bob"}]);
1497 let expr = runtime.compile("map_expr(&name, @)").unwrap();
1498 let result = expr.search(&data).unwrap();
1499 let arr = result.as_array().unwrap();
1500 assert_eq!(arr.len(), 2);
1501 assert_eq!(arr[0].as_str().unwrap(), "Alice");
1502 assert_eq!(arr[1].as_str().unwrap(), "Bob");
1503 }
1504
1505 #[test]
1506 fn test_map_expr_transform() {
1507 let runtime = setup_runtime();
1508 let data = json!(["hello", "world"]);
1509 let expr = runtime.compile("map_expr(&length(@), @)").unwrap();
1510 let result = expr.search(&data).unwrap();
1511 let arr = result.as_array().unwrap();
1512 assert_eq!(arr[0].as_f64().unwrap(), 5.0);
1513 assert_eq!(arr[1].as_f64().unwrap(), 5.0);
1514 }
1515
1516 #[test]
1517 fn test_filter_expr() {
1518 let runtime = setup_runtime();
1519 let data = json!([{"age": 25}, {"age": 17}, {"age": 30}]);
1520 let expr = runtime.compile("filter_expr(&(age >= `18`), @)").unwrap();
1521 let result = expr.search(&data).unwrap();
1522 let arr = result.as_array().unwrap();
1523 assert_eq!(arr.len(), 2);
1524 }
1525
1526 #[test]
1527 fn test_filter_expr_empty() {
1528 let runtime = setup_runtime();
1529 let data = json!([1, 2, 3]);
1530 let expr = runtime.compile("filter_expr(&(@ > `10`), @)").unwrap();
1531 let result = expr.search(&data).unwrap();
1532 let arr = result.as_array().unwrap();
1533 assert_eq!(arr.len(), 0);
1534 }
1535
1536 #[test]
1537 fn test_any_expr_true() {
1538 let runtime = setup_runtime();
1539 let data = json!([{"active": false}, {"active": true}]);
1540 let expr = runtime.compile("any_expr(&active, @)").unwrap();
1541 let result = expr.search(&data).unwrap();
1542 assert!(result.as_bool().unwrap());
1543 }
1544
1545 #[test]
1546 fn test_any_expr_false() {
1547 let runtime = setup_runtime();
1548 let data = json!([{"active": false}, {"active": false}]);
1549 let expr = runtime.compile("any_expr(&active, @)").unwrap();
1550 let result = expr.search(&data).unwrap();
1551 assert!(!result.as_bool().unwrap());
1552 }
1553
1554 #[test]
1555 fn test_all_expr_true() {
1556 let runtime = setup_runtime();
1557 let data = json!([{"active": true}, {"active": true}]);
1558 let expr = runtime.compile("all_expr(&active, @)").unwrap();
1559 let result = expr.search(&data).unwrap();
1560 assert!(result.as_bool().unwrap());
1561 }
1562
1563 #[test]
1564 fn test_all_expr_false() {
1565 let runtime = setup_runtime();
1566 let data = json!([{"active": true}, {"active": false}]);
1567 let expr = runtime.compile("all_expr(&active, @)").unwrap();
1568 let result = expr.search(&data).unwrap();
1569 assert!(!result.as_bool().unwrap());
1570 }
1571
1572 #[test]
1573 fn test_all_expr_empty() {
1574 let runtime = setup_runtime();
1575 let data = json!([]);
1576 let expr = runtime.compile("all_expr(&active, @)").unwrap();
1577 let result = expr.search(&data).unwrap();
1578 assert!(result.as_bool().unwrap()); }
1580
1581 #[test]
1582 fn test_find_expr_found() {
1583 let runtime = setup_runtime();
1584 let data = json!([{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]);
1585 let expr = runtime.compile("find_expr(&(id == `2`), @)").unwrap();
1586 let result = expr.search(&data).unwrap();
1587 let obj = result.as_object().unwrap();
1588 assert_eq!(obj.get("name").unwrap().as_str().unwrap(), "Bob");
1589 }
1590
1591 #[test]
1592 fn test_find_expr_not_found() {
1593 let runtime = setup_runtime();
1594 let data = json!([{"id": 1}, {"id": 2}]);
1595 let expr = runtime.compile("find_expr(&(id == `99`), @)").unwrap();
1596 let result = expr.search(&data).unwrap();
1597 assert!(result.is_null());
1598 }
1599
1600 #[test]
1601 fn test_sort_by_expr_numbers() {
1602 let runtime = setup_runtime();
1603 let data = json!([{"val": 3}, {"val": 1}, {"val": 2}]);
1604 let expr = runtime.compile("sort_by_expr(&val, @)").unwrap();
1605 let result = expr.search(&data).unwrap();
1606 let arr = result.as_array().unwrap();
1607 assert_eq!(
1608 arr[0]
1609 .as_object()
1610 .unwrap()
1611 .get("val")
1612 .unwrap()
1613 .as_f64()
1614 .unwrap(),
1615 1.0
1616 );
1617 assert_eq!(
1618 arr[1]
1619 .as_object()
1620 .unwrap()
1621 .get("val")
1622 .unwrap()
1623 .as_f64()
1624 .unwrap(),
1625 2.0
1626 );
1627 assert_eq!(
1628 arr[2]
1629 .as_object()
1630 .unwrap()
1631 .get("val")
1632 .unwrap()
1633 .as_f64()
1634 .unwrap(),
1635 3.0
1636 );
1637 }
1638
1639 #[test]
1640 fn test_sort_by_expr_strings() {
1641 let runtime = setup_runtime();
1642 let data = json!([{"name": "Charlie"}, {"name": "Alice"}, {"name": "Bob"}]);
1643 let expr = runtime.compile("sort_by_expr(&name, @)").unwrap();
1644 let result = expr.search(&data).unwrap();
1645 let arr = result.as_array().unwrap();
1646 assert_eq!(
1647 arr[0]
1648 .as_object()
1649 .unwrap()
1650 .get("name")
1651 .unwrap()
1652 .as_str()
1653 .unwrap(),
1654 "Alice"
1655 );
1656 assert_eq!(
1657 arr[1]
1658 .as_object()
1659 .unwrap()
1660 .get("name")
1661 .unwrap()
1662 .as_str()
1663 .unwrap(),
1664 "Bob"
1665 );
1666 assert_eq!(
1667 arr[2]
1668 .as_object()
1669 .unwrap()
1670 .get("name")
1671 .unwrap()
1672 .as_str()
1673 .unwrap(),
1674 "Charlie"
1675 );
1676 }
1677
1678 #[test]
1679 fn test_find_index_expr_found() {
1680 let runtime = setup_runtime();
1681 let data = json!([{"id": 1}, {"id": 2}, {"id": 3}]);
1682 let expr = runtime.compile("find_index_expr(&(id == `2`), @)").unwrap();
1683 let result = expr.search(&data).unwrap();
1684 assert_eq!(result.as_f64().unwrap(), 1.0);
1685 }
1686
1687 #[test]
1688 fn test_find_index_expr_not_found() {
1689 let runtime = setup_runtime();
1690 let data = json!([{"id": 1}, {"id": 2}]);
1691 let expr = runtime
1692 .compile("find_index_expr(&(id == `99`), @)")
1693 .unwrap();
1694 let result = expr.search(&data).unwrap();
1695 assert_eq!(result.as_f64().unwrap(), -1.0);
1696 }
1697
1698 #[test]
1699 fn test_count_expr() {
1700 let runtime = setup_runtime();
1701 let data = json!([{"active": true}, {"active": false}, {"active": true}]);
1702 let expr = runtime.compile("count_expr(&active, @)").unwrap();
1703 let result = expr.search(&data).unwrap();
1704 assert_eq!(result.as_f64().unwrap(), 2.0);
1705 }
1706
1707 #[test]
1708 fn test_count_expr_none() {
1709 let runtime = setup_runtime();
1710 let data = json!([1, 2, 3]);
1711 let expr = runtime.compile("count_expr(&(@ > `10`), @)").unwrap();
1712 let result = expr.search(&data).unwrap();
1713 assert_eq!(result.as_f64().unwrap(), 0.0);
1714 }
1715
1716 #[test]
1717 fn test_group_by_expr() {
1718 let runtime = setup_runtime();
1719 let data = json!([
1720 {"type": "a", "val": 1},
1721 {"type": "b", "val": 2},
1722 {"type": "a", "val": 3}
1723 ]);
1724 let expr = runtime.compile("group_by_expr(&type, @)").unwrap();
1725 let result = expr.search(&data).unwrap();
1726 let obj = result.as_object().unwrap();
1727 assert_eq!(obj.get("a").unwrap().as_array().unwrap().len(), 2);
1728 assert_eq!(obj.get("b").unwrap().as_array().unwrap().len(), 1);
1729 }
1730
1731 #[test]
1732 fn test_partition_expr() {
1733 let runtime = setup_runtime();
1734 let data = json!([1, 2, 3, 4, 5]);
1735 let expr = runtime.compile("partition_expr(&(@ > `3`), @)").unwrap();
1736 let result = expr.search(&data).unwrap();
1737 let arr = result.as_array().unwrap();
1738 let matches = arr[0].as_array().unwrap();
1739 let non_matches = arr[1].as_array().unwrap();
1740 assert_eq!(matches.len(), 2); assert_eq!(non_matches.len(), 3); }
1743
1744 #[test]
1745 fn test_min_by_expr() {
1746 let runtime = setup_runtime();
1747 let data = json!([{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]);
1748 let expr = runtime.compile("min_by_expr(&age, @)").unwrap();
1749 let result = expr.search(&data).unwrap();
1750 let obj = result.as_object().unwrap();
1751 assert_eq!(obj.get("name").unwrap().as_str().unwrap(), "Bob");
1752 }
1753
1754 #[test]
1755 fn test_min_by_expr_empty() {
1756 let runtime = setup_runtime();
1757 let data = json!([]);
1758 let expr = runtime.compile("min_by_expr(&age, @)").unwrap();
1759 let result = expr.search(&data).unwrap();
1760 assert!(result.is_null());
1761 }
1762
1763 #[test]
1764 fn test_max_by_expr() {
1765 let runtime = setup_runtime();
1766 let data = json!([{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]);
1767 let expr = runtime.compile("max_by_expr(&age, @)").unwrap();
1768 let result = expr.search(&data).unwrap();
1769 let obj = result.as_object().unwrap();
1770 assert_eq!(obj.get("name").unwrap().as_str().unwrap(), "Alice");
1771 }
1772
1773 #[test]
1774 fn test_unique_by_expr() {
1775 let runtime = setup_runtime();
1776 let data = json!([
1777 {"type": "a", "val": 1},
1778 {"type": "b", "val": 2},
1779 {"type": "a", "val": 3}
1780 ]);
1781 let expr = runtime.compile("unique_by_expr(&type, @)").unwrap();
1782 let result = expr.search(&data).unwrap();
1783 let arr = result.as_array().unwrap();
1784 assert_eq!(arr.len(), 2); assert_eq!(
1786 arr[0]
1787 .as_object()
1788 .unwrap()
1789 .get("val")
1790 .unwrap()
1791 .as_f64()
1792 .unwrap(),
1793 1.0
1794 );
1795 }
1796
1797 #[test]
1798 fn test_flat_map_expr() {
1799 let runtime = setup_runtime();
1800 let data = json!([
1801 {"tags": ["a", "b"]},
1802 {"tags": ["c"]},
1803 {"tags": ["d", "e"]}
1804 ]);
1805 let expr = runtime.compile("flat_map_expr(&tags, @)").unwrap();
1806 let result = expr.search(&data).unwrap();
1807 let arr = result.as_array().unwrap();
1808 assert_eq!(arr.len(), 5);
1809 assert_eq!(arr[0].as_str().unwrap(), "a");
1810 assert_eq!(arr[4].as_str().unwrap(), "e");
1811 }
1812
1813 #[test]
1814 fn test_flat_map_expr_non_array() {
1815 let runtime = setup_runtime();
1816 let data = json!([{"name": "Alice"}, {"name": "Bob"}]);
1817 let expr = runtime.compile("flat_map_expr(&name, @)").unwrap();
1818 let result = expr.search(&data).unwrap();
1819 let arr = result.as_array().unwrap();
1820 assert_eq!(arr.len(), 2);
1821 assert_eq!(arr[0].as_str().unwrap(), "Alice");
1822 }
1823
1824 #[test]
1825 fn test_some_alias() {
1826 let runtime = setup_runtime();
1827 let data = json!([1, 2, 3, 4, 5]);
1828 let expr = runtime.compile("some(&(@ > `3`), @)").unwrap();
1829 let result = expr.search(&data).unwrap();
1830 assert!(result.as_bool().unwrap());
1831 }
1832
1833 #[test]
1834 fn test_every_alias() {
1835 let runtime = setup_runtime();
1836 let data = json!([2, 4, 6]);
1837 let expr = runtime.compile("every(&(@ > `0`), @)").unwrap();
1838 let result = expr.search(&data).unwrap();
1839 assert!(result.as_bool().unwrap());
1840 }
1841
1842 #[test]
1843 fn test_none_true() {
1844 let runtime = setup_runtime();
1845 let data = json!([1, 2, 3]);
1846 let expr = runtime.compile("none(&(@ > `5`), @)").unwrap();
1847 let result = expr.search(&data).unwrap();
1848 assert!(result.as_bool().unwrap()); }
1850
1851 #[test]
1852 fn test_none_false() {
1853 let runtime = setup_runtime();
1854 let data = json!([1, 2, 10]);
1855 let expr = runtime.compile("none(&(@ > `5`), @)").unwrap();
1856 let result = expr.search(&data).unwrap();
1857 assert!(!result.as_bool().unwrap()); }
1859
1860 #[test]
1861 fn test_none_empty() {
1862 let runtime = setup_runtime();
1863 let data = json!([]);
1864 let expr = runtime.compile("none(&(@ > `0`), @)").unwrap();
1865 let result = expr.search(&data).unwrap();
1866 assert!(result.as_bool().unwrap()); }
1868
1869 #[test]
1870 fn test_none_objects() {
1871 let runtime = setup_runtime();
1872 let data = json!([{"active": false}, {"active": false}]);
1873 let expr = runtime.compile("none(&active, @)").unwrap();
1874 let result = expr.search(&data).unwrap();
1875 assert!(result.as_bool().unwrap()); }
1877
1878 #[test]
1879 fn test_mapcat_alias() {
1880 let runtime = setup_runtime();
1881 let data = json!([
1882 {"tags": ["a", "b"]},
1883 {"tags": ["c"]},
1884 {"tags": ["d", "e"]}
1885 ]);
1886 let expr = runtime.compile("mapcat(&tags, @)").unwrap();
1887 let result = expr.search(&data).unwrap();
1888 let arr = result.as_array().unwrap();
1889 assert_eq!(arr.len(), 5);
1890 assert_eq!(arr[0].as_str().unwrap(), "a");
1891 assert_eq!(arr[4].as_str().unwrap(), "e");
1892 }
1893
1894 #[test]
1895 fn test_reductions_alias() {
1896 let runtime = setup_runtime();
1897 let data = json!([1, 2, 3, 4]);
1898 let expr = runtime
1899 .compile("reductions(&sum([accumulator, current]), @, `0`)")
1900 .unwrap();
1901 let result = expr.search(&data).unwrap();
1902 let arr = result.as_array().unwrap();
1903 assert_eq!(arr.len(), 4);
1905 assert_eq!(arr[0].as_f64().unwrap(), 1.0);
1906 assert_eq!(arr[1].as_f64().unwrap(), 3.0);
1907 assert_eq!(arr[2].as_f64().unwrap(), 6.0);
1908 assert_eq!(arr[3].as_f64().unwrap(), 10.0);
1909 }
1910
1911 #[test]
1912 fn test_reject() {
1913 let runtime = setup_runtime();
1914 let data = json!([1, 2, 3, 4, 5]);
1915 let expr = runtime.compile("reject(&(@ > `2`), @)").unwrap();
1916 let result = expr.search(&data).unwrap();
1917 let arr = result.as_array().unwrap();
1918 assert_eq!(arr.len(), 2); assert_eq!(arr[0].as_f64().unwrap(), 1.0);
1920 assert_eq!(arr[1].as_f64().unwrap(), 2.0);
1921 }
1922
1923 #[test]
1924 fn test_reject_objects() {
1925 let runtime = setup_runtime();
1926 let data = json!([{"active": true}, {"active": false}, {"active": true}]);
1927 let expr = runtime.compile("reject(&active, @)").unwrap();
1928 let result = expr.search(&data).unwrap();
1929 let arr = result.as_array().unwrap();
1930 assert_eq!(arr.len(), 1); }
1932
1933 #[test]
1934 fn test_map_keys() {
1935 let runtime = setup_runtime();
1936 let data = json!({"abc": 1, "de": 2});
1938 let expr = runtime.compile("map_keys(&length(@), @)").unwrap();
1939 let result = expr.search(&data).unwrap();
1940 let obj = result.as_object().unwrap();
1941 assert!(obj.contains_key("3") || obj.contains_key("2"));
1943 }
1944
1945 #[test]
1946 fn test_map_values_add() {
1947 let runtime = setup_runtime();
1948 let data = json!({"a": 1, "b": 2, "c": 3});
1950 let expr = runtime.compile("map_values(&sum(`[1]`), @)").unwrap();
1951 let result = expr.search(&data).unwrap();
1952 let obj = result.as_object().unwrap();
1953 assert_eq!(obj.get("a").unwrap().as_f64().unwrap(), 1.0);
1955 }
1956
1957 #[test]
1958 fn test_map_values_length() {
1959 let runtime = setup_runtime();
1960 let data = json!({"name": "alice", "city": "boston"});
1961 let expr = runtime.compile("map_values(&length(@), @)").unwrap();
1962 let result = expr.search(&data).unwrap();
1963 let obj = result.as_object().unwrap();
1964 assert_eq!(obj.get("name").unwrap().as_f64().unwrap(), 5.0); assert_eq!(obj.get("city").unwrap().as_f64().unwrap(), 6.0); }
1967
1968 #[test]
1969 fn test_map_values_with_string_fns() {
1970 let runtime = setup_runtime();
1971 let data = json!({"name": "alice", "city": "boston"});
1972 let expr = runtime.compile("map_values(&upper(@), @)").unwrap();
1973 let result = expr.search(&data).unwrap();
1974 let obj = result.as_object().unwrap();
1975 assert_eq!(obj.get("name").unwrap().as_str().unwrap(), "ALICE");
1976 assert_eq!(obj.get("city").unwrap().as_str().unwrap(), "BOSTON");
1977 }
1978
1979 #[test]
1980 fn test_map_keys_with_string_fns() {
1981 let runtime = setup_runtime();
1982 let data = json!({"hello": 1, "world": 2});
1983 let expr = runtime.compile("map_keys(&upper(@), @)").unwrap();
1984 let result = expr.search(&data).unwrap();
1985 let obj = result.as_object().unwrap();
1986 assert!(obj.contains_key("HELLO"));
1987 assert!(obj.contains_key("WORLD"));
1988 }
1989
1990 #[test]
1991 fn test_order_by_single_field_asc() {
1992 let runtime = setup_runtime();
1993 let data = json!([
1994 {"name": "Charlie", "age": 30},
1995 {"name": "Alice", "age": 25},
1996 {"name": "Bob", "age": 35}
1997 ]);
1998 let expr = runtime
1999 .compile(r#"order_by(@, `[["name", "asc"]]`)"#)
2000 .unwrap();
2001 let result = expr.search(&data).unwrap();
2002 let arr = result.as_array().unwrap();
2003 assert_eq!(
2004 arr[0]
2005 .as_object()
2006 .unwrap()
2007 .get("name")
2008 .unwrap()
2009 .as_str()
2010 .unwrap(),
2011 "Alice"
2012 );
2013 assert_eq!(
2014 arr[1]
2015 .as_object()
2016 .unwrap()
2017 .get("name")
2018 .unwrap()
2019 .as_str()
2020 .unwrap(),
2021 "Bob"
2022 );
2023 assert_eq!(
2024 arr[2]
2025 .as_object()
2026 .unwrap()
2027 .get("name")
2028 .unwrap()
2029 .as_str()
2030 .unwrap(),
2031 "Charlie"
2032 );
2033 }
2034
2035 #[test]
2036 fn test_order_by_single_field_desc() {
2037 let runtime = setup_runtime();
2038 let data = json!([
2039 {"name": "Alice", "age": 25},
2040 {"name": "Bob", "age": 35},
2041 {"name": "Charlie", "age": 30}
2042 ]);
2043 let expr = runtime
2044 .compile(r#"order_by(@, `[["age", "desc"]]`)"#)
2045 .unwrap();
2046 let result = expr.search(&data).unwrap();
2047 let arr = result.as_array().unwrap();
2048 assert_eq!(
2049 arr[0]
2050 .as_object()
2051 .unwrap()
2052 .get("age")
2053 .unwrap()
2054 .as_f64()
2055 .unwrap(),
2056 35.0
2057 );
2058 assert_eq!(
2059 arr[1]
2060 .as_object()
2061 .unwrap()
2062 .get("age")
2063 .unwrap()
2064 .as_f64()
2065 .unwrap(),
2066 30.0
2067 );
2068 assert_eq!(
2069 arr[2]
2070 .as_object()
2071 .unwrap()
2072 .get("age")
2073 .unwrap()
2074 .as_f64()
2075 .unwrap(),
2076 25.0
2077 );
2078 }
2079
2080 #[test]
2081 fn test_order_by_multiple_fields() {
2082 let runtime = setup_runtime();
2083 let data = json!([
2084 {"dept": "sales", "name": "Bob"},
2085 {"dept": "eng", "name": "Alice"},
2086 {"dept": "sales", "name": "Alice"}
2087 ]);
2088 let expr = runtime
2089 .compile(r#"order_by(@, `[["dept", "asc"], ["name", "asc"]]`)"#)
2090 .unwrap();
2091 let result = expr.search(&data).unwrap();
2092 let arr = result.as_array().unwrap();
2093 assert_eq!(
2095 arr[0]
2096 .as_object()
2097 .unwrap()
2098 .get("dept")
2099 .unwrap()
2100 .as_str()
2101 .unwrap(),
2102 "eng"
2103 );
2104 assert_eq!(
2106 arr[1]
2107 .as_object()
2108 .unwrap()
2109 .get("name")
2110 .unwrap()
2111 .as_str()
2112 .unwrap(),
2113 "Alice"
2114 );
2115 assert_eq!(
2116 arr[2]
2117 .as_object()
2118 .unwrap()
2119 .get("name")
2120 .unwrap()
2121 .as_str()
2122 .unwrap(),
2123 "Bob"
2124 );
2125 }
2126
2127 #[test]
2128 fn test_reduce_expr_sum() {
2129 let runtime = setup_runtime();
2130 let data = json!([1, 2, 3, 4, 5]);
2131 let expr = runtime
2132 .compile("reduce_expr(&sum([accumulator, current]), @, `0`)")
2133 .unwrap();
2134 let result = expr.search(&data).unwrap();
2135 assert_eq!(result.as_f64().unwrap(), 15.0);
2136 }
2137
2138 #[test]
2139 fn test_reduce_expr_max() {
2140 let runtime = setup_runtime();
2141 let data = json!([3, 1, 4, 1, 5, 9, 2, 6]);
2142 let expr = runtime
2143 .compile("reduce_expr(&max([accumulator, current]), @, `0`)")
2144 .unwrap();
2145 let result = expr.search(&data).unwrap();
2146 assert_eq!(result.as_f64().unwrap(), 9.0);
2147 }
2148
2149 #[test]
2150 fn test_reduce_expr_empty() {
2151 let runtime = setup_runtime();
2152 let data = json!([]);
2153 let expr = runtime
2154 .compile("reduce_expr(&sum([accumulator, current]), @, `42`)")
2155 .unwrap();
2156 let result = expr.search(&data).unwrap();
2157 assert_eq!(result.as_f64().unwrap(), 42.0); }
2159
2160 #[test]
2161 fn test_fold_alias() {
2162 let runtime = setup_runtime();
2163 let data = json!([1, 2, 3]);
2164 let expr = runtime
2165 .compile("fold(&sum([accumulator, current]), @, `0`)")
2166 .unwrap();
2167 let result = expr.search(&data).unwrap();
2168 assert_eq!(result.as_f64().unwrap(), 6.0);
2169 }
2170
2171 #[test]
2172 fn test_scan_expr_running_sum() {
2173 let runtime = setup_runtime();
2174 let data = json!([1, 2, 3, 4]);
2175 let expr = runtime
2176 .compile("scan_expr(&sum([accumulator, current]), @, `0`)")
2177 .unwrap();
2178 let result = expr.search(&data).unwrap();
2179 let arr = result.as_array().unwrap();
2180 assert_eq!(arr.len(), 4);
2182 assert_eq!(arr[0].as_f64().unwrap(), 1.0);
2183 assert_eq!(arr[1].as_f64().unwrap(), 3.0);
2184 assert_eq!(arr[2].as_f64().unwrap(), 6.0);
2185 assert_eq!(arr[3].as_f64().unwrap(), 10.0);
2186 }
2187
2188 #[test]
2189 fn test_scan_expr_empty() {
2190 let runtime = setup_runtime();
2191 let data = json!([]);
2192 let expr = runtime
2193 .compile("scan_expr(&sum([accumulator, current]), @, `0`)")
2194 .unwrap();
2195 let result = expr.search(&data).unwrap();
2196 let arr = result.as_array().unwrap();
2197 assert_eq!(arr.len(), 0);
2198 }
2199
2200 #[test]
2201 fn test_reduce_expr_with_index() {
2202 let runtime = setup_runtime();
2203 let data = json!([10, 20, 30]);
2205 let expr = runtime
2206 .compile("reduce_expr(&sum([accumulator, index]), @, `0`)")
2207 .unwrap();
2208 let result = expr.search(&data).unwrap();
2209 assert_eq!(result.as_f64().unwrap(), 3.0);
2211 }
2212
2213 #[test]
2214 fn test_count_by_objects() {
2215 let runtime = setup_runtime();
2216 let data = json!([
2217 {"type": "a"},
2218 {"type": "b"},
2219 {"type": "a"},
2220 {"type": "a"}
2221 ]);
2222 let expr = runtime.compile("count_by(&type, @)").unwrap();
2223 let result = expr.search(&data).unwrap();
2224 let obj = result.as_object().unwrap();
2225 assert_eq!(obj.get("a").unwrap().as_f64().unwrap(), 3.0);
2226 assert_eq!(obj.get("b").unwrap().as_f64().unwrap(), 1.0);
2227 }
2228
2229 #[test]
2230 fn test_count_by_strings() {
2231 let runtime = setup_runtime();
2232 let data = json!(["a", "b", "a", "c", "a"]);
2233 let expr = runtime.compile("count_by(&@, @)").unwrap();
2234 let result = expr.search(&data).unwrap();
2235 let obj = result.as_object().unwrap();
2236 assert_eq!(obj.get("a").unwrap().as_f64().unwrap(), 3.0);
2237 assert_eq!(obj.get("b").unwrap().as_f64().unwrap(), 1.0);
2238 assert_eq!(obj.get("c").unwrap().as_f64().unwrap(), 1.0);
2239 }
2240
2241 #[test]
2242 fn test_count_by_empty() {
2243 let runtime = setup_runtime();
2244 let data = json!([]);
2245 let expr = runtime.compile("count_by(&type, @)").unwrap();
2246 let result = expr.search(&data).unwrap();
2247 let obj = result.as_object().unwrap();
2248 assert!(obj.is_empty());
2249 }
2250
2251 #[test]
2252 fn test_count_by_numbers() {
2253 let runtime = setup_runtime();
2254 let data = json!([1, 2, 1, 3, 1, 2]);
2255 let expr = runtime.compile("count_by(&@, @)").unwrap();
2256 let result = expr.search(&data).unwrap();
2257 let obj = result.as_object().unwrap();
2258 assert_eq!(obj.get("1").unwrap().as_f64().unwrap(), 3.0);
2259 assert_eq!(obj.get("2").unwrap().as_f64().unwrap(), 2.0);
2260 assert_eq!(obj.get("3").unwrap().as_f64().unwrap(), 1.0);
2261 }
2262
2263 #[test]
2268 fn test_partial_creates_object() {
2269 let runtime = setup_runtime();
2270 let data = json!(null);
2271 let expr = runtime.compile("partial('length')").unwrap();
2272 let result = expr.search(&data).unwrap();
2273 let obj = result.as_object().unwrap();
2274 assert!(obj.get("__partial__").unwrap().as_bool().unwrap());
2275 assert_eq!(obj.get("fn").unwrap().as_str().unwrap(), "length");
2276 assert!(obj.get("args").unwrap().as_array().unwrap().is_empty());
2277 }
2278
2279 #[test]
2280 fn test_partial_with_args() {
2281 let runtime = setup_runtime();
2282 let data = json!(null);
2283 let expr = runtime
2284 .compile("partial('contains', `\"hello world\"`)")
2285 .unwrap();
2286 let result = expr.search(&data).unwrap();
2287 let obj = result.as_object().unwrap();
2288 assert!(obj.get("__partial__").unwrap().as_bool().unwrap());
2289 assert_eq!(obj.get("fn").unwrap().as_str().unwrap(), "contains");
2290 let args = obj.get("args").unwrap().as_array().unwrap();
2291 assert_eq!(args.len(), 1);
2292 assert_eq!(args[0].as_str().unwrap(), "hello world");
2293 }
2294
2295 #[test]
2296 fn test_apply_with_fn_name() {
2297 let runtime = setup_runtime();
2298 let data = json!(null);
2299 let expr = runtime.compile("apply('length', `\"hello\"`)").unwrap();
2300 let result = expr.search(&data).unwrap();
2301 assert_eq!(result.as_f64().unwrap(), 5.0);
2302 }
2303
2304 #[test]
2305 fn test_apply_with_partial() {
2306 let runtime = setup_runtime();
2307 let data = json!(null);
2308 let expr = runtime
2310 .compile("apply(partial('contains', `\"hello world\"`), `\"world\"`)")
2311 .unwrap();
2312 let result = expr.search(&data).unwrap();
2313 assert!(result.as_bool().unwrap());
2314 }
2315
2316 #[test]
2317 fn test_apply_partial_not_found() {
2318 let runtime = setup_runtime();
2319 let data = json!(null);
2320 let expr = runtime
2321 .compile("apply(partial('contains', `\"hello world\"`), `\"xyz\"`)")
2322 .unwrap();
2323 let result = expr.search(&data).unwrap();
2324 assert!(!result.as_bool().unwrap());
2325 }
2326
2327 #[test]
2328 fn test_partial_with_multiple_prefilled_args() {
2329 let runtime = setup_runtime();
2330 let data = json!(null);
2331 let expr = runtime.compile("partial('join', `\"-\"`)").unwrap();
2333 let result = expr.search(&data).unwrap();
2334 let obj = result.as_object().unwrap();
2335 let args = obj.get("args").unwrap().as_array().unwrap();
2336 assert_eq!(args.len(), 1);
2337 assert_eq!(args[0].as_str().unwrap(), "-");
2338 }
2339
2340 #[test]
2341 fn test_apply_partial_join() {
2342 let runtime = setup_runtime();
2343 let data = json!(null);
2344 let expr = runtime
2346 .compile("apply(partial('join', `\"-\"`), `[\"a\", \"b\", \"c\"]`)")
2347 .unwrap();
2348 let result = expr.search(&data).unwrap();
2349 assert_eq!(result.as_str().unwrap(), "a-b-c");
2350 }
2351
2352 #[test]
2357 fn test_pipeline_filter_sort_products() {
2358 let runtime = setup_runtime();
2359 let data = json!({
2360 "products": [
2361 {"name": "A", "price": 30, "in_stock": true},
2362 {"name": "B", "price": 10, "in_stock": true},
2363 {"name": "C", "price": 20, "in_stock": false},
2364 {"name": "D", "price": 5, "in_stock": true}
2365 ]
2366 });
2367 let expr = runtime
2368 .compile("products | filter_expr(&in_stock, @) | sort_by_expr(&price, @)")
2369 .unwrap();
2370 let result = expr.search(&data).unwrap();
2371 let arr = result.as_array().unwrap();
2372 assert_eq!(arr.len(), 3);
2373 assert_eq!(
2374 arr[0]
2375 .as_object()
2376 .unwrap()
2377 .get("name")
2378 .unwrap()
2379 .as_str()
2380 .unwrap(),
2381 "D"
2382 ); assert_eq!(
2384 arr[1]
2385 .as_object()
2386 .unwrap()
2387 .get("name")
2388 .unwrap()
2389 .as_str()
2390 .unwrap(),
2391 "B"
2392 ); }
2394
2395 #[test]
2396 fn test_pipeline_funnel_errors() {
2397 let runtime = setup_runtime();
2398 let data = json!({
2399 "events": [
2400 {"level": "error", "timestamp": 1704067300, "message": "Disk full"},
2401 {"level": "info", "timestamp": 1704067200, "message": "Started"},
2402 {"level": "error", "timestamp": 1704067400, "message": "Connection lost"},
2403 {"level": "warn", "timestamp": 1704067350, "message": "High memory"}
2404 ]
2405 });
2406 let expr = runtime
2407 .compile(
2408 r#"events | filter_expr(&(level == `"error"`), @) | sort_by_expr(×tamp, @)"#,
2409 )
2410 .unwrap();
2411 let result = expr.search(&data).unwrap();
2412 let arr = result.as_array().unwrap();
2413 assert_eq!(arr.len(), 2);
2414 assert_eq!(
2416 arr[0]
2417 .as_object()
2418 .unwrap()
2419 .get("message")
2420 .unwrap()
2421 .as_str()
2422 .unwrap(),
2423 "Disk full"
2424 );
2425 }
2426
2427 #[test]
2428 fn test_pipeline_transactions_completed() {
2429 let runtime = setup_runtime();
2430 let data = json!({
2431 "transactions": [
2432 {"amount": 100, "status": "completed"},
2433 {"amount": 50, "status": "completed"},
2434 {"amount": 75, "status": "pending"},
2435 {"amount": 200, "status": "completed"}
2436 ]
2437 });
2438 let expr = runtime
2439 .compile(
2440 r#"transactions | filter_expr(&(status == `"completed"`), @) | map_expr(&amount, @)"#,
2441 )
2442 .unwrap();
2443 let result = expr.search(&data).unwrap();
2444 let arr = result.as_array().unwrap();
2445 assert_eq!(arr.len(), 3);
2446 assert_eq!(arr[0].as_f64().unwrap(), 100.0);
2447 assert_eq!(arr[1].as_f64().unwrap(), 50.0);
2448 assert_eq!(arr[2].as_f64().unwrap(), 200.0);
2449 }
2450
2451 #[test]
2452 fn test_pipeline_fork_join() {
2453 let runtime = setup_runtime();
2454 let data = json!({
2455 "items": [
2456 {"name": "A", "price": 150},
2457 {"name": "B", "price": 50},
2458 {"name": "C", "price": 200},
2459 {"name": "D", "price": 25}
2460 ]
2461 });
2462 let expr = runtime
2463 .compile(
2464 r#"@.{
2465 expensive: items | filter_expr(&(price > `100`), @),
2466 cheap: items | filter_expr(&(price <= `100`), @)
2467 }"#,
2468 )
2469 .unwrap();
2470 let result = expr.search(&data).unwrap();
2471 let obj = result.as_object().unwrap();
2472 assert_eq!(obj.get("expensive").unwrap().as_array().unwrap().len(), 2);
2473 assert_eq!(obj.get("cheap").unwrap().as_array().unwrap().len(), 2);
2474 }
2475
2476 #[test]
2477 fn test_pipeline_nested_users() {
2478 let runtime = setup_runtime();
2479 let data = json!({
2480 "users": [
2481 {"name": "Alice", "orders": [{"total": 100}, {"total": 50}]},
2482 {"name": "Bob", "orders": [{"total": 200}]},
2483 {"name": "Carol", "orders": []}
2484 ]
2485 });
2486 let expr = runtime
2488 .compile("users | filter_expr(&(length(orders) > `0`), @) | map_expr(&name, @)")
2489 .unwrap();
2490 let result = expr.search(&data).unwrap();
2491 let arr = result.as_array().unwrap();
2492 assert_eq!(arr.len(), 2);
2493 assert_eq!(arr[0].as_str().unwrap(), "Alice");
2494 assert_eq!(arr[1].as_str().unwrap(), "Bob");
2495 }
2496
2497 #[test]
2498 fn test_pipeline_rag_chunks() {
2499 let runtime = setup_runtime();
2500 let data = json!({
2501 "chunks": [
2502 {"content": "Redis is fast", "score": 0.9},
2503 {"content": "Redis is in-memory", "score": 0.85},
2504 {"content": "Unrelated content", "score": 0.5},
2505 {"content": "Redis supports modules", "score": 0.75}
2506 ]
2507 });
2508 let expr = runtime
2509 .compile("chunks | filter_expr(&(score > `0.7`), @) | sort_by_expr(&score, @)")
2510 .unwrap();
2511 let result = expr.search(&data).unwrap();
2512 let arr = result.as_array().unwrap();
2513 assert_eq!(arr.len(), 3);
2514 assert_eq!(
2516 arr[0]
2517 .as_object()
2518 .unwrap()
2519 .get("score")
2520 .unwrap()
2521 .as_f64()
2522 .unwrap(),
2523 0.75
2524 );
2525 }
2526
2527 #[test]
2532 fn test_reduce_expr_product() {
2533 let runtime = setup_runtime();
2534 let data = json!([5, 3, 8, 1, 9]);
2536 let expr = runtime
2537 .compile("reduce_expr(&min([accumulator, current]), @, `100`)")
2538 .unwrap();
2539 let result = expr.search(&data).unwrap();
2540 assert_eq!(result.as_f64().unwrap(), 1.0);
2541 }
2542
2543 #[test]
2544 fn test_scan_expr_running_balance() {
2545 let runtime = setup_runtime();
2546 let data = json!([3, 1, 4, 1, 5, 9]);
2548 let expr = runtime
2549 .compile("scan_expr(&max([accumulator, current]), @, `0`)")
2550 .unwrap();
2551 let result = expr.search(&data).unwrap();
2552 let arr = result.as_array().unwrap();
2553 assert_eq!(arr[0].as_f64().unwrap(), 3.0);
2555 assert_eq!(arr[1].as_f64().unwrap(), 3.0);
2556 assert_eq!(arr[2].as_f64().unwrap(), 4.0);
2557 assert_eq!(arr[3].as_f64().unwrap(), 4.0);
2558 assert_eq!(arr[4].as_f64().unwrap(), 5.0);
2559 assert_eq!(arr[5].as_f64().unwrap(), 9.0);
2560 }
2561
2562 #[test]
2567 fn test_order_by_three_fields() {
2568 let runtime = setup_runtime();
2569 let data = json!([
2570 {"dept": "Engineering", "level": "senior", "name": "Charlie"},
2571 {"dept": "Engineering", "level": "junior", "name": "Alice"},
2572 {"dept": "Engineering", "level": "senior", "name": "Bob"},
2573 {"dept": "Sales", "level": "senior", "name": "David"}
2574 ]);
2575 let expr = runtime
2576 .compile(r#"order_by(@, `[["dept", "asc"], ["level", "desc"], ["name", "asc"]]`)"#)
2577 .unwrap();
2578 let result = expr.search(&data).unwrap();
2579 let arr = result.as_array().unwrap();
2580 assert_eq!(
2582 arr[0]
2583 .as_object()
2584 .unwrap()
2585 .get("name")
2586 .unwrap()
2587 .as_str()
2588 .unwrap(),
2589 "Bob"
2590 );
2591 assert_eq!(
2592 arr[1]
2593 .as_object()
2594 .unwrap()
2595 .get("name")
2596 .unwrap()
2597 .as_str()
2598 .unwrap(),
2599 "Charlie"
2600 );
2601 }
2602
2603 #[test]
2604 fn test_order_by_empty() {
2605 let runtime = setup_runtime();
2606 let data = json!([]);
2607 let expr = runtime
2608 .compile(r#"order_by(@, `[["name", "asc"]]`)"#)
2609 .unwrap();
2610 let result = expr.search(&data).unwrap();
2611 let arr = result.as_array().unwrap();
2612 assert!(arr.is_empty());
2613 }
2614
2615 #[test]
2620 fn test_partition_expr_scores() {
2621 let runtime = setup_runtime();
2622 let data = json!([85, 42, 91, 67, 55, 78, 33, 99]);
2623 let expr = runtime.compile("partition_expr(&(@ >= `60`), @)").unwrap();
2624 let result = expr.search(&data).unwrap();
2625 let arr = result.as_array().unwrap();
2626 let passing = arr[0].as_array().unwrap();
2627 let failing = arr[1].as_array().unwrap();
2628 assert_eq!(passing.len(), 5); assert_eq!(failing.len(), 3); }
2631
2632 #[test]
2633 fn test_partition_expr_active() {
2634 let runtime = setup_runtime();
2635 let data = json!([{"active": true}, {"active": false}, {"active": true}]);
2636 let expr = runtime.compile("partition_expr(&active, @)").unwrap();
2637 let result = expr.search(&data).unwrap();
2638 let arr = result.as_array().unwrap();
2639 assert_eq!(arr[0].as_array().unwrap().len(), 2);
2640 assert_eq!(arr[1].as_array().unwrap().len(), 1);
2641 }
2642
2643 #[test]
2648 fn test_map_values_discount() {
2649 let runtime = setup_runtime();
2650 let data = json!({"apple": "FRUIT", "banana": "ITEM"});
2652 let expr = runtime.compile("map_values(&length(@), @)").unwrap();
2653 let result = expr.search(&data).unwrap();
2654 let obj = result.as_object().unwrap();
2655 assert_eq!(obj.get("apple").unwrap().as_f64().unwrap(), 5.0);
2656 assert_eq!(obj.get("banana").unwrap().as_f64().unwrap(), 4.0);
2657 }
2658
2659 #[test]
2664 fn test_group_by_expr_type() {
2665 let runtime = setup_runtime();
2666 let data = json!([
2667 {"type": "fruit", "name": "apple"},
2668 {"type": "vegetable", "name": "carrot"},
2669 {"type": "fruit", "name": "banana"}
2670 ]);
2671 let expr = runtime.compile("group_by_expr(&type, @)").unwrap();
2672 let result = expr.search(&data).unwrap();
2673 let obj = result.as_object().unwrap();
2674 assert_eq!(obj.get("fruit").unwrap().as_array().unwrap().len(), 2);
2675 assert_eq!(obj.get("vegetable").unwrap().as_array().unwrap().len(), 1);
2676 }
2677
2678 #[test]
2679 fn test_group_by_expr_computed() {
2680 let runtime = setup_runtime();
2681 let data = json!(["a", "bb", "ccc", "dd", "eee", "f"]);
2683 let expr = runtime
2684 .compile("group_by_expr(&to_string(length(@)), @)")
2685 .unwrap();
2686 let result = expr.search(&data).unwrap();
2687 let obj = result.as_object().unwrap();
2688 assert!(obj.contains_key("1")); assert!(obj.contains_key("2")); assert!(obj.contains_key("3")); assert_eq!(obj.get("1").unwrap().as_array().unwrap().len(), 2);
2692 assert_eq!(obj.get("2").unwrap().as_array().unwrap().len(), 2);
2693 assert_eq!(obj.get("3").unwrap().as_array().unwrap().len(), 2);
2694 }
2695
2696 #[test]
2701 fn test_unique_by_expr_id() {
2702 let runtime = setup_runtime();
2703 let data = json!([
2704 {"id": 1, "v": "a"},
2705 {"id": 2, "v": "b"},
2706 {"id": 1, "v": "c"}
2707 ]);
2708 let expr = runtime.compile("unique_by_expr(&id, @)").unwrap();
2709 let result = expr.search(&data).unwrap();
2710 let arr = result.as_array().unwrap();
2711 assert_eq!(arr.len(), 2);
2712 assert_eq!(
2714 arr[0]
2715 .as_object()
2716 .unwrap()
2717 .get("v")
2718 .unwrap()
2719 .as_str()
2720 .unwrap(),
2721 "a"
2722 );
2723 }
2724
2725 #[test]
2730 fn test_any_expr_empty() {
2731 let runtime = setup_runtime();
2732 let data = json!([]);
2733 let expr = runtime.compile("any_expr(&(@ > `0`), @)").unwrap();
2734 let result = expr.search(&data).unwrap();
2735 assert!(!result.as_bool().unwrap());
2736 }
2737
2738 #[test]
2739 fn test_max_by_expr_empty() {
2740 let runtime = setup_runtime();
2741 let data = json!([]);
2742 let expr = runtime.compile("max_by_expr(&age, @)").unwrap();
2743 let result = expr.search(&data).unwrap();
2744 assert!(result.is_null());
2745 }
2746
2747 #[test]
2748 fn test_flat_map_expr_duplicate() {
2749 let runtime = setup_runtime();
2750 let data = json!([1, 2, 3]);
2751 let expr = runtime.compile("flat_map_expr(&[@, @], @)").unwrap();
2753 let result = expr.search(&data).unwrap();
2754 let arr = result.as_array().unwrap();
2755 assert_eq!(arr.len(), 6);
2756 }
2757
2758 #[test]
2759 fn test_reject_greater_than() {
2760 let runtime = setup_runtime();
2761 let data = json!([1, 2, 3, 4, 5, 6]);
2762 let expr = runtime.compile("reject(&(@ > `3`), @)").unwrap();
2763 let result = expr.search(&data).unwrap();
2764 let arr = result.as_array().unwrap();
2765 assert_eq!(arr.len(), 3); }
2767
2768 #[test]
2769 fn test_every_false_case() {
2770 let runtime = setup_runtime();
2771 let data = json!([1, -1, 3]);
2772 let expr = runtime.compile("every(&(@ > `0`), @)").unwrap();
2773 let result = expr.search(&data).unwrap();
2774 assert!(!result.as_bool().unwrap());
2775 }
2776
2777 #[test]
2778 fn test_count_expr_all_match() {
2779 let runtime = setup_runtime();
2780 let data = json!([5, 10, 15, 20]);
2781 let expr = runtime.compile("count_expr(&(@ > `0`), @)").unwrap();
2782 let result = expr.search(&data).unwrap();
2783 assert_eq!(result.as_f64().unwrap(), 4.0);
2784 }
2785
2786 #[test]
2787 fn test_find_expr_first_match() {
2788 let runtime = setup_runtime();
2789 let data = json!([1, 5, 10, 15]);
2790 let expr = runtime.compile("find_expr(&(@ > `3`), @)").unwrap();
2791 let result = expr.search(&data).unwrap();
2792 assert_eq!(result.as_f64().unwrap(), 5.0);
2793 }
2794
2795 #[test]
2796 fn test_find_index_expr_first_match() {
2797 let runtime = setup_runtime();
2798 let data = json!([1, 5, 10, 15]);
2799 let expr = runtime.compile("find_index_expr(&(@ > `3`), @)").unwrap();
2800 let result = expr.search(&data).unwrap();
2801 assert_eq!(result.as_f64().unwrap(), 1.0);
2802 }
2803
2804 #[test]
2805 fn test_take_while_basic() {
2806 let runtime = setup_runtime();
2807 let data = json!([1, 2, 3, 5, 1, 2]);
2808 let expr = runtime.compile("take_while(&(@ < `4`), @)").unwrap();
2809 let result = expr.search(&data).unwrap();
2810 let arr = result.as_array().unwrap();
2811 assert_eq!(arr.len(), 3);
2812 assert_eq!(arr[0].as_f64().unwrap(), 1.0);
2813 assert_eq!(arr[1].as_f64().unwrap(), 2.0);
2814 assert_eq!(arr[2].as_f64().unwrap(), 3.0);
2815 }
2816
2817 #[test]
2818 fn test_take_while_all_match() {
2819 let runtime = setup_runtime();
2820 let data = json!([1, 2, 3]);
2821 let expr = runtime.compile("take_while(&(@ < `10`), @)").unwrap();
2822 let result = expr.search(&data).unwrap();
2823 let arr = result.as_array().unwrap();
2824 assert_eq!(arr.len(), 3);
2825 }
2826
2827 #[test]
2828 fn test_take_while_none_match() {
2829 let runtime = setup_runtime();
2830 let data = json!([5, 6, 7]);
2831 let expr = runtime.compile("take_while(&(@ < `4`), @)").unwrap();
2832 let result = expr.search(&data).unwrap();
2833 let arr = result.as_array().unwrap();
2834 assert_eq!(arr.len(), 0);
2835 }
2836
2837 #[test]
2838 fn test_drop_while_basic() {
2839 let runtime = setup_runtime();
2840 let data = json!([1, 2, 3, 5, 1, 2]);
2841 let expr = runtime.compile("drop_while(&(@ < `4`), @)").unwrap();
2842 let result = expr.search(&data).unwrap();
2843 let arr = result.as_array().unwrap();
2844 assert_eq!(arr.len(), 3);
2845 assert_eq!(arr[0].as_f64().unwrap(), 5.0);
2846 assert_eq!(arr[1].as_f64().unwrap(), 1.0);
2847 assert_eq!(arr[2].as_f64().unwrap(), 2.0);
2848 }
2849
2850 #[test]
2851 fn test_drop_while_all_match() {
2852 let runtime = setup_runtime();
2853 let data = json!([1, 2, 3]);
2854 let expr = runtime.compile("drop_while(&(@ < `10`), @)").unwrap();
2855 let result = expr.search(&data).unwrap();
2856 let arr = result.as_array().unwrap();
2857 assert_eq!(arr.len(), 0);
2858 }
2859
2860 #[test]
2861 fn test_drop_while_none_match() {
2862 let runtime = setup_runtime();
2863 let data = json!([5, 6, 7]);
2864 let expr = runtime.compile("drop_while(&(@ < `4`), @)").unwrap();
2865 let result = expr.search(&data).unwrap();
2866 let arr = result.as_array().unwrap();
2867 assert_eq!(arr.len(), 3);
2868 }
2869
2870 #[test]
2871 fn test_zip_with_add() {
2872 let runtime = setup_runtime();
2873 let data = json!(null);
2874 let expr = runtime
2875 .compile("zip_with(&add([0], [1]), `[1, 2, 3]`, `[10, 20, 30]`)")
2876 .unwrap();
2877 let result = expr.search(&data).unwrap();
2878 let arr = result.as_array().unwrap();
2879 assert_eq!(arr.len(), 3);
2880 assert_eq!(arr[0].as_f64().unwrap(), 11.0);
2881 assert_eq!(arr[1].as_f64().unwrap(), 22.0);
2882 assert_eq!(arr[2].as_f64().unwrap(), 33.0);
2883 }
2884
2885 #[test]
2886 fn test_zip_with_unequal_lengths() {
2887 let runtime = setup_runtime();
2888 let data = json!(null);
2889 let expr = runtime
2890 .compile("zip_with(&add([0], [1]), `[1, 2, 3, 4, 5]`, `[10, 20]`)")
2891 .unwrap();
2892 let result = expr.search(&data).unwrap();
2893 let arr = result.as_array().unwrap();
2894 assert_eq!(arr.len(), 2);
2895 assert_eq!(arr[0].as_f64().unwrap(), 11.0);
2896 assert_eq!(arr[1].as_f64().unwrap(), 22.0);
2897 }
2898
2899 #[test]
2900 fn test_zip_with_multiply() {
2901 let runtime = setup_runtime();
2902 let data = json!(null);
2903 let expr = runtime
2904 .compile("zip_with(&multiply([0], [1]), `[2, 3, 4]`, `[5, 6, 7]`)")
2905 .unwrap();
2906 let result = expr.search(&data).unwrap();
2907 let arr = result.as_array().unwrap();
2908 assert_eq!(arr.len(), 3);
2909 assert_eq!(arr[0].as_f64().unwrap(), 10.0);
2910 assert_eq!(arr[1].as_f64().unwrap(), 18.0);
2911 assert_eq!(arr[2].as_f64().unwrap(), 28.0);
2912 }
2913
2914 #[test]
2919 fn test_walk_identity() {
2920 let runtime = setup_runtime();
2921 let data = json!({"a": [1, 2, 3], "b": {"c": 4}});
2922 let expr = runtime.compile("walk(&@, @)").unwrap();
2923 let result = expr.search(&data).unwrap();
2924 assert!(result.is_object());
2926 let obj = result.as_object().unwrap();
2927 assert!(obj.contains_key("a"));
2928 assert!(obj.contains_key("b"));
2929 }
2930
2931 #[test]
2932 fn test_walk_type_of_all() {
2933 let runtime = setup_runtime();
2934 let data = json!({"a": 5, "b": [1, 2]});
2935 let expr = runtime.compile("walk(&type(@), @)").unwrap();
2937 let result = expr.search(&data).unwrap();
2938 assert_eq!(result.as_str().unwrap(), "object");
2941 }
2942
2943 #[test]
2944 fn test_walk_nested_arrays() {
2945 let runtime = setup_runtime();
2946 let data = json!([[[], []], [[]]]);
2948 let expr = runtime.compile("walk(&length(@), @)").unwrap();
2950 let result = expr.search(&data).unwrap();
2951 assert_eq!(result.as_f64().unwrap(), 2.0);
2953 }
2954
2955 #[test]
2956 fn test_walk_scalar() {
2957 let runtime = setup_runtime();
2958 let data = json!(5);
2959 let expr = runtime.compile("walk(&multiply(@, `2`), @)").unwrap();
2961 let result = expr.search(&data).unwrap();
2962 assert_eq!(result.as_f64().unwrap(), 10.0);
2963 }
2964
2965 #[test]
2966 fn test_walk_length_all() {
2967 let runtime = setup_runtime();
2968 let data = json!({"items": ["a", "bb", "ccc"]});
2969 let expr = runtime.compile("walk(&length(@), @)").unwrap();
2971 let result = expr.search(&data).unwrap();
2972 assert_eq!(result.as_f64().unwrap(), 1.0);
2974 }
2975
2976 #[test]
2977 fn test_walk_preserves_structure() {
2978 let runtime = setup_runtime();
2979 let data = json!({"a": [1, {"b": 2}], "c": "hello"});
2980 let expr = runtime.compile("walk(&@, @)").unwrap();
2982 let result = expr.search(&data).unwrap();
2983
2984 let obj = result.as_object().unwrap();
2985 assert!(obj.contains_key("a"));
2986 assert!(obj.contains_key("c"));
2987 let arr = obj.get("a").unwrap().as_array().unwrap();
2988 assert_eq!(arr.len(), 2);
2989 }
2990
2991 #[test]
2992 fn test_walk_empty_structures() {
2993 let runtime = setup_runtime();
2994
2995 let data = json!([]);
2997 let expr = runtime.compile("walk(&@, @)").unwrap();
2998 let result = expr.search(&data).unwrap();
2999 assert!(result.as_array().unwrap().is_empty());
3000
3001 let data = json!({});
3003 let result = expr.search(&data).unwrap();
3004 assert!(result.as_object().unwrap().is_empty());
3005 }
3006
3007 #[test]
3012 fn test_recurse_nested_object() {
3013 let runtime = setup_runtime();
3014 let data = json!({"a": {"b": 1}});
3015 let expr = runtime.compile("recurse(@)").unwrap();
3016 let result = expr.search(&data).unwrap();
3017 let arr = result.as_array().unwrap();
3018 assert_eq!(arr.len(), 3);
3020 }
3021
3022 #[test]
3023 fn test_recurse_nested_array() {
3024 let runtime = setup_runtime();
3025 let data = json!([1, [2, 3]]);
3026 let expr = runtime.compile("recurse(@)").unwrap();
3027 let result = expr.search(&data).unwrap();
3028 let arr = result.as_array().unwrap();
3029 assert_eq!(arr.len(), 5);
3031 }
3032
3033 #[test]
3034 fn test_recurse_scalar() {
3035 let runtime = setup_runtime();
3036 let data = json!(42);
3037 let expr = runtime.compile("recurse(@)").unwrap();
3038 let result = expr.search(&data).unwrap();
3039 let arr = result.as_array().unwrap();
3040 assert_eq!(arr.len(), 1);
3042 assert_eq!(arr[0].as_f64().unwrap(), 42.0);
3043 }
3044
3045 #[test]
3046 fn test_recurse_with_field() {
3047 let runtime = setup_runtime();
3048 let data = json!({"a": {"a": {"a": null}}});
3049 let expr = runtime.compile("recurse_with(@, &a)").unwrap();
3050 let result = expr.search(&data).unwrap();
3051 let arr = result.as_array().unwrap();
3052 assert_eq!(arr.len(), 3);
3054 }
3055
3056 #[test]
3057 fn test_recurse_with_children() {
3058 let runtime = setup_runtime();
3059 let data = json!({
3060 "name": "root",
3061 "children": [
3062 {"name": "child1", "children": []},
3063 {"name": "child2", "children": []}
3064 ]
3065 });
3066 let expr = runtime.compile("recurse_with(@, &children[])").unwrap();
3067 let result = expr.search(&data).unwrap();
3068 let arr = result.as_array().unwrap();
3069 assert_eq!(arr.len(), 3);
3071 }
3072
3073 #[test]
3078 fn test_while_expr_doubling() {
3079 let runtime = setup_runtime();
3080 let data = json!(null);
3081 let expr = runtime
3082 .compile("while_expr(`1`, &(@ < `100`), &multiply(@, `2`))")
3083 .unwrap();
3084 let result = expr.search(&data).unwrap();
3085 assert_eq!(result.as_f64().unwrap(), 128.0);
3087 }
3088
3089 #[test]
3090 fn test_while_expr_counter() {
3091 let runtime = setup_runtime();
3092 let data = json!(null);
3093 let expr = runtime
3094 .compile("while_expr(`0`, &(@ < `5`), &add(@, `1`))")
3095 .unwrap();
3096 let result = expr.search(&data).unwrap();
3097 assert_eq!(result.as_f64().unwrap(), 5.0);
3099 }
3100
3101 #[test]
3102 fn test_while_expr_immediate_false() {
3103 let runtime = setup_runtime();
3104 let data = json!(null);
3105 let expr = runtime
3106 .compile("while_expr(`100`, &(@ < `10`), &add(@, `1`))")
3107 .unwrap();
3108 let result = expr.search(&data).unwrap();
3109 assert_eq!(result.as_f64().unwrap(), 100.0);
3111 }
3112
3113 #[test]
3118 fn test_until_expr_doubling() {
3119 let runtime = setup_runtime();
3120 let data = json!(null);
3121 let expr = runtime
3122 .compile("until_expr(`1`, &(@ >= `100`), &multiply(@, `2`))")
3123 .unwrap();
3124 let result = expr.search(&data).unwrap();
3125 assert_eq!(result.as_f64().unwrap(), 128.0);
3127 }
3128
3129 #[test]
3130 fn test_until_expr_counter() {
3131 let runtime = setup_runtime();
3132 let data = json!(null);
3133 let expr = runtime
3134 .compile("until_expr(`0`, &(@ == `5`), &add(@, `1`))")
3135 .unwrap();
3136 let result = expr.search(&data).unwrap();
3137 assert_eq!(result.as_f64().unwrap(), 5.0);
3139 }
3140
3141 #[test]
3142 fn test_until_expr_immediate_true() {
3143 let runtime = setup_runtime();
3144 let data = json!(null);
3145 let expr = runtime
3146 .compile("until_expr(`100`, &(@ > `10`), &add(@, `1`))")
3147 .unwrap();
3148 let result = expr.search(&data).unwrap();
3149 assert_eq!(result.as_f64().unwrap(), 100.0);
3151 }
3152
3153 #[test]
3154 fn test_rank_with_ties() {
3155 let runtime = setup_runtime();
3156 let data = json!([{"score": 90}, {"score": 85}, {"score": 90}]);
3157 let expr = runtime.compile("rank(&score, @)").unwrap();
3158 let result = expr.search(&data).unwrap();
3159 assert_eq!(result, json!([1, 3, 1]));
3161 }
3162
3163 #[test]
3164 fn test_rank_no_ties() {
3165 let runtime = setup_runtime();
3166 let data = json!([{"score": 90}, {"score": 85}, {"score": 80}]);
3167 let expr = runtime.compile("rank(&score, @)").unwrap();
3168 let result = expr.search(&data).unwrap();
3169 assert_eq!(result, json!([1, 2, 3]));
3170 }
3171
3172 #[test]
3173 fn test_rank_empty_array() {
3174 let runtime = setup_runtime();
3175 let data = json!([]);
3176 let expr = runtime.compile("rank(&@, @)").unwrap();
3177 let result = expr.search(&data).unwrap();
3178 assert_eq!(result, json!([]));
3179 }
3180
3181 #[test]
3182 fn test_rank_all_same() {
3183 let runtime = setup_runtime();
3184 let data = json!([{"v": 5}, {"v": 5}, {"v": 5}]);
3185 let expr = runtime.compile("rank(&v, @)").unwrap();
3186 let result = expr.search(&data).unwrap();
3187 assert_eq!(result, json!([1, 1, 1]));
3188 }
3189
3190 #[test]
3191 fn test_rank_numbers() {
3192 let runtime = setup_runtime();
3193 let data = json!([3, 1, 2, 1]);
3194 let expr = runtime.compile("rank(&@, @)").unwrap();
3195 let result = expr.search(&data).unwrap();
3196 assert_eq!(result, json!([1, 3, 2, 3]));
3197 }
3198
3199 #[test]
3200 fn test_dense_rank_with_ties() {
3201 let runtime = setup_runtime();
3202 let data = json!([{"score": 90}, {"score": 85}, {"score": 90}]);
3203 let expr = runtime.compile("dense_rank(&score, @)").unwrap();
3204 let result = expr.search(&data).unwrap();
3205 assert_eq!(result, json!([1, 2, 1]));
3207 }
3208
3209 #[test]
3210 fn test_dense_rank_no_ties() {
3211 let runtime = setup_runtime();
3212 let data = json!([{"score": 90}, {"score": 85}, {"score": 80}]);
3213 let expr = runtime.compile("dense_rank(&score, @)").unwrap();
3214 let result = expr.search(&data).unwrap();
3215 assert_eq!(result, json!([1, 2, 3]));
3216 }
3217
3218 #[test]
3219 fn test_dense_rank_empty_array() {
3220 let runtime = setup_runtime();
3221 let data = json!([]);
3222 let expr = runtime.compile("dense_rank(&@, @)").unwrap();
3223 let result = expr.search(&data).unwrap();
3224 assert_eq!(result, json!([]));
3225 }
3226
3227 #[test]
3228 fn test_dense_rank_all_same() {
3229 let runtime = setup_runtime();
3230 let data = json!([{"v": 5}, {"v": 5}, {"v": 5}]);
3231 let expr = runtime.compile("dense_rank(&v, @)").unwrap();
3232 let result = expr.search(&data).unwrap();
3233 assert_eq!(result, json!([1, 1, 1]));
3234 }
3235
3236 #[test]
3237 fn test_dense_rank_numbers() {
3238 let runtime = setup_runtime();
3239 let data = json!([3, 1, 2, 1]);
3240 let expr = runtime.compile("dense_rank(&@, @)").unwrap();
3241 let result = expr.search(&data).unwrap();
3242 assert_eq!(result, json!([1, 3, 2, 3]));
3243 }
3244
3245 #[test]
3246 fn test_rank_vs_dense_rank() {
3247 let runtime = setup_runtime();
3248 let data = json!([{"s": 100}, {"s": 90}, {"s": 90}, {"s": 80}]);
3249
3250 let rank_expr = runtime.compile("rank(&s, @)").unwrap();
3251 let rank_result = rank_expr.search(&data).unwrap();
3252 assert_eq!(rank_result, json!([1, 2, 2, 4]));
3254
3255 let dense_expr = runtime.compile("dense_rank(&s, @)").unwrap();
3256 let dense_result = dense_expr.search(&data).unwrap();
3257 assert_eq!(dense_result, json!([1, 2, 2, 3]));
3259 }
3260
3261 #[test]
3266 fn test_pivot_basic() {
3267 let runtime = setup_runtime();
3268 let data = json!([
3269 {"name": "Alice", "score": 90},
3270 {"name": "Bob", "score": 85}
3271 ]);
3272 let expr = runtime.compile("pivot(@, &name, &score)").unwrap();
3273 let result = expr.search(&data).unwrap();
3274 assert_eq!(result, json!({"Alice": 90, "Bob": 85}));
3275 }
3276
3277 #[test]
3278 fn test_pivot_empty_array() {
3279 let runtime = setup_runtime();
3280 let data = json!([]);
3281 let expr = runtime.compile("pivot(@, &name, &score)").unwrap();
3282 let result = expr.search(&data).unwrap();
3283 assert_eq!(result, json!({}));
3284 }
3285
3286 #[test]
3287 fn test_pivot_numeric_keys() {
3288 let runtime = setup_runtime();
3289 let data = json!([
3290 {"id": 1, "value": "one"},
3291 {"id": 2, "value": "two"}
3292 ]);
3293 let expr = runtime.compile("pivot(@, &id, &value)").unwrap();
3294 let result = expr.search(&data).unwrap();
3295 assert_eq!(result, json!({"1": "one", "2": "two"}));
3296 }
3297
3298 #[test]
3299 fn test_pivot_duplicate_keys_last_wins() {
3300 let runtime = setup_runtime();
3301 let data = json!([
3302 {"name": "a", "v": 1},
3303 {"name": "a", "v": 2}
3304 ]);
3305 let expr = runtime.compile("pivot(@, &name, &v)").unwrap();
3306 let result = expr.search(&data).unwrap();
3307 assert_eq!(result, json!({"a": 2}));
3308 }
3309
3310 #[test]
3315 fn test_unpivot_basic() {
3316 let runtime = setup_runtime();
3317 let data = json!({"Alice": 90, "Bob": 85});
3318 let expr = runtime.compile("unpivot(@)").unwrap();
3319 let result = expr.search(&data).unwrap();
3320 let arr = result.as_array().unwrap();
3321 assert_eq!(arr.len(), 2);
3322 let has_alice = arr
3324 .iter()
3325 .any(|v| v == &json!({"key": "Alice", "value": 90}));
3326 let has_bob = arr.iter().any(|v| v == &json!({"key": "Bob", "value": 85}));
3327 assert!(has_alice);
3328 assert!(has_bob);
3329 }
3330
3331 #[test]
3332 fn test_unpivot_empty_object() {
3333 let runtime = setup_runtime();
3334 let data = json!({});
3335 let expr = runtime.compile("unpivot(@)").unwrap();
3336 let result = expr.search(&data).unwrap();
3337 assert_eq!(result, json!([]));
3338 }
3339
3340 #[test]
3341 fn test_pivot_unpivot_roundtrip() {
3342 let runtime = setup_runtime();
3343 let data = json!([
3344 {"name": "x", "val": 10},
3345 {"name": "y", "val": 20}
3346 ]);
3347 let expr = runtime.compile("pivot(@, &name, &val)").unwrap();
3349 let pivoted = expr.search(&data).unwrap();
3350 assert_eq!(pivoted, json!({"x": 10, "y": 20}));
3351
3352 let expr2 = runtime.compile("unpivot(@)").unwrap();
3354 let unpivoted = expr2.search(&pivoted).unwrap();
3355 let arr = unpivoted.as_array().unwrap();
3356 assert_eq!(arr.len(), 2);
3357 let has_x = arr.iter().any(|v| v == &json!({"key": "x", "value": 10}));
3358 let has_y = arr.iter().any(|v| v == &json!({"key": "y", "value": 20}));
3359 assert!(has_x);
3360 assert!(has_y);
3361 }
3362}