Skip to main content

linkrs_core/value/
value_compare.rs

1use crate::value::{
2    date_time::{DateTimeValue, DateValue, TimeValue},
3    geography::Geography,
4    interval::IntervalValue,
5    list::List,
6    null::NullType,
7    Value,
8};
9use std::{cmp::Ordering as CmpOrdering, collections::HashMap, hash::Hash};
10
11// Manual implementation of PartialEq to handle f64 comparisons correctly
12impl PartialEq for Value {
13    fn eq(&self, other: &Self) -> bool {
14        match (self, other) {
15            (Value::Empty, Value::Empty) => true,
16            (Value::Null(a), Value::Null(b)) => a == b,
17            (Value::Bool(a), Value::Bool(b)) => a == b,
18            (Value::SmallInt(a), Value::SmallInt(b)) => a == b,
19            (Value::Int(a), Value::Int(b)) => a == b,
20            (Value::BigInt(a), Value::BigInt(b)) => a == b,
21            (Value::Float(a), Value::Float(b)) => (a == b) || (a.is_nan() && b.is_nan()),
22            (Value::Double(a), Value::Double(b)) => (a == b) || (a.is_nan() && b.is_nan()),
23            (Value::Decimal128(a), Value::Decimal128(b)) => a == b,
24            (Value::String(a), Value::String(b)) => a == b,
25            (Value::FixedString(a), Value::FixedString(b)) => a == b,
26            (Value::Date(a), Value::Date(b)) => a == b,
27            (Value::Time(a), Value::Time(b)) => a == b,
28            (Value::DateTime(a), Value::DateTime(b)) => a == b,
29            (Value::Vertex(a), Value::Vertex(b)) => a == b,
30            (Value::Edge(a), Value::Edge(b)) => a == b,
31            (Value::EdgeHeader(a), Value::EdgeHeader(b)) => a == b,
32            (Value::Path(a), Value::Path(b)) => a == b,
33            (Value::List(a), Value::List(b)) => a == b,
34            (Value::Map(a), Value::Map(b)) => a == b,
35            (Value::Set(a), Value::Set(b)) => a == b,
36            (Value::Geography(a), Value::Geography(b)) => a == b,
37            (Value::Vector(a), Value::Vector(b)) => a == b,
38            (Value::DataSet(a), Value::DataSet(b)) => a == b,
39            (Value::Json(a), Value::Json(b)) => a == b,
40            (Value::JsonB(a), Value::JsonB(b)) => a == b,
41            // JSON and JSONB can be compared
42            (Value::Json(a), Value::JsonB(b)) => a.to_value().ok() == Some(b.as_value().clone()),
43            (Value::JsonB(a), Value::Json(b)) => Some(a.as_value().clone()) == b.to_value().ok(),
44            (Value::Blob(a), Value::Blob(b)) => a == b,
45            (Value::Uuid(a), Value::Uuid(b)) => a == b,
46            (Value::Interval(a), Value::Interval(b)) => a == b,
47            (Value::VertexId(a), Value::VertexId(b)) => a == b,
48            (Value::EdgeId(a), Value::EdgeId(b)) => a == b,
49            (Value::Struct(a), Value::Struct(b)) => a == b,
50            (Value::Array(a), Value::Array(b)) => a == b,
51
52            // Cross-type integer comparisons: promote to i64
53            (Value::SmallInt(a), Value::Int(b)) => *a as i64 == *b as i64,
54            (Value::Int(a), Value::SmallInt(b)) => *a as i64 == *b as i64,
55            (Value::SmallInt(a), Value::BigInt(b)) => *a as i64 == *b,
56            (Value::BigInt(a), Value::SmallInt(b)) => *a == *b as i64,
57            (Value::Int(a), Value::BigInt(b)) => *a as i64 == *b,
58            (Value::BigInt(a), Value::Int(b)) => *a == *b as i64,
59
60            // Integer to float comparisons
61            (Value::SmallInt(a), Value::Float(b)) => *a as f32 == *b,
62            (Value::Float(a), Value::SmallInt(b)) => *a == *b as f32,
63            (Value::Int(a), Value::Float(b)) => *a as f32 == *b,
64            (Value::Float(a), Value::Int(b)) => *a == *b as f32,
65            (Value::BigInt(a), Value::Float(b)) => *a as f32 == *b,
66            (Value::Float(a), Value::BigInt(b)) => *a == *b as f32,
67            (Value::SmallInt(a), Value::Double(b)) => *a as f64 == *b,
68            (Value::Double(a), Value::SmallInt(b)) => *a == *b as f64,
69            (Value::Int(a), Value::Double(b)) => *a as f64 == *b,
70            (Value::Double(a), Value::Int(b)) => *a == *b as f64,
71            (Value::BigInt(a), Value::Double(b)) => *a as f64 == *b,
72            (Value::Double(a), Value::BigInt(b)) => *a == *b as f64,
73            (Value::Float(a), Value::Double(b)) => *a as f64 == *b,
74            (Value::Double(a), Value::Float(b)) => *a == *b as f64,
75
76            _ => false,
77        }
78    }
79}
80
81// Eq is implemented manually, since f64 does not implement Eq
82impl Eq for Value {}
83
84impl PartialOrd for Value {
85    fn partial_cmp(&self, other: &Self) -> Option<CmpOrdering> {
86        Some(self.cmp(other))
87    }
88}
89
90impl Ord for Value {
91    fn cmp(&self, other: &Self) -> CmpOrdering {
92        match (self, other) {
93            // Comparison of the same type
94            (Value::Empty, Value::Empty) => CmpOrdering::Equal,
95            (Value::Null(a), Value::Null(b)) => Self::cmp_null(a, b),
96            (Value::Bool(a), Value::Bool(b)) => a.cmp(b),
97            (Value::SmallInt(a), Value::SmallInt(b)) => a.cmp(b),
98            (Value::Int(a), Value::Int(b)) => a.cmp(b),
99            (Value::BigInt(a), Value::BigInt(b)) => a.cmp(b),
100            (Value::Float(a), Value::Float(b)) => Self::cmp_f32(*a, *b),
101            (Value::Double(a), Value::Double(b)) => Self::cmp_f64(*a, *b),
102            (Value::Decimal128(a), Value::Decimal128(b)) => a.cmp(b),
103            (Value::Blob(a), Value::Blob(b)) => a.cmp(b),
104            (Value::String(a), Value::String(b)) => a.cmp(b),
105            (Value::FixedString(a), Value::FixedString(b)) => a.cmp(b),
106            (Value::Date(a), Value::Date(b)) => Self::cmp_date(a, b),
107            (Value::Time(a), Value::Time(b)) => Self::cmp_time(a, b),
108            (Value::DateTime(a), Value::DateTime(b)) => Self::cmp_datetime(a, b),
109            (Value::Vertex(a), Value::Vertex(b)) => a.cmp(b),
110            (Value::Edge(a), Value::Edge(b)) => a.cmp(b),
111            (Value::EdgeHeader(a), Value::EdgeHeader(b)) => a.cmp(b),
112            (Value::Path(a), Value::Path(b)) => a.cmp(b),
113            (Value::List(a), Value::List(b)) => Self::cmp_list(a, b),
114            (Value::Map(a), Value::Map(b)) => Self::cmp_map(a, b),
115            (Value::Set(a), Value::Set(b)) => Self::cmp_set(a, b),
116            (Value::Geography(a), Value::Geography(b)) => Self::cmp_geography(a, b),
117            (Value::Json(a), Value::Json(b)) => match (a.to_value(), b.to_value()) {
118                (Ok(a_val), Ok(b_val)) => Self::cmp_json_values(&a_val, &b_val),
119                _ => CmpOrdering::Equal,
120            },
121            (Value::JsonB(a), Value::JsonB(b)) => a.cmp(b),
122            // Cross-type comparison
123            (Value::Json(a), Value::JsonB(b)) => match a.to_value() {
124                Ok(a_val) => Self::cmp_json_values(&a_val, b.as_value()),
125                _ => CmpOrdering::Equal,
126            },
127            (Value::JsonB(a), Value::Json(b)) => match b.to_value() {
128                Ok(b_val) => Self::cmp_json_values(a.as_value(), &b_val),
129                _ => CmpOrdering::Equal,
130            },
131            (Value::Uuid(a), Value::Uuid(b)) => a.cmp(b),
132            (Value::Interval(a), Value::Interval(b)) => Self::cmp_interval(a, b),
133            (Value::Struct(a), Value::Struct(b)) => Self::cmp_struct(a, b),
134            (Value::Array(a), Value::Array(b)) => Self::cmp_array(a, b),
135
136            // Cross-type integer comparisons: promote to i64
137            (Value::SmallInt(a), Value::Int(b)) => (*a as i64).cmp(&(*b as i64)),
138            (Value::Int(a), Value::SmallInt(b)) => (*a as i64).cmp(&(*b as i64)),
139            (Value::SmallInt(a), Value::BigInt(b)) => (*a as i64).cmp(b),
140            (Value::BigInt(a), Value::SmallInt(b)) => a.cmp(&(*b as i64)),
141            (Value::Int(a), Value::BigInt(b)) => (*a as i64).cmp(b),
142            (Value::BigInt(a), Value::Int(b)) => a.cmp(&(*b as i64)),
143
144            // Integer to float comparisons
145            (Value::SmallInt(a), Value::Float(b)) => {
146                (*a as f32).partial_cmp(b).unwrap_or(CmpOrdering::Equal)
147            }
148            (Value::Float(a), Value::SmallInt(b)) => {
149                a.partial_cmp(&(*b as f32)).unwrap_or(CmpOrdering::Equal)
150            }
151            (Value::Int(a), Value::Float(b)) => {
152                (*a as f32).partial_cmp(b).unwrap_or(CmpOrdering::Equal)
153            }
154            (Value::Float(a), Value::Int(b)) => {
155                a.partial_cmp(&(*b as f32)).unwrap_or(CmpOrdering::Equal)
156            }
157            (Value::BigInt(a), Value::Float(b)) => {
158                (*a as f32).partial_cmp(b).unwrap_or(CmpOrdering::Equal)
159            }
160            (Value::Float(a), Value::BigInt(b)) => {
161                a.partial_cmp(&(*b as f32)).unwrap_or(CmpOrdering::Equal)
162            }
163            (Value::SmallInt(a), Value::Double(b)) => {
164                (*a as f64).partial_cmp(b).unwrap_or(CmpOrdering::Equal)
165            }
166            (Value::Double(a), Value::SmallInt(b)) => {
167                a.partial_cmp(&(*b as f64)).unwrap_or(CmpOrdering::Equal)
168            }
169            (Value::Int(a), Value::Double(b)) => {
170                (*a as f64).partial_cmp(b).unwrap_or(CmpOrdering::Equal)
171            }
172            (Value::Double(a), Value::Int(b)) => {
173                a.partial_cmp(&(*b as f64)).unwrap_or(CmpOrdering::Equal)
174            }
175            (Value::BigInt(a), Value::Double(b)) => {
176                (*a as f64).partial_cmp(b).unwrap_or(CmpOrdering::Equal)
177            }
178            (Value::Double(a), Value::BigInt(b)) => {
179                a.partial_cmp(&(*b as f64)).unwrap_or(CmpOrdering::Equal)
180            }
181            (Value::Float(a), Value::Double(b)) => {
182                (*a as f64).partial_cmp(b).unwrap_or(CmpOrdering::Equal)
183            }
184            (Value::Double(a), Value::Float(b)) => {
185                a.partial_cmp(&(*b as f64)).unwrap_or(CmpOrdering::Equal)
186            }
187
188            // Comparison between different types: based on type prioritization
189            (a, b) => Self::cmp_by_type_priority(a, b),
190        }
191    }
192}
193
194// Manually implementing Hash to handle f64 hashes
195impl Hash for Value {
196    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
197        match self {
198            Value::Empty => 0u8.hash(state),
199            Value::Null(n) => {
200                1u8.hash(state);
201                n.hash(state);
202            }
203            Value::Bool(b) => {
204                2u8.hash(state);
205                b.hash(state);
206            }
207            Value::SmallInt(i) => {
208                3u8.hash(state);
209                i.hash(state);
210            }
211            Value::Int(i) => {
212                4u8.hash(state);
213                i.hash(state);
214            }
215            Value::BigInt(i) => {
216                5u8.hash(state);
217                i.hash(state);
218            }
219            Value::Float(f) => {
220                6u8.hash(state);
221                // Creating a hash from a bitwise representation of a floating point number
222                if f.is_nan() {
223                    // All NaN values should hash to the same value
224                    0x7fc00000_u32.hash(state);
225                } else if *f == 0.0 {
226                    // Ensure +0.0 and -0.0 hash to the same value
227                    0.0_f32.to_bits().hash(state);
228                } else {
229                    f.to_bits().hash(state);
230                }
231            }
232            Value::Double(f) => {
233                7u8.hash(state);
234                // Creating a hash from a bitwise representation of a floating point number
235                if f.is_nan() {
236                    // All NaN values should hash to the same value
237                    0x7ff8000000000000_u64.hash(state);
238                } else if *f == 0.0 {
239                    // Ensure +0.0 and -0.0 hash to the same value
240                    0.0_f64.to_bits().hash(state);
241                } else {
242                    f.to_bits().hash(state);
243                }
244            }
245            Value::Decimal128(d) => {
246                8u8.hash(state);
247                d.hash(state);
248            }
249            Value::String(s) => {
250                9u8.hash(state);
251                s.hash(state);
252            }
253            Value::FixedString(data) => {
254                10u8.hash(state);
255                data.hash(state);
256            }
257            Value::Blob(b) => {
258                11u8.hash(state);
259                b.hash(state);
260            }
261            Value::Date(d) => {
262                12u8.hash(state);
263                d.hash(state);
264            }
265            Value::Time(t) => {
266                13u8.hash(state);
267                t.hash(state);
268            }
269            Value::DateTime(dt) => {
270                14u8.hash(state);
271                dt.hash(state);
272            }
273            Value::Vertex(v) => {
274                15u8.hash(state);
275                v.hash(state);
276            }
277            Value::Edge(e) => {
278                16u8.hash(state);
279                e.hash(state);
280            }
281            Value::EdgeHeader(h) => {
282                32u8.hash(state);
283                h.hash(state);
284            }
285            Value::Path(p) => {
286                17u8.hash(state);
287                p.hash(state);
288            }
289            Value::List(l) => {
290                18u8.hash(state);
291                l.hash(state);
292            }
293            Value::Map(m) => {
294                19u8.hash(state);
295                // Hash mapping by sorted key-value pairs; keys sort as `Value`.
296                let mut pairs: Vec<_> = m.iter().collect();
297                pairs.sort();
298                pairs.hash(state);
299            }
300            Value::Set(s) => {
301                20u8.hash(state);
302                // For collections, we will hash all values in sorted order to ensure consistency
303                let mut values: Vec<_> = s.iter().collect();
304                values.sort();
305                values.hash(state);
306            }
307            Value::Geography(g) => {
308                21u8.hash(state);
309                g.hash(state);
310            }
311            Value::Json(j) => {
312                22u8.hash(state);
313                j.hash(state);
314            }
315            Value::JsonB(j) => {
316                23u8.hash(state);
317                j.hash(state);
318            }
319            Value::DataSet(ds) => {
320                24u8.hash(state);
321                ds.hash(state);
322            }
323            Value::Vector(v) => {
324                25u8.hash(state);
325                v.hash(state);
326            }
327            Value::Uuid(u) => {
328                26u8.hash(state);
329                u.hash(state);
330            }
331            Value::Interval(i) => {
332                27u8.hash(state);
333                i.hash(state);
334            }
335            Value::VertexId(vid) => {
336                28u8.hash(state);
337                vid.hash(state);
338            }
339            Value::EdgeId(eid) => {
340                29u8.hash(state);
341                eid.hash(state);
342            }
343            Value::Struct(s) => {
344                30u8.hash(state);
345                s.fields.hash(state);
346            }
347            Value::Array(a) => {
348                31u8.hash(state);
349                a.values.hash(state);
350            }
351        }
352    }
353}
354
355impl Value {
356    // Null type comparison helper function
357    fn cmp_null(a: &NullType, b: &NullType) -> CmpOrdering {
358        match (a, b) {
359            (NullType::Null, NullType::Null) => CmpOrdering::Equal,
360            (NullType::NaN, NullType::NaN) => CmpOrdering::Equal,
361            (NullType::BadData, NullType::BadData) => CmpOrdering::Equal,
362            (NullType::BadType, NullType::BadType) => CmpOrdering::Equal,
363            _ => {
364                let priority_a = Self::null_type_priority(a);
365                let priority_b = Self::null_type_priority(b);
366                priority_a.cmp(&priority_b)
367            }
368        }
369    }
370
371    // Null type priority mapping function
372    fn null_type_priority(typ: &NullType) -> u8 {
373        match typ {
374            NullType::Null => 0,
375            NullType::NaN => 1,
376            NullType::BadData => 2,
377            NullType::BadType => 2,
378            NullType::ErrOverflow => 3,
379            NullType::UnknownProp => 4,
380            NullType::DivByZero => 5,
381            NullType::OutOfRange => 6,
382        }
383    }
384
385    // Floating Point Comparison Helper Functions (f32)
386    fn cmp_f32(a: f32, b: f32) -> CmpOrdering {
387        if a.is_nan() && b.is_nan() {
388            CmpOrdering::Equal
389        } else if a.is_nan() {
390            CmpOrdering::Less
391        } else if b.is_nan() {
392            CmpOrdering::Greater
393        } else {
394            a.partial_cmp(&b).unwrap_or(CmpOrdering::Equal)
395        }
396    }
397
398    // Floating Point Comparison Helper Functions (f64)
399    fn cmp_f64(a: f64, b: f64) -> CmpOrdering {
400        if a.is_nan() && b.is_nan() {
401            CmpOrdering::Equal
402        } else if a.is_nan() {
403            CmpOrdering::Less
404        } else if b.is_nan() {
405            CmpOrdering::Greater
406        } else {
407            a.partial_cmp(&b).unwrap_or(CmpOrdering::Equal)
408        }
409    }
410
411    // Date Comparison Helper Functions
412    fn cmp_date(a: &DateValue, b: &DateValue) -> CmpOrdering {
413        match a.year.cmp(&b.year) {
414            CmpOrdering::Equal => match a.month.cmp(&b.month) {
415                CmpOrdering::Equal => a.day.cmp(&b.day),
416                ord => ord,
417            },
418            ord => ord,
419        }
420    }
421
422    // Time Comparison Auxiliary Functions
423    fn cmp_time(a: &TimeValue, b: &TimeValue) -> CmpOrdering {
424        match a.hour.cmp(&b.hour) {
425            CmpOrdering::Equal => match a.minute.cmp(&b.minute) {
426                CmpOrdering::Equal => match a.sec.cmp(&b.sec) {
427                    CmpOrdering::Equal => a.microsec.cmp(&b.microsec),
428                    ord => ord,
429                },
430                ord => ord,
431            },
432            ord => ord,
433        }
434    }
435
436    // Date-Time Comparison Helper Functions
437    fn cmp_datetime(a: &DateTimeValue, b: &DateTimeValue) -> CmpOrdering {
438        match a.year.cmp(&b.year) {
439            CmpOrdering::Equal => match a.month.cmp(&b.month) {
440                CmpOrdering::Equal => match a.day.cmp(&b.day) {
441                    CmpOrdering::Equal => match a.hour.cmp(&b.hour) {
442                        CmpOrdering::Equal => match a.minute.cmp(&b.minute) {
443                            CmpOrdering::Equal => match a.sec.cmp(&b.sec) {
444                                CmpOrdering::Equal => a.microsec.cmp(&b.microsec),
445                                ord => ord,
446                            },
447                            ord => ord,
448                        },
449                        ord => ord,
450                    },
451                    ord => ord,
452                },
453                ord => ord,
454            },
455            ord => ord,
456        }
457    }
458
459    // List Comparison Helper Functions
460    fn cmp_list(a: &List, b: &List) -> CmpOrdering {
461        a.values.len().cmp(&b.values.len()).then_with(|| {
462            a.values
463                .iter()
464                .zip(b.values.iter())
465                .fold(CmpOrdering::Equal, |acc, (a_val, b_val)| {
466                    if acc == CmpOrdering::Equal {
467                        a_val.cmp(b_val)
468                    } else {
469                        acc
470                    }
471                })
472        })
473    }
474
475    // Mapping Comparison Helper Functions
476    fn cmp_map(a: &HashMap<Value, Value>, b: &HashMap<Value, Value>) -> CmpOrdering {
477        a.len().cmp(&b.len()).then_with(|| {
478            let mut a_pairs: Vec<_> = a.iter().collect();
479            let mut b_pairs: Vec<_> = b.iter().collect();
480            a_pairs.sort();
481            b_pairs.sort();
482            a_pairs.iter().zip(b_pairs.iter()).fold(
483                CmpOrdering::Equal,
484                |acc, ((a_k, a_v), (b_k, b_v))| {
485                    if acc == CmpOrdering::Equal {
486                        a_k.cmp(b_k).then_with(|| a_v.cmp(b_v))
487                    } else {
488                        acc
489                    }
490                },
491            )
492        })
493    }
494
495    // Collection comparison helper functions
496    fn cmp_set(
497        a: &std::collections::HashSet<Value>,
498        b: &std::collections::HashSet<Value>,
499    ) -> CmpOrdering {
500        a.len().cmp(&b.len()).then_with(|| {
501            let mut a_values: Vec<_> = a.iter().collect();
502            let mut b_values: Vec<_> = b.iter().collect();
503            a_values.sort();
504            b_values.sort();
505            a_values
506                .iter()
507                .zip(b_values.iter())
508                .fold(CmpOrdering::Equal, |acc, (a_val, b_val)| {
509                    if acc == CmpOrdering::Equal {
510                        a_val.cmp(b_val)
511                    } else {
512                        acc
513                    }
514                })
515        })
516    }
517
518    // Geographic comparison helper functions
519    fn cmp_geography(a: &Geography, b: &Geography) -> CmpOrdering {
520        use crate::value::geography::Geography::*;
521
522        fn type_priority(geo: &Geography) -> u8 {
523            match geo {
524                Point(_) => 0,
525                LineString(_) => 1,
526                Polygon(_) => 2,
527                MultiPoint(_) => 3,
528                MultiLineString(_) => 4,
529                MultiPolygon(_) => 5,
530            }
531        }
532
533        fn cmp_points(
534            a: &[crate::value::geography::GeographyValue],
535            b: &[crate::value::geography::GeographyValue],
536        ) -> CmpOrdering {
537            a.len().cmp(&b.len()).then_with(|| {
538                for (pa, pb) in a.iter().zip(b.iter()) {
539                    match pa.latitude.partial_cmp(&pb.latitude) {
540                        Some(CmpOrdering::Equal) => match pa.longitude.partial_cmp(&pb.longitude) {
541                            Some(CmpOrdering::Equal) => continue,
542                            Some(ord) => return ord,
543                            None => return CmpOrdering::Equal,
544                        },
545                        Some(ord) => return ord,
546                        None => continue,
547                    }
548                }
549                CmpOrdering::Equal
550            })
551        }
552
553        match (a, b) {
554            (Point(pa), Point(pb)) => match pa.latitude.partial_cmp(&pb.latitude) {
555                Some(CmpOrdering::Equal) => pa
556                    .longitude
557                    .partial_cmp(&pb.longitude)
558                    .unwrap_or(CmpOrdering::Equal),
559                Some(ord) => ord,
560                None => CmpOrdering::Equal,
561            },
562            (LineString(la), LineString(lb)) => cmp_points(&la.points, &lb.points),
563            (Polygon(pa), Polygon(pb)) => cmp_points(&pa.exterior.points, &pb.exterior.points)
564                .then_with(|| pa.holes.len().cmp(&pb.holes.len())),
565            (MultiPoint(ma), MultiPoint(mb)) => cmp_points(&ma.points, &mb.points),
566            (MultiLineString(ma), MultiLineString(mb)) => ma
567                .linestrings
568                .len()
569                .cmp(&mb.linestrings.len())
570                .then_with(|| {
571                    for (la, lb) in ma.linestrings.iter().zip(mb.linestrings.iter()) {
572                        let ord = cmp_points(&la.points, &lb.points);
573                        if ord != CmpOrdering::Equal {
574                            return ord;
575                        }
576                    }
577                    CmpOrdering::Equal
578                }),
579            (MultiPolygon(ma), MultiPolygon(mb)) => {
580                ma.polygons.len().cmp(&mb.polygons.len()).then_with(|| {
581                    for (pa, pb) in ma.polygons.iter().zip(mb.polygons.iter()) {
582                        let ord = cmp_points(&pa.exterior.points, &pb.exterior.points);
583                        if ord != CmpOrdering::Equal {
584                            return ord;
585                        }
586                    }
587                    CmpOrdering::Equal
588                })
589            }
590            _ => type_priority(a).cmp(&type_priority(b)),
591        }
592    }
593
594    // Interval Comparison Helper Functions
595    fn cmp_interval(a: &IntervalValue, b: &IntervalValue) -> CmpOrdering {
596        match a.months.cmp(&b.months) {
597            CmpOrdering::Equal => match a.days.cmp(&b.days) {
598                CmpOrdering::Equal => a.microseconds.cmp(&b.microseconds),
599                ord => ord,
600            },
601            ord => ord,
602        }
603    }
604
605    // Struct comparison: field count, then field-name order, then field values.
606    fn cmp_struct(a: &crate::value::StructValue, b: &crate::value::StructValue) -> CmpOrdering {
607        a.fields.len().cmp(&b.fields.len()).then_with(|| {
608            a.fields.iter().zip(b.fields.iter()).fold(
609                CmpOrdering::Equal,
610                |acc, ((a_k, a_v), (b_k, b_v))| {
611                    if acc == CmpOrdering::Equal {
612                        a_k.cmp(b_k).then_with(|| a_v.cmp(b_v))
613                    } else {
614                        acc
615                    }
616                },
617            )
618        })
619    }
620
621    // Array comparison: element-by-element lexicographic, then length.
622    fn cmp_array(a: &crate::value::ArrayValue, b: &crate::value::ArrayValue) -> CmpOrdering {
623        a.values
624            .iter()
625            .zip(b.values.iter())
626            .fold(CmpOrdering::Equal, |acc, (a_val, b_val)| {
627                if acc == CmpOrdering::Equal {
628                    a_val.cmp(b_val)
629                } else {
630                    acc
631                }
632            })
633            .then_with(|| a.values.len().cmp(&b.values.len()))
634    }
635
636    // JSON value comparison helper functions
637    fn cmp_json_values(a: &serde_json::Value, b: &serde_json::Value) -> CmpOrdering {
638        use serde_json::Value as JsonValue;
639        match (a, b) {
640            (JsonValue::Null, JsonValue::Null) => CmpOrdering::Equal,
641            (JsonValue::Bool(a), JsonValue::Bool(b)) => a.cmp(b),
642            (JsonValue::Number(a), JsonValue::Number(b)) => {
643                // Compare numbers
644                if let (Some(a_i64), Some(b_i64)) = (a.as_i64(), b.as_i64()) {
645                    a_i64.cmp(&b_i64)
646                } else if let (Some(a_f64), Some(b_f64)) = (a.as_f64(), b.as_f64()) {
647                    a_f64.partial_cmp(&b_f64).unwrap_or(CmpOrdering::Equal)
648                } else {
649                    CmpOrdering::Equal
650                }
651            }
652            (JsonValue::String(a), JsonValue::String(b)) => a.cmp(b),
653            (JsonValue::Array(a), JsonValue::Array(b)) => a.len().cmp(&b.len()).then_with(|| {
654                a.iter()
655                    .zip(b.iter())
656                    .fold(CmpOrdering::Equal, |acc, (a_val, b_val)| {
657                        if acc == CmpOrdering::Equal {
658                            Self::cmp_json_values(a_val, b_val)
659                        } else {
660                            acc
661                        }
662                    })
663            }),
664            (JsonValue::Object(a), JsonValue::Object(b)) => a.len().cmp(&b.len()).then_with(|| {
665                let mut a_pairs: Vec<_> = a.iter().collect();
666                let mut b_pairs: Vec<_> = b.iter().collect();
667                a_pairs.sort_by_key(|&(k, _)| k);
668                b_pairs.sort_by_key(|&(k, _)| k);
669                a_pairs.iter().zip(b_pairs.iter()).fold(
670                    CmpOrdering::Equal,
671                    |acc, ((a_k, a_v), (b_k, b_v))| {
672                        if acc == CmpOrdering::Equal {
673                            a_k.cmp(b_k).then_with(|| Self::cmp_json_values(a_v, b_v))
674                        } else {
675                            acc
676                        }
677                    },
678                )
679            }),
680            // Cross-type comparison: Null < Bool < Number < String < Array < Object
681            (JsonValue::Null, _) => CmpOrdering::Less,
682            (_, JsonValue::Null) => CmpOrdering::Greater,
683            (JsonValue::Bool(_), _) => CmpOrdering::Less,
684            (_, JsonValue::Bool(_)) => CmpOrdering::Greater,
685            (JsonValue::Number(_), _) => CmpOrdering::Less,
686            (_, JsonValue::Number(_)) => CmpOrdering::Greater,
687            (JsonValue::String(_), _) => CmpOrdering::Less,
688            (_, JsonValue::String(_)) => CmpOrdering::Greater,
689            (JsonValue::Array(_), _) => CmpOrdering::Less,
690            (_, JsonValue::Array(_)) => CmpOrdering::Greater,
691        }
692    }
693
694    // Type priority comparison helper functions
695    fn cmp_by_type_priority(a: &Value, b: &Value) -> CmpOrdering {
696        let priority_a = Self::type_priority(a);
697        let priority_b = Self::type_priority(b);
698        priority_a.cmp(&priority_b)
699    }
700
701    // Type priority mapping function
702    fn type_priority(value: &Value) -> u8 {
703        match value {
704            Value::Empty => 0,
705            Value::Null(_) => 1,
706            Value::Bool(_) => 2,
707            Value::SmallInt(_) => 3,
708            Value::Int(_) => 4,
709            Value::BigInt(_) => 5,
710            Value::Float(_) => 6,
711            Value::Double(_) => 7,
712            Value::Decimal128(_) => 8,
713            Value::Date(_) => 9,
714            Value::Time(_) => 10,
715            Value::DateTime(_) => 11,
716            Value::String(_) => 12,
717            Value::FixedString(_) => 13,
718            Value::Blob(_) => 14,
719            Value::Vertex(_) => 15,
720            Value::Edge(_) => 16,
721            Value::EdgeHeader(_) => 32,
722            Value::Path(_) => 17,
723            Value::List(_) => 18,
724            Value::Map(_) => 19,
725            Value::Set(_) => 20,
726            Value::Geography(_) => 21,
727            Value::Json(_) => 22,
728            Value::JsonB(_) => 23,
729            Value::DataSet(_) => 24,
730            Value::Vector(_) => 25,
731            Value::Uuid(_) => 26,
732            Value::Interval(_) => 27,
733            Value::VertexId(_) => 28,
734            Value::EdgeId(_) => 29,
735            Value::Struct(_) => 30,
736            Value::Array(_) => 31,
737        }
738    }
739}