agdb/query/
query_condition.rs

1use crate::DbValue;
2use crate::QueryId;
3use crate::graph_search::SearchControl;
4
5/// Logical operator for query conditions
6#[derive(Clone, Copy, Debug, PartialEq)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
9#[cfg_attr(feature = "derive", derive(agdb::DbSerialize))]
10#[cfg_attr(feature = "api", derive(agdb::ApiDef))]
11pub enum QueryConditionLogic {
12    /// Logical AND (&&)
13    And,
14
15    /// Logical Or (||)
16    Or,
17}
18
19/// Query condition modifier
20#[derive(Clone, Copy, Debug, PartialEq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
23#[cfg_attr(feature = "derive", derive(agdb::DbSerialize))]
24#[cfg_attr(feature = "api", derive(agdb::ApiDef))]
25pub enum QueryConditionModifier {
26    /// No modifier
27    None,
28
29    /// Continues the search beyond the current element
30    /// if the condition being modified passes.
31    Beyond,
32
33    /// Reversal of the result (equivalent to `!`).
34    Not,
35
36    /// Stops the search beyond the current element
37    /// if the condition being modified passes.
38    NotBeyond,
39}
40
41/// Query condition data
42#[derive(Debug, Clone, PartialEq)]
43#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
44#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
45#[cfg_attr(feature = "derive", derive(agdb::DbSerialize))]
46#[cfg_attr(feature = "api", derive(agdb::ApiDef))]
47pub enum QueryConditionData {
48    /// Distance from the search origin. Takes count comparison
49    /// (e.g. Equal, GreaterThan).
50    Distance(CountComparison),
51
52    /// Is the current element an edge? I.e. `id < 0`.
53    Edge,
54
55    /// Tests number of edges (from+to) of the current element.
56    /// Only nodes will pass. Self-referential edges are
57    /// counted twice. Takes count comparison
58    /// (e.g. Equal, GreaterThan).
59    EdgeCount(CountComparison),
60
61    /// Tests the number of outgoing edges (from) of the
62    /// current element. Takes count comparison
63    /// (e.g. Equal, GreaterThan).
64    EdgeCountFrom(CountComparison),
65
66    /// Tests the number of incoming edges (to) of the
67    /// current element. Takes count comparison
68    /// (e.g. Equal, GreaterThan).
69    EdgeCountTo(CountComparison),
70
71    /// Tests if the current id is in the list of ids.
72    Ids(Vec<QueryId>),
73
74    /// Tests if the current element has a property `key`
75    /// with a value that evaluates true against `comparison`.
76    KeyValue(KeyValueComparison),
77
78    /// Test if the current element has **all** of the keys listed.
79    Keys(Vec<DbValue>),
80
81    /// Is the current element a node? I.e. `0 < id`.
82    Node,
83
84    /// Nested list of conditions (equivalent to brackets).
85    Where(Vec<QueryCondition>),
86}
87
88/// Query condition. The condition consists of
89/// `data`, logic operator and a modifier.
90#[derive(Debug, Clone, PartialEq)]
91#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
92#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
93#[cfg_attr(feature = "derive", derive(agdb::DbSerialize))]
94#[cfg_attr(feature = "api", derive(agdb::ApiDef))]
95pub struct QueryCondition {
96    /// Logic operator (e.g. And, Or)
97    pub logic: QueryConditionLogic,
98
99    /// Condition modifier (e.g. None, Beyond, Not, NotBeyond)
100    pub modifier: QueryConditionModifier,
101
102    /// Condition data (or type) defining what type
103    /// of validation is to be performed.
104    #[cfg_attr(feature = "openapi", schema(no_recursion))]
105    pub data: QueryConditionData,
106}
107
108/// Comparison of unsigned integers (`u64`) used
109/// by `distance()` and `edge_count*()` conditions. Supports
110/// the usual set of named comparisons: `==, !=, <, <=, >, =>`.
111#[derive(Debug, Clone, PartialEq)]
112#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
113#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
114#[cfg_attr(feature = "derive", derive(agdb::DbSerialize))]
115#[cfg_attr(feature = "api", derive(agdb::ApiDef))]
116pub enum CountComparison {
117    /// property == this
118    Equal(u64),
119
120    /// property > this
121    GreaterThan(u64),
122
123    /// property >= this
124    GreaterThanOrEqual(u64),
125
126    /// property < this
127    LessThan(u64),
128
129    /// property <= this
130    LessThanOrEqual(u64),
131
132    /// property != this
133    NotEqual(u64),
134}
135
136/// Comparison of database values ([`DbValue`]) used
137/// by `key()` condition. Supports
138/// the usual set of named comparisons: `==, !=, <, <=, >, =>`
139/// plus `contains()`. The comparisons are type
140/// strict except for the `contains` comparison
141/// which allows vectorized version of the base type. Notably
142/// however it does not support the `bytes` and integral types
143/// where the "contains" makes little sense (i.e. does 3 contain 1?).
144#[derive(Debug, Clone, PartialEq)]
145#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
146#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
147#[cfg_attr(feature = "derive", derive(agdb::DbSerialize))]
148#[cfg_attr(feature = "api", derive(agdb::ApiDef))]
149pub enum Comparison {
150    /// property == this
151    Equal(DbValue),
152
153    /// property > this
154    GreaterThan(DbValue),
155
156    /// property >= this
157    GreaterThanOrEqual(DbValue),
158
159    /// property < this
160    LessThan(DbValue),
161
162    /// property <= this
163    LessThanOrEqual(DbValue),
164
165    /// property != this
166    NotEqual(DbValue),
167
168    /// property.contains(this)
169    Contains(DbValue),
170
171    /// property.starts_with(this)
172    StartsWith(DbValue),
173
174    /// property.ends_with(this)
175    EndsWith(DbValue),
176}
177
178/// Comparison of a value stored under specific `key` to
179/// a value using the comparison operator.
180#[derive(Debug, Clone, PartialEq)]
181#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
182#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
183#[cfg_attr(feature = "derive", derive(agdb::DbSerialize))]
184#[cfg_attr(feature = "api", derive(agdb::ApiDef))]
185pub struct KeyValueComparison {
186    /// Property key
187    pub key: DbValue,
188
189    /// Comparison operator (e.g. Equal, GreaterThan etc.)
190    pub value: Comparison,
191}
192
193impl CountComparison {
194    pub(crate) fn compare_distance(&self, right: u64) -> SearchControl {
195        match self {
196            CountComparison::Equal(left) => match right.cmp(left) {
197                std::cmp::Ordering::Less => SearchControl::Continue(false),
198                std::cmp::Ordering::Equal => SearchControl::Stop(true),
199                std::cmp::Ordering::Greater => SearchControl::Stop(false),
200            },
201            CountComparison::GreaterThan(left) => match right.cmp(left) {
202                std::cmp::Ordering::Less | std::cmp::Ordering::Equal => {
203                    SearchControl::Continue(false)
204                }
205                std::cmp::Ordering::Greater => SearchControl::Continue(true),
206            },
207            CountComparison::GreaterThanOrEqual(left) => match right.cmp(left) {
208                std::cmp::Ordering::Less => SearchControl::Continue(false),
209                std::cmp::Ordering::Greater | std::cmp::Ordering::Equal => {
210                    SearchControl::Continue(true)
211                }
212            },
213            CountComparison::LessThan(left) => match right.cmp(left) {
214                std::cmp::Ordering::Less => SearchControl::Continue(true),
215                std::cmp::Ordering::Greater | std::cmp::Ordering::Equal => {
216                    SearchControl::Stop(false)
217                }
218            },
219            CountComparison::LessThanOrEqual(left) => match right.cmp(left) {
220                std::cmp::Ordering::Less | std::cmp::Ordering::Equal => {
221                    SearchControl::Continue(true)
222                }
223                std::cmp::Ordering::Greater => SearchControl::Stop(false),
224            },
225            CountComparison::NotEqual(left) => match right.cmp(left) {
226                std::cmp::Ordering::Less | std::cmp::Ordering::Greater => {
227                    SearchControl::Continue(true)
228                }
229                std::cmp::Ordering::Equal => SearchControl::Continue(false),
230            },
231        }
232    }
233
234    pub(crate) fn compare(&self, left: u64) -> bool {
235        match self {
236            CountComparison::Equal(right) => left == *right,
237            CountComparison::GreaterThan(right) => left > *right,
238            CountComparison::GreaterThanOrEqual(right) => left >= *right,
239            CountComparison::LessThan(right) => left < *right,
240            CountComparison::LessThanOrEqual(right) => left <= *right,
241            CountComparison::NotEqual(right) => left != *right,
242        }
243    }
244}
245
246impl Comparison {
247    pub(crate) fn compare(&self, left: &DbValue) -> bool {
248        match self {
249            Comparison::Equal(right) => left == right,
250            Comparison::GreaterThan(right) => left > right,
251            Comparison::GreaterThanOrEqual(right) => left >= right,
252            Comparison::LessThan(right) => left < right,
253            Comparison::LessThanOrEqual(right) => left <= right,
254            Comparison::NotEqual(right) => left != right,
255
256            Comparison::Contains(right) => match (left, right) {
257                (DbValue::String(left), DbValue::String(right)) => left.contains(right),
258                (DbValue::String(left), DbValue::VecString(right)) => {
259                    right.iter().all(|x| left.contains(x))
260                }
261                (DbValue::VecI64(left), DbValue::I64(right)) => left.contains(right),
262                (DbValue::VecI64(left), DbValue::VecI64(right)) => {
263                    right.iter().all(|x| left.contains(x))
264                }
265                (DbValue::VecU64(left), DbValue::U64(right)) => left.contains(right),
266                (DbValue::VecU64(left), DbValue::VecU64(right)) => {
267                    right.iter().all(|x| left.contains(x))
268                }
269                (DbValue::VecF64(left), DbValue::F64(right)) => left.contains(right),
270                (DbValue::VecF64(left), DbValue::VecF64(right)) => {
271                    right.iter().all(|x| left.contains(x))
272                }
273                (DbValue::VecString(left), DbValue::String(right)) => left.contains(right),
274                (DbValue::VecString(left), DbValue::VecString(right)) => {
275                    right.iter().all(|x| left.contains(x))
276                }
277                _ => false,
278            },
279
280            Comparison::StartsWith(right) => match (left, right) {
281                (DbValue::String(left), DbValue::String(right)) => left.starts_with(right),
282                (DbValue::String(left), DbValue::VecString(right)) => {
283                    left.starts_with(&right.concat())
284                }
285                (DbValue::VecI64(left), DbValue::I64(right)) => left.starts_with(&[*right]),
286                (DbValue::VecI64(left), DbValue::VecI64(right)) => left.starts_with(right),
287                (DbValue::VecU64(left), DbValue::U64(right)) => left.starts_with(&[*right]),
288                (DbValue::VecU64(left), DbValue::VecU64(right)) => left.starts_with(right),
289                (DbValue::VecF64(left), DbValue::F64(right)) => left.starts_with(&[*right]),
290                (DbValue::VecF64(left), DbValue::VecF64(right)) => left.starts_with(right),
291                (DbValue::VecString(left), DbValue::String(right)) => left.first() == Some(right),
292                (DbValue::VecString(left), DbValue::VecString(right)) => left.starts_with(right),
293                _ => false,
294            },
295
296            Comparison::EndsWith(right) => match (left, right) {
297                (DbValue::String(left), DbValue::String(right)) => left.ends_with(right),
298                (DbValue::String(left), DbValue::VecString(right)) => {
299                    left.ends_with(&right.concat())
300                }
301                (DbValue::VecI64(left), DbValue::I64(right)) => left.ends_with(&[*right]),
302                (DbValue::VecI64(left), DbValue::VecI64(right)) => left.ends_with(right),
303                (DbValue::VecU64(left), DbValue::U64(right)) => left.ends_with(&[*right]),
304                (DbValue::VecU64(left), DbValue::VecU64(right)) => left.ends_with(right),
305                (DbValue::VecF64(left), DbValue::F64(right)) => left.ends_with(&[*right]),
306                (DbValue::VecF64(left), DbValue::VecF64(right)) => left.ends_with(right),
307                (DbValue::VecString(left), DbValue::String(right)) => left.last() == Some(right),
308                (DbValue::VecString(left), DbValue::VecString(right)) => left.ends_with(right),
309                _ => false,
310            },
311        }
312    }
313
314    pub(crate) fn value(&self) -> &DbValue {
315        match self {
316            Comparison::Equal(value)
317            | Comparison::GreaterThan(value)
318            | Comparison::GreaterThanOrEqual(value)
319            | Comparison::LessThan(value)
320            | Comparison::LessThanOrEqual(value)
321            | Comparison::NotEqual(value)
322            | Comparison::Contains(value)
323            | Comparison::StartsWith(value)
324            | Comparison::EndsWith(value) => value,
325        }
326    }
327}
328
329impl From<u64> for CountComparison {
330    fn from(value: u64) -> Self {
331        CountComparison::Equal(value)
332    }
333}
334
335impl<T: Into<DbValue>> From<T> for Comparison {
336    fn from(value: T) -> Self {
337        Comparison::Equal(value.into())
338    }
339}
340
341#[cfg(test)]
342mod tests {
343    use super::*;
344
345    #[test]
346    fn derived_from_debug() {
347        let _ = format!(
348            "{:?}",
349            QueryCondition {
350                logic: QueryConditionLogic::And,
351                modifier: QueryConditionModifier::None,
352                data: QueryConditionData::Edge,
353            }
354        );
355
356        let _ = format!("{:?}", Comparison::Equal(DbValue::I64(0)));
357
358        let _ = format!("{:?}", CountComparison::Equal(0));
359    }
360
361    #[test]
362    #[allow(clippy::redundant_clone)]
363    fn derived_from_clone() {
364        let left = QueryCondition {
365            logic: QueryConditionLogic::And,
366            modifier: QueryConditionModifier::None,
367            data: QueryConditionData::Edge,
368        };
369        let right = left.clone();
370        assert_eq!(left, right);
371
372        let left = Comparison::Equal(DbValue::I64(0));
373        let right = left.clone();
374        assert_eq!(left, right);
375
376        let left = CountComparison::Equal(0);
377        let right = left.clone();
378        assert_eq!(left, right);
379    }
380
381    #[test]
382    fn derived_from_partial_eq() {
383        assert_eq!(
384            QueryCondition {
385                logic: QueryConditionLogic::And,
386                modifier: QueryConditionModifier::None,
387                data: QueryConditionData::Edge,
388            },
389            QueryCondition {
390                logic: QueryConditionLogic::And,
391                modifier: QueryConditionModifier::None,
392                data: QueryConditionData::Edge,
393            }
394        );
395
396        assert_eq!(
397            Comparison::Equal(DbValue::I64(0)),
398            Comparison::Equal(DbValue::I64(0))
399        );
400
401        assert_eq!(CountComparison::Equal(0), CountComparison::Equal(0));
402    }
403
404    #[test]
405    fn count_comparison() {
406        use CountComparison::Equal;
407        use CountComparison::GreaterThan;
408        use CountComparison::GreaterThanOrEqual;
409        use CountComparison::LessThan;
410        use CountComparison::LessThanOrEqual;
411        use CountComparison::NotEqual;
412        use SearchControl::Continue;
413        use SearchControl::Stop;
414
415        assert_eq!(Equal(2).compare_distance(3), Stop(false));
416        assert_eq!(Equal(2).compare_distance(2), Stop(true));
417        assert_eq!(Equal(2).compare_distance(1), Continue(false));
418        assert_eq!(NotEqual(2).compare_distance(3), Continue(true));
419        assert_eq!(NotEqual(2).compare_distance(2), Continue(false));
420        assert_eq!(NotEqual(2).compare_distance(1), Continue(true));
421        assert_eq!(GreaterThan(2).compare_distance(3), Continue(true));
422        assert_eq!(GreaterThan(2).compare_distance(2), Continue(false));
423        assert_eq!(GreaterThan(2).compare_distance(1), Continue(false));
424        assert_eq!(GreaterThanOrEqual(2).compare_distance(3), Continue(true));
425        assert_eq!(GreaterThanOrEqual(2).compare_distance(2), Continue(true));
426        assert_eq!(GreaterThanOrEqual(2).compare_distance(1), Continue(false));
427        assert_eq!(LessThan(2).compare_distance(3), Stop(false));
428        assert_eq!(LessThan(2).compare_distance(2), Stop(false));
429        assert_eq!(LessThan(2).compare_distance(1), Continue(true));
430        assert_eq!(LessThanOrEqual(2).compare_distance(3), Stop(false));
431        assert_eq!(LessThanOrEqual(2).compare_distance(2), Continue(true));
432        assert_eq!(LessThanOrEqual(2).compare_distance(1), Continue(true));
433    }
434
435    #[test]
436    fn contains() {
437        let condition = Comparison::Contains("abc".into());
438        assert!(condition.compare(&"0abc123".into()));
439        assert!(!condition.compare(&"0bc123".into()));
440
441        let condition = Comparison::Contains(vec!["ab".to_string(), "23".to_string()].into());
442        assert!(condition.compare(&"0abc123".into()));
443        assert!(!condition.compare(&"0abc12".into()));
444
445        assert!(Comparison::Contains(1.into()).compare(&vec![2, 1, 3].into()));
446        assert!(!Comparison::Contains(4.into()).compare(&vec![2, 1, 3].into()));
447
448        let condition = Comparison::Contains(vec![2, 3].into());
449        assert!(condition.compare(&vec![2, 3].into()));
450        assert!(!condition.compare(&vec![1, 3].into()));
451
452        let condition = Comparison::Contains(1_u64.into());
453        assert!(condition.compare(&vec![2_u64, 1_u64, 3_u64].into()));
454        assert!(!condition.compare(&vec![2_u64, 3_u64].into()));
455
456        let condition = Comparison::Contains(vec![2_u64, 3_u64].into());
457        assert!(condition.compare(&vec![2_u64, 1_u64, 3_u64].into()));
458        assert!(!condition.compare(&vec![1_u64, 3_u64].into()));
459
460        let condition = Comparison::Contains(1.1.into());
461        assert!(condition.compare(&vec![2.1, 1.1, 3.3].into()));
462        assert!(!condition.compare(&vec![2.1, 3.3].into()));
463
464        let condition = Comparison::Contains(vec![2.2, 3.3].into());
465        assert!(condition.compare(&vec![2.2, 1.1, 3.3].into()));
466        assert!(!condition.compare(&vec![1.1, 3.3].into()));
467
468        let condition = Comparison::Contains("abc".into());
469        assert!(condition.compare(&vec!["abc".to_string(), "123".to_string()].into()));
470        assert!(!condition.compare(&vec!["0".to_string(), "123".to_string()].into()));
471
472        let condition = Comparison::Contains(vec!["abc".to_string(), "123".to_string()].into());
473        assert!(condition.compare(&vec!["abc".to_string(), "123".to_string()].into()));
474        assert!(!condition.compare(&vec!["123".to_string()].into()));
475
476        assert!(!Comparison::Contains("abc".into()).compare(&1.into()));
477    }
478
479    #[test]
480    fn value() {
481        assert_eq!(Comparison::Equal(DbValue::I64(0)).value(), &DbValue::I64(0));
482        assert_eq!(
483            Comparison::GreaterThan(DbValue::I64(0)).value(),
484            &DbValue::I64(0)
485        );
486        assert_eq!(
487            Comparison::GreaterThanOrEqual(DbValue::I64(0)).value(),
488            &DbValue::I64(0)
489        );
490        assert_eq!(
491            Comparison::LessThan(DbValue::I64(0)).value(),
492            &DbValue::I64(0)
493        );
494        assert_eq!(
495            Comparison::LessThanOrEqual(DbValue::I64(0)).value(),
496            &DbValue::I64(0)
497        );
498        assert_eq!(
499            Comparison::NotEqual(DbValue::I64(0)).value(),
500            &DbValue::I64(0)
501        );
502        assert_eq!(
503            Comparison::Contains(DbValue::I64(0)).value(),
504            &DbValue::I64(0)
505        );
506    }
507
508    #[test]
509    fn starts_with() {
510        let condition = Comparison::StartsWith("a".into());
511        assert!(condition.compare(&"abc".into()));
512        assert!(!condition.compare(&"bca".into()));
513
514        let condition = Comparison::StartsWith(vec!["ab".to_string(), "23".to_string()].into());
515        assert!(condition.compare(&"ab23".into()));
516        assert!(!condition.compare(&"ab2".into()));
517
518        assert!(Comparison::StartsWith(1.into()).compare(&vec![1, 2, 3].into()));
519        assert!(!Comparison::StartsWith(1.into()).compare(&vec![2, 1, 3].into()));
520
521        let condition = Comparison::StartsWith(vec![2, 3].into());
522        assert!(condition.compare(&vec![2, 3].into()));
523        assert!(!condition.compare(&vec![1, 2, 3].into()));
524
525        let condition = Comparison::StartsWith(1_u64.into());
526        assert!(condition.compare(&vec![1_u64, 2_u64, 3_u64].into()));
527        assert!(!condition.compare(&vec![2_u64, 1_u64].into()));
528
529        let condition = Comparison::StartsWith(vec![2_u64, 3_u64].into());
530        assert!(condition.compare(&vec![2_u64, 3_u64, 1_u64].into()));
531        assert!(!condition.compare(&vec![1_u64, 2_u64, 3_u64].into()));
532
533        let condition = Comparison::StartsWith(1.1.into());
534        assert!(condition.compare(&vec![1.1, 2.1, 3.3].into()));
535        assert!(!condition.compare(&vec![2.1, 3.3, 1.1].into()));
536
537        let condition = Comparison::StartsWith(vec![2.2, 3.3].into());
538        assert!(condition.compare(&vec![2.2, 3.3, 3.3].into()));
539        assert!(!condition.compare(&vec![1.1, 3.3, 2.2].into()));
540
541        let condition = Comparison::StartsWith("abc".into());
542        assert!(condition.compare(&vec!["abc".to_string(), "123".to_string()].into()));
543        assert!(!condition.compare(&vec!["0".to_string(), "abc".to_string()].into()));
544
545        let condition = Comparison::StartsWith(vec!["abc".to_string(), "123".to_string()].into());
546        assert!(condition.compare(&vec!["abc".to_string(), "123".to_string()].into()));
547        assert!(!condition.compare(&vec!["123".to_string(), "abc".to_string()].into()));
548
549        assert!(!Comparison::StartsWith("abc".into()).compare(&1.into()));
550    }
551
552    #[test]
553    fn ends_with() {
554        let condition = Comparison::EndsWith("a".into());
555        assert!(condition.compare(&"bca".into()));
556        assert!(!condition.compare(&"abc".into()));
557
558        let condition = Comparison::EndsWith(vec!["ab".to_string(), "23".to_string()].into());
559        assert!(condition.compare(&"ffeeggab23".into()));
560        assert!(!condition.compare(&"ab23ff".into()));
561
562        assert!(Comparison::EndsWith(1.into()).compare(&vec![1, 2, 1].into()));
563        assert!(!Comparison::EndsWith(1.into()).compare(&vec![1, 1, 3].into()));
564
565        let condition = Comparison::EndsWith(vec![2, 3].into());
566        assert!(condition.compare(&vec![4, 5, 2, 3].into()));
567        assert!(!condition.compare(&vec![1, 2, 3, 4, 5].into()));
568
569        let condition = Comparison::EndsWith(1_u64.into());
570        assert!(condition.compare(&vec![1_u64, 2_u64, 1_u64].into()));
571        assert!(!condition.compare(&vec![2_u64, 1_u64, 3_u64].into()));
572
573        let condition = Comparison::EndsWith(vec![2_u64, 3_u64].into());
574        assert!(condition.compare(&vec![1_u64, 2_u64, 3_u64].into()));
575        assert!(!condition.compare(&vec![2_u64, 3_u64, 1_u64].into()));
576
577        let condition = Comparison::EndsWith(1.1.into());
578        assert!(condition.compare(&vec![2.1, 3.3, 1.1].into()));
579        assert!(!condition.compare(&vec![2.1, 3.3, 1.1, 4.4].into()));
580
581        let condition = Comparison::EndsWith(vec![2.2, 3.3].into());
582        assert!(condition.compare(&vec![3.3, 4.4, 2.2, 3.3].into()));
583        assert!(!condition.compare(&vec![1.1, 3.3, 2.2].into()));
584
585        let condition = Comparison::EndsWith("abc".into());
586        assert!(condition.compare(&vec!["123".to_string(), "abc".to_string()].into()));
587        assert!(!condition.compare(&vec!["0".to_string(), "abcdef".to_string()].into()));
588
589        let condition = Comparison::EndsWith(vec!["abc".to_string(), "123".to_string()].into());
590        assert!(condition.compare(&vec!["abc".to_string(), "123".to_string()].into()));
591        assert!(!condition.compare(&vec!["123".to_string(), "abc".to_string()].into()));
592
593        assert!(!Comparison::EndsWith("abc".into()).compare(&1.into()));
594    }
595}