1use super::value_compare::partial_compare_values;
7use reddb_types::Value;
8use std::fmt;
9
10#[derive(Debug, Clone, PartialEq)]
12pub enum FilterOp {
13 Eq,
15 Ne,
17 Lt,
19 Le,
21 Gt,
23 Ge,
25 Between,
27 In,
29 NotIn,
31 Like,
33 NotLike,
35 IsNull,
37 IsNotNull,
39 Contains,
41 StartsWith,
43 EndsWith,
45}
46
47impl fmt::Display for FilterOp {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 FilterOp::Eq => write!(f, "="),
51 FilterOp::Ne => write!(f, "!="),
52 FilterOp::Lt => write!(f, "<"),
53 FilterOp::Le => write!(f, "<="),
54 FilterOp::Gt => write!(f, ">"),
55 FilterOp::Ge => write!(f, ">="),
56 FilterOp::Between => write!(f, "BETWEEN"),
57 FilterOp::In => write!(f, "IN"),
58 FilterOp::NotIn => write!(f, "NOT IN"),
59 FilterOp::Like => write!(f, "LIKE"),
60 FilterOp::NotLike => write!(f, "NOT LIKE"),
61 FilterOp::IsNull => write!(f, "IS NULL"),
62 FilterOp::IsNotNull => write!(f, "IS NOT NULL"),
63 FilterOp::Contains => write!(f, "CONTAINS"),
64 FilterOp::StartsWith => write!(f, "STARTS WITH"),
65 FilterOp::EndsWith => write!(f, "ENDS WITH"),
66 }
67 }
68}
69
70#[derive(Debug, Clone)]
72pub struct Predicate {
73 pub column: String,
75 pub op: FilterOp,
77 pub value: PredicateValue,
79}
80
81#[derive(Debug, Clone)]
83pub enum PredicateValue {
84 Single(Value),
86 Range(Value, Value),
88 List(Vec<Value>),
90 Pattern(String),
92 None,
94}
95
96impl Predicate {
97 pub fn eq(column: impl Into<String>, value: Value) -> Self {
99 Self {
100 column: column.into(),
101 op: FilterOp::Eq,
102 value: PredicateValue::Single(value),
103 }
104 }
105
106 pub fn ne(column: impl Into<String>, value: Value) -> Self {
108 Self {
109 column: column.into(),
110 op: FilterOp::Ne,
111 value: PredicateValue::Single(value),
112 }
113 }
114
115 pub fn lt(column: impl Into<String>, value: Value) -> Self {
117 Self {
118 column: column.into(),
119 op: FilterOp::Lt,
120 value: PredicateValue::Single(value),
121 }
122 }
123
124 pub fn le(column: impl Into<String>, value: Value) -> Self {
126 Self {
127 column: column.into(),
128 op: FilterOp::Le,
129 value: PredicateValue::Single(value),
130 }
131 }
132
133 pub fn gt(column: impl Into<String>, value: Value) -> Self {
135 Self {
136 column: column.into(),
137 op: FilterOp::Gt,
138 value: PredicateValue::Single(value),
139 }
140 }
141
142 pub fn ge(column: impl Into<String>, value: Value) -> Self {
144 Self {
145 column: column.into(),
146 op: FilterOp::Ge,
147 value: PredicateValue::Single(value),
148 }
149 }
150
151 pub fn between(column: impl Into<String>, low: Value, high: Value) -> Self {
153 Self {
154 column: column.into(),
155 op: FilterOp::Between,
156 value: PredicateValue::Range(low, high),
157 }
158 }
159
160 pub fn in_list(column: impl Into<String>, values: Vec<Value>) -> Self {
162 Self {
163 column: column.into(),
164 op: FilterOp::In,
165 value: PredicateValue::List(values),
166 }
167 }
168
169 pub fn not_in(column: impl Into<String>, values: Vec<Value>) -> Self {
171 Self {
172 column: column.into(),
173 op: FilterOp::NotIn,
174 value: PredicateValue::List(values),
175 }
176 }
177
178 pub fn like(column: impl Into<String>, pattern: impl Into<String>) -> Self {
180 Self {
181 column: column.into(),
182 op: FilterOp::Like,
183 value: PredicateValue::Pattern(pattern.into()),
184 }
185 }
186
187 pub fn not_like(column: impl Into<String>, pattern: impl Into<String>) -> Self {
189 Self {
190 column: column.into(),
191 op: FilterOp::NotLike,
192 value: PredicateValue::Pattern(pattern.into()),
193 }
194 }
195
196 pub fn is_null(column: impl Into<String>) -> Self {
198 Self {
199 column: column.into(),
200 op: FilterOp::IsNull,
201 value: PredicateValue::None,
202 }
203 }
204
205 pub fn is_not_null(column: impl Into<String>) -> Self {
207 Self {
208 column: column.into(),
209 op: FilterOp::IsNotNull,
210 value: PredicateValue::None,
211 }
212 }
213
214 pub fn contains(column: impl Into<String>, value: Value) -> Self {
216 Self {
217 column: column.into(),
218 op: FilterOp::Contains,
219 value: PredicateValue::Single(value),
220 }
221 }
222
223 pub fn starts_with(column: impl Into<String>, prefix: impl Into<String>) -> Self {
225 Self {
226 column: column.into(),
227 op: FilterOp::StartsWith,
228 value: PredicateValue::Pattern(prefix.into()),
229 }
230 }
231
232 pub fn ends_with(column: impl Into<String>, suffix: impl Into<String>) -> Self {
234 Self {
235 column: column.into(),
236 op: FilterOp::EndsWith,
237 value: PredicateValue::Pattern(suffix.into()),
238 }
239 }
240
241 pub fn evaluate(&self, column_value: &Value) -> bool {
243 match (&self.op, &self.value) {
244 (FilterOp::Eq, PredicateValue::Single(v)) => column_value == v,
245 (FilterOp::Ne, PredicateValue::Single(v)) => column_value != v,
246 (FilterOp::Lt, PredicateValue::Single(v)) => {
247 partial_compare_values(column_value, v) == Some(std::cmp::Ordering::Less)
248 }
249 (FilterOp::Le, PredicateValue::Single(v)) => {
250 matches!(
251 partial_compare_values(column_value, v),
252 Some(std::cmp::Ordering::Less | std::cmp::Ordering::Equal)
253 )
254 }
255 (FilterOp::Gt, PredicateValue::Single(v)) => {
256 partial_compare_values(column_value, v) == Some(std::cmp::Ordering::Greater)
257 }
258 (FilterOp::Ge, PredicateValue::Single(v)) => {
259 matches!(
260 partial_compare_values(column_value, v),
261 Some(std::cmp::Ordering::Greater | std::cmp::Ordering::Equal)
262 )
263 }
264 (FilterOp::Between, PredicateValue::Range(low, high)) => {
265 matches!(
266 partial_compare_values(column_value, low),
267 Some(std::cmp::Ordering::Greater | std::cmp::Ordering::Equal)
268 ) && matches!(
269 partial_compare_values(column_value, high),
270 Some(std::cmp::Ordering::Less | std::cmp::Ordering::Equal)
271 )
272 }
273 (FilterOp::In, PredicateValue::List(values)) => values.contains(column_value),
274 (FilterOp::NotIn, PredicateValue::List(values)) => !values.contains(column_value),
275 (FilterOp::Like, PredicateValue::Pattern(pattern)) => match column_value {
276 Value::Text(s) => match_like_pattern(s, pattern),
277 _ => false,
278 },
279 (FilterOp::NotLike, PredicateValue::Pattern(pattern)) => match column_value {
280 Value::Text(s) => !match_like_pattern(s, pattern),
281 _ => true,
282 },
283 (FilterOp::IsNull, PredicateValue::None) => matches!(column_value, Value::Null),
284 (FilterOp::IsNotNull, PredicateValue::None) => !matches!(column_value, Value::Null),
285 (FilterOp::Contains, PredicateValue::Single(v)) => match (column_value, v) {
286 (Value::Text(haystack), Value::Text(needle)) => haystack.contains(needle.as_ref()),
287 _ => false,
288 },
289 (FilterOp::StartsWith, PredicateValue::Pattern(prefix)) => match column_value {
290 Value::Text(s) => s.starts_with(prefix),
291 _ => false,
292 },
293 (FilterOp::EndsWith, PredicateValue::Pattern(suffix)) => match column_value {
294 Value::Text(s) => s.ends_with(suffix),
295 _ => false,
296 },
297 _ => false,
298 }
299 }
300}
301
302fn match_like_pattern(text: &str, pattern: &str) -> bool {
307 let mut text_chars = text.chars().peekable();
308 let mut pattern_chars = pattern.chars().peekable();
309
310 match_like_recursive(&mut text_chars, &mut pattern_chars)
311}
312
313fn match_like_recursive<I: Iterator<Item = char> + Clone>(
314 text: &mut std::iter::Peekable<I>,
315 pattern: &mut std::iter::Peekable<I>,
316) -> bool {
317 loop {
318 match (pattern.peek().cloned(), text.peek().cloned()) {
319 (None, None) => return true,
321 (None, Some(_)) => return false,
322
323 (Some('%'), _) => {
325 pattern.next();
326 while pattern.peek() == Some(&'%') {
328 pattern.next();
329 }
330 if pattern.peek().is_none() {
332 return true;
333 }
334 loop {
336 let mut text_clone = text.clone();
337 let mut pattern_clone = pattern.clone();
338 if match_like_recursive(&mut text_clone, &mut pattern_clone) {
339 return true;
340 }
341 if text.next().is_none() {
342 return false;
343 }
344 }
345 }
346
347 (Some('_'), Some(_)) => {
349 pattern.next();
350 text.next();
351 }
352 (Some('_'), None) => return false,
353
354 (Some(p), Some(t)) => {
356 if p.to_lowercase().next() == t.to_lowercase().next() {
357 pattern.next();
358 text.next();
359 } else {
360 return false;
361 }
362 }
363
364 (Some(_), None) => return false,
366 }
367 }
368}
369
370#[derive(Debug, Clone)]
372pub enum Filter {
373 Predicate(Predicate),
375 And(Vec<Filter>),
377 Or(Vec<Filter>),
379 Not(Box<Filter>),
381}
382
383impl Filter {
384 pub fn from_predicate(predicate: Predicate) -> Self {
386 Filter::Predicate(predicate)
387 }
388
389 pub fn eq(column: impl Into<String>, value: Value) -> Self {
391 Filter::Predicate(Predicate::eq(column, value))
392 }
393
394 pub fn ne(column: impl Into<String>, value: Value) -> Self {
396 Filter::Predicate(Predicate::ne(column, value))
397 }
398
399 pub fn lt(column: impl Into<String>, value: Value) -> Self {
401 Filter::Predicate(Predicate::lt(column, value))
402 }
403
404 pub fn le(column: impl Into<String>, value: Value) -> Self {
406 Filter::Predicate(Predicate::le(column, value))
407 }
408
409 pub fn gt(column: impl Into<String>, value: Value) -> Self {
411 Filter::Predicate(Predicate::gt(column, value))
412 }
413
414 pub fn ge(column: impl Into<String>, value: Value) -> Self {
416 Filter::Predicate(Predicate::ge(column, value))
417 }
418
419 pub fn between(column: impl Into<String>, low: Value, high: Value) -> Self {
421 Filter::Predicate(Predicate::between(column, low, high))
422 }
423
424 pub fn in_list(column: impl Into<String>, values: Vec<Value>) -> Self {
426 Filter::Predicate(Predicate::in_list(column, values))
427 }
428
429 pub fn like(column: impl Into<String>, pattern: impl Into<String>) -> Self {
431 Filter::Predicate(Predicate::like(column, pattern))
432 }
433
434 pub fn is_null(column: impl Into<String>) -> Self {
436 Filter::Predicate(Predicate::is_null(column))
437 }
438
439 pub fn is_not_null(column: impl Into<String>) -> Self {
441 Filter::Predicate(Predicate::is_not_null(column))
442 }
443
444 pub fn and(filters: Vec<Filter>) -> Self {
446 Filter::And(filters)
447 }
448
449 pub fn or(filters: Vec<Filter>) -> Self {
451 Filter::Or(filters)
452 }
453
454 pub fn not(filter: Filter) -> Self {
456 Filter::Not(Box::new(filter))
457 }
458
459 pub fn and_filter(self, other: Filter) -> Self {
461 match self {
462 Filter::And(mut filters) => {
463 filters.push(other);
464 Filter::And(filters)
465 }
466 _ => Filter::And(vec![self, other]),
467 }
468 }
469
470 pub fn or_filter(self, other: Filter) -> Self {
472 match self {
473 Filter::Or(mut filters) => {
474 filters.push(other);
475 Filter::Or(filters)
476 }
477 _ => Filter::Or(vec![self, other]),
478 }
479 }
480
481 pub fn evaluate(&self, get_value: &impl Fn(&str) -> Option<Value>) -> bool {
483 match self {
484 Filter::Predicate(pred) => {
485 if let Some(value) = get_value(&pred.column) {
486 pred.evaluate(&value)
487 } else {
488 matches!(pred.op, FilterOp::IsNull)
490 }
491 }
492 Filter::And(filters) => filters.iter().all(|f| f.evaluate(get_value)),
493 Filter::Or(filters) => filters.iter().any(|f| f.evaluate(get_value)),
494 Filter::Not(filter) => !filter.evaluate(get_value),
495 }
496 }
497
498 pub fn references_column(&self, column: &str) -> bool {
500 match self {
501 Filter::Predicate(pred) => pred.column == column,
502 Filter::And(filters) | Filter::Or(filters) => {
503 filters.iter().any(|f| f.references_column(column))
504 }
505 Filter::Not(filter) => filter.references_column(column),
506 }
507 }
508
509 pub fn referenced_columns(&self) -> Vec<&str> {
511 let mut columns = Vec::new();
512 self.collect_columns(&mut columns);
513 columns
514 }
515
516 fn collect_columns<'a>(&'a self, columns: &mut Vec<&'a str>) {
517 match self {
518 Filter::Predicate(pred) => {
519 if !columns.contains(&pred.column.as_str()) {
520 columns.push(&pred.column);
521 }
522 }
523 Filter::And(filters) | Filter::Or(filters) => {
524 for f in filters {
525 f.collect_columns(columns);
526 }
527 }
528 Filter::Not(filter) => filter.collect_columns(columns),
529 }
530 }
531}
532
533#[cfg(test)]
534mod tests {
535 use super::*;
536
537 fn make_row() -> impl Fn(&str) -> Option<Value> {
538 |col: &str| -> Option<Value> {
539 match col {
540 "name" => Some(Value::text("Alice".to_string())),
541 "age" => Some(Value::Integer(25)),
542 "score" => Some(Value::Float(95.5)),
543 "active" => Some(Value::Boolean(true)),
544 "email" => Some(Value::text("alice@example.com".to_string())),
545 "nullable" => Some(Value::Null),
546 _ => None,
547 }
548 }
549 }
550
551 #[test]
552 fn test_predicate_eq() {
553 let pred = Predicate::eq("name", Value::text("Alice".to_string()));
554 assert!(pred.evaluate(&Value::text("Alice".to_string())));
555 assert!(!pred.evaluate(&Value::text("Bob".to_string())));
556 }
557
558 #[test]
559 fn test_predicate_ne() {
560 let pred = Predicate::ne("name", Value::text("Bob".to_string()));
561 assert!(pred.evaluate(&Value::text("Alice".to_string())));
562 assert!(!pred.evaluate(&Value::text("Bob".to_string())));
563 }
564
565 #[test]
566 fn test_predicate_lt() {
567 let pred = Predicate::lt("age", Value::Integer(30));
568 assert!(pred.evaluate(&Value::Integer(25)));
569 assert!(!pred.evaluate(&Value::Integer(30)));
570 assert!(!pred.evaluate(&Value::Integer(35)));
571 }
572
573 #[test]
574 fn test_predicate_le() {
575 let pred = Predicate::le("age", Value::Integer(30));
576 assert!(pred.evaluate(&Value::Integer(25)));
577 assert!(pred.evaluate(&Value::Integer(30)));
578 assert!(!pred.evaluate(&Value::Integer(35)));
579 }
580
581 #[test]
582 fn test_predicate_gt() {
583 let pred = Predicate::gt("age", Value::Integer(20));
584 assert!(pred.evaluate(&Value::Integer(25)));
585 assert!(!pred.evaluate(&Value::Integer(20)));
586 assert!(!pred.evaluate(&Value::Integer(15)));
587 }
588
589 #[test]
590 fn test_predicate_ge() {
591 let pred = Predicate::ge("age", Value::Integer(20));
592 assert!(pred.evaluate(&Value::Integer(25)));
593 assert!(pred.evaluate(&Value::Integer(20)));
594 assert!(!pred.evaluate(&Value::Integer(15)));
595 }
596
597 #[test]
598 fn test_predicate_between() {
599 let pred = Predicate::between("age", Value::Integer(20), Value::Integer(30));
600 assert!(pred.evaluate(&Value::Integer(25)));
601 assert!(pred.evaluate(&Value::Integer(20)));
602 assert!(pred.evaluate(&Value::Integer(30)));
603 assert!(!pred.evaluate(&Value::Integer(19)));
604 assert!(!pred.evaluate(&Value::Integer(31)));
605 }
606
607 #[test]
608 fn test_predicate_in() {
609 let pred = Predicate::in_list(
610 "name",
611 vec![
612 Value::text("Alice".to_string()),
613 Value::text("Bob".to_string()),
614 ],
615 );
616 assert!(pred.evaluate(&Value::text("Alice".to_string())));
617 assert!(pred.evaluate(&Value::text("Bob".to_string())));
618 assert!(!pred.evaluate(&Value::text("Charlie".to_string())));
619 }
620
621 #[test]
622 fn test_predicate_not_in() {
623 let pred = Predicate::not_in("name", vec![Value::text("Alice".to_string())]);
624 assert!(!pred.evaluate(&Value::text("Alice".to_string())));
625 assert!(pred.evaluate(&Value::text("Bob".to_string())));
626 }
627
628 #[test]
629 fn test_predicate_like() {
630 let pred = Predicate::like("email", "%@example.com");
632 assert!(pred.evaluate(&Value::text("alice@example.com".to_string())));
633 assert!(pred.evaluate(&Value::text("bob@example.com".to_string())));
634 assert!(!pred.evaluate(&Value::text("alice@gmail.com".to_string())));
635
636 let pred2 = Predicate::like("name", "A_ice");
638 assert!(pred2.evaluate(&Value::text("Alice".to_string())));
639 assert!(!pred2.evaluate(&Value::text("Alicia".to_string())));
640
641 let pred3 = Predicate::like("email", "a%@%.com");
643 assert!(pred3.evaluate(&Value::text("alice@example.com".to_string())));
644 }
645
646 #[test]
647 fn test_predicate_is_null() {
648 let pred = Predicate::is_null("nullable");
649 assert!(pred.evaluate(&Value::Null));
650 assert!(!pred.evaluate(&Value::Integer(0)));
651 }
652
653 #[test]
654 fn test_predicate_is_not_null() {
655 let pred = Predicate::is_not_null("name");
656 assert!(pred.evaluate(&Value::text("Alice".to_string())));
657 assert!(!pred.evaluate(&Value::Null));
658 }
659
660 #[test]
661 fn test_predicate_contains() {
662 let pred = Predicate::contains("email", Value::text("@example".to_string()));
663 assert!(pred.evaluate(&Value::text("alice@example.com".to_string())));
664 assert!(!pred.evaluate(&Value::text("alice@gmail.com".to_string())));
665 }
666
667 #[test]
668 fn test_predicate_starts_with() {
669 let pred = Predicate::starts_with("name", "Al");
670 assert!(pred.evaluate(&Value::text("Alice".to_string())));
671 assert!(pred.evaluate(&Value::text("Albert".to_string())));
672 assert!(!pred.evaluate(&Value::text("Bob".to_string())));
673 }
674
675 #[test]
676 fn test_predicate_ends_with() {
677 let pred = Predicate::ends_with("email", ".com");
678 assert!(pred.evaluate(&Value::text("alice@example.com".to_string())));
679 assert!(!pred.evaluate(&Value::text("alice@example.org".to_string())));
680 }
681
682 #[test]
683 fn test_filter_evaluate() {
684 let row = make_row();
685
686 let filter = Filter::eq("name", Value::text("Alice".to_string()));
688 assert!(filter.evaluate(&row));
689
690 let filter = Filter::gt("age", Value::Integer(20));
692 assert!(filter.evaluate(&row));
693 }
694
695 #[test]
696 fn test_filter_and() {
697 let row = make_row();
698
699 let filter = Filter::and(vec![
700 Filter::eq("name", Value::text("Alice".to_string())),
701 Filter::gt("age", Value::Integer(20)),
702 ]);
703 assert!(filter.evaluate(&row));
704
705 let filter = Filter::and(vec![
706 Filter::eq("name", Value::text("Alice".to_string())),
707 Filter::gt("age", Value::Integer(30)), ]);
709 assert!(!filter.evaluate(&row));
710 }
711
712 #[test]
713 fn test_filter_or() {
714 let row = make_row();
715
716 let filter = Filter::or(vec![
717 Filter::eq("name", Value::text("Bob".to_string())), Filter::gt("age", Value::Integer(20)), ]);
720 assert!(filter.evaluate(&row));
721
722 let filter = Filter::or(vec![
723 Filter::eq("name", Value::text("Bob".to_string())),
724 Filter::lt("age", Value::Integer(20)),
725 ]);
726 assert!(!filter.evaluate(&row));
727 }
728
729 #[test]
730 fn test_filter_not() {
731 let row = make_row();
732
733 let filter = Filter::not(Filter::eq("name", Value::text("Bob".to_string())));
734 assert!(filter.evaluate(&row)); let filter = Filter::not(Filter::eq("name", Value::text("Alice".to_string())));
737 assert!(!filter.evaluate(&row));
738 }
739
740 #[test]
741 fn test_filter_chain() {
742 let row = make_row();
743
744 let filter = Filter::eq("name", Value::text("Alice".to_string()))
745 .and_filter(Filter::gt("age", Value::Integer(20)))
746 .and_filter(Filter::lt("age", Value::Integer(30)));
747
748 assert!(filter.evaluate(&row));
749 }
750
751 #[test]
752 fn test_filter_referenced_columns() {
753 let filter = Filter::and(vec![
754 Filter::eq("name", Value::text("Alice".to_string())),
755 Filter::or(vec![
756 Filter::gt("age", Value::Integer(20)),
757 Filter::lt("score", Value::Float(100.0)),
758 ]),
759 ]);
760
761 let columns = filter.referenced_columns();
762 assert!(columns.contains(&"name"));
763 assert!(columns.contains(&"age"));
764 assert!(columns.contains(&"score"));
765 assert_eq!(columns.len(), 3);
766 }
767
768 #[test]
769 fn test_cross_type_comparison() {
770 let pred = Predicate::lt("value", Value::Float(30.5));
772 assert!(pred.evaluate(&Value::Integer(30)));
773 assert!(!pred.evaluate(&Value::Integer(31)));
774 }
775
776 #[test]
777 fn test_like_edge_cases() {
778 let pred = Predicate::like("text", "");
780 assert!(pred.evaluate(&Value::text("".to_string())));
781 assert!(!pred.evaluate(&Value::text("a".to_string())));
782
783 let pred = Predicate::like("text", "%");
785 assert!(pred.evaluate(&Value::text("anything".to_string())));
786 assert!(pred.evaluate(&Value::text("".to_string())));
787
788 let pred = Predicate::like("text", "a%b%c");
790 assert!(pred.evaluate(&Value::text("abc".to_string())));
791 assert!(pred.evaluate(&Value::text("aXXbYYc".to_string())));
792 assert!(!pred.evaluate(&Value::text("ab".to_string())));
793 }
794}