surrealpatch/lib/src/sql/value/compare.rs
2022-05-05 10:24:52 +01:00

195 lines
6.4 KiB
Rust

use crate::sql::part::Next;
use crate::sql::part::Part;
use crate::sql::value::Value;
use std::cmp::Ordering;
impl Value {
pub fn compare(
&self,
other: &Self,
path: &[Part],
collate: bool,
numeric: bool,
) -> Option<Ordering> {
match path.first() {
// Get the current path part
Some(p) => match (self, other) {
// Current path part is an object
(Value::Object(a), Value::Object(b)) => match p {
Part::Field(f) => match (a.get(f as &str), b.get(f as &str)) {
(Some(a), Some(b)) => a.compare(b, path.next(), collate, numeric),
(Some(_), None) => Some(Ordering::Greater),
(None, Some(_)) => Some(Ordering::Less),
(_, _) => Some(Ordering::Equal),
},
_ => None,
},
// Current path part is an array
(Value::Array(a), Value::Array(b)) => match p {
Part::All => {
for (a, b) in a.iter().zip(b.iter()) {
match a.compare(b, path.next(), collate, numeric) {
Some(Ordering::Equal) => continue,
None => continue,
o => return o,
}
}
match (a.len(), b.len()) {
(a, b) if a > b => Some(Ordering::Greater),
(a, b) if a < b => Some(Ordering::Less),
_ => Some(Ordering::Equal),
}
}
Part::First => match (a.first(), b.first()) {
(Some(a), Some(b)) => a.compare(b, path.next(), collate, numeric),
(Some(_), None) => Some(Ordering::Greater),
(None, Some(_)) => Some(Ordering::Less),
(_, _) => Some(Ordering::Equal),
},
Part::Last => match (a.first(), b.first()) {
(Some(a), Some(b)) => a.compare(b, path.next(), collate, numeric),
(Some(_), None) => Some(Ordering::Greater),
(None, Some(_)) => Some(Ordering::Less),
(_, _) => Some(Ordering::Equal),
},
Part::Index(i) => match (a.get(i.to_usize()), b.get(i.to_usize())) {
(Some(a), Some(b)) => a.compare(b, path.next(), collate, numeric),
(Some(_), None) => Some(Ordering::Greater),
(None, Some(_)) => Some(Ordering::Less),
(_, _) => Some(Ordering::Equal),
},
_ => {
for (a, b) in a.iter().zip(b.iter()) {
match a.compare(b, path, collate, numeric) {
Some(Ordering::Equal) => continue,
None => continue,
o => return o,
}
}
match (a.len(), b.len()) {
(a, b) if a > b => Some(Ordering::Greater),
(a, b) if a < b => Some(Ordering::Less),
_ => Some(Ordering::Equal),
}
}
},
// Ignore everything else
(a, b) => a.compare(b, path.next(), collate, numeric),
},
// No more parts so get the value
None => match (collate, numeric) {
(true, true) => self.natural_lexical_cmp(other),
(true, false) => self.lexical_cmp(other),
(false, true) => self.natural_cmp(other),
_ => self.partial_cmp(other),
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sql::idiom::Idiom;
use crate::sql::test::Parse;
#[test]
fn compare_none() {
let idi = Idiom::default();
let one = Value::parse("{ test: { other: null, something: 456 } }");
let two = Value::parse("{ test: { other: null, something: 123 } }");
let res = one.compare(&two, &idi, false, false);
assert_eq!(res, Some(Ordering::Greater));
}
#[test]
fn compare_basic() {
let idi = Idiom::parse("test.something");
let one = Value::parse("{ test: { other: null, something: 456 } }");
let two = Value::parse("{ test: { other: null, something: 123 } }");
let res = one.compare(&two, &idi, false, false);
assert_eq!(res, Some(Ordering::Greater));
}
#[test]
fn compare_basic_missing_left() {
let idi = Idiom::parse("test.something");
let one = Value::parse("{ test: { other: null } }");
let two = Value::parse("{ test: { other: null, something: 123 } }");
let res = one.compare(&two, &idi, false, false);
assert_eq!(res, Some(Ordering::Less));
}
#[test]
fn compare_basic_missing_right() {
let idi = Idiom::parse("test.something");
let one = Value::parse("{ test: { other: null, something: 456 } }");
let two = Value::parse("{ test: { other: null } }");
let res = one.compare(&two, &idi, false, false);
assert_eq!(res, Some(Ordering::Greater));
}
#[test]
fn compare_array() {
let idi = Idiom::parse("test.something.*");
let one = Value::parse("{ test: { other: null, something: [4, 5, 6] } }");
let two = Value::parse("{ test: { other: null, something: [1, 2, 3] } }");
let res = one.compare(&two, &idi, false, false);
assert_eq!(res, Some(Ordering::Greater));
}
#[test]
fn compare_array_longer_left() {
let idi = Idiom::parse("test.something.*");
let one = Value::parse("{ test: { other: null, something: [1, 2, 3, 4, 5, 6] } }");
let two = Value::parse("{ test: { other: null, something: [1, 2, 3] } }");
let res = one.compare(&two, &idi, false, false);
assert_eq!(res, Some(Ordering::Greater));
}
#[test]
fn compare_array_longer_right() {
let idi = Idiom::parse("test.something.*");
let one = Value::parse("{ test: { other: null, something: [1, 2, 3] } }");
let two = Value::parse("{ test: { other: null, something: [1, 2, 3, 4, 5, 6] } }");
let res = one.compare(&two, &idi, false, false);
assert_eq!(res, Some(Ordering::Less));
}
#[test]
fn compare_array_missing_left() {
let idi = Idiom::parse("test.something.*");
let one = Value::parse("{ test: { other: null, something: null } }");
let two = Value::parse("{ test: { other: null, something: [1, 2, 3] } }");
let res = one.compare(&two, &idi, false, false);
assert_eq!(res, Some(Ordering::Less));
}
#[test]
fn compare_array_missing_right() {
let idi = Idiom::parse("test.something.*");
let one = Value::parse("{ test: { other: null, something: [4, 5, 6] } }");
let two = Value::parse("{ test: { other: null, something: null } }");
let res = one.compare(&two, &idi, false, false);
assert_eq!(res, Some(Ordering::Greater));
}
#[test]
fn compare_array_missing_value_left() {
let idi = Idiom::parse("test.something.*");
let one = Value::parse("{ test: { other: null, something: [1, null, 3] } }");
let two = Value::parse("{ test: { other: null, something: [1, 2, 3] } }");
let res = one.compare(&two, &idi, false, false);
assert_eq!(res, Some(Ordering::Less));
}
#[test]
fn compare_array_missing_value_right() {
let idi = Idiom::parse("test.something.*");
let one = Value::parse("{ test: { other: null, something: [1, 2, 3] } }");
let two = Value::parse("{ test: { other: null, something: [1, null, 3] } }");
let res = one.compare(&two, &idi, false, false);
assert_eq!(res, Some(Ordering::Greater));
}
}