Skip to content

Commit ce23717

Browse files
committed
Multipath - DEL - order by array index even if path lengths are different
1 parent f6dd83d commit ce23717

File tree

2 files changed

+42
-31
lines changed

2 files changed

+42
-31
lines changed

src/commands.rs

+33-30
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::redisjson::SetOptions;
1616
use serde_json::{Number, Value};
1717

1818
use itertools::FoldWhile::{Continue, Done};
19-
use itertools::Itertools;
19+
use itertools::{EitherOrBoth, Itertools};
2020
use serde::{Serialize, Serializer};
2121
use std::collections::HashMap;
2222

@@ -758,40 +758,43 @@ where
758758
.collect::<Vec<Value>>()
759759
}
760760

761-
/// Sort the paths so higher indices precede lower indices on the same on the same array
762-
/// And objects with higher hierarchy (closer to the top-level) preceded objects with deeper hierarchy
761+
/// Sort the paths so higher indices precede lower indices on the same array,
762+
/// And if one path is a sub-path of the other, then paths with shallower hierarchy (closer to the top-level) precedes paths with deeper hierarchy
763763
fn prepare_paths_for_deletion(paths: &mut Vec<Vec<String>>) {
764-
paths.sort_by(|v1, v2| match (v1.len(), v2.len()) {
765-
(l1, l2) if l1 < l2 => Ordering::Less, // Shorter paths before longer paths
766-
(l1, l2) if l1 > l2 => Ordering::Greater, // Shorter paths before longer paths
767-
_ => v1
768-
.iter()
769-
.zip(v2.iter())
770-
.fold_while(Ordering::Equal, |_acc, (p1, p2)| {
771-
let i1 = p1.parse::<usize>();
772-
let i2 = p2.parse::<usize>();
773-
match (i1, i2) {
774-
(Err(_), Err(_)) => match p1.cmp(p2) {
775-
// String compare
776-
Ordering::Less => Done(Ordering::Less),
777-
Ordering::Equal => Continue(Ordering::Equal),
778-
Ordering::Greater => Done(Ordering::Greater),
779-
},
780-
(Ok(_), Err(_)) => Done(Ordering::Greater), //String before Numeric
781-
(Err(_), Ok(_)) => Done(Ordering::Less), //String before Numeric
782-
(Ok(i1), Ok(i2)) => {
783-
// Numeric compare - higher indices before lower ones
784-
if i1 < i2 {
785-
Done(Ordering::Greater)
786-
} else if i2 < i1 {
787-
Done(Ordering::Less)
788-
} else {
789-
Continue(Ordering::Equal)
764+
paths.sort_by(|v1, v2| {
765+
v1.iter()
766+
.zip_longest(v2.iter())
767+
.fold_while(Ordering::Equal, |_acc, v| {
768+
match v {
769+
EitherOrBoth::Left(_) => Done(Ordering::Greater), // Shorter paths before longer paths
770+
EitherOrBoth::Right(_) => Done(Ordering::Less), // Shorter paths before longer paths
771+
EitherOrBoth::Both(p1, p2) => {
772+
let i1 = p1.parse::<usize>();
773+
let i2 = p2.parse::<usize>();
774+
match (i1, i2) {
775+
(Err(_), Err(_)) => match p1.cmp(p2) {
776+
// String compare
777+
Ordering::Less => Done(Ordering::Less),
778+
Ordering::Equal => Continue(Ordering::Equal),
779+
Ordering::Greater => Done(Ordering::Greater),
780+
},
781+
(Ok(_), Err(_)) => Done(Ordering::Greater), //String before Numeric
782+
(Err(_), Ok(_)) => Done(Ordering::Less), //String before Numeric
783+
(Ok(i1), Ok(i2)) => {
784+
// Numeric compare - higher indices before lower ones
785+
if i1 < i2 {
786+
Done(Ordering::Greater)
787+
} else if i2 < i1 {
788+
Done(Ordering::Less)
789+
} else {
790+
Continue(Ordering::Equal)
791+
}
792+
}
790793
}
791794
}
792795
}
793796
})
794-
.into_inner(),
797+
.into_inner()
795798
});
796799
}
797800

tests/pytest/test_multi.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def testDelCommand(env):
6666
r.assertEqual(res, 3)
6767

6868
r.assertOk(r.execute_command('JSON.SET', 'doc2', '$', '[1, 2, 3]'))
69-
res = r.execute_command('JSON.DEL', 'doc2', '$[2,1,0]')
69+
res = r.execute_command('JSON.DEL', 'doc2', '$[1,2,0]')
7070
r.assertEqual(res, 3)
7171

7272
r.assertOk(r.execute_command('JSON.SET', 'doc2', '$', '{"b": [1,2,3], "a": {"b": [1, 2, 3], "c": [1, 2, 3]}, "x": {"b": [1, 2, 3], "c": [1, 2, 3]}}'))
@@ -88,6 +88,14 @@ def testDelCommand(env):
8888
res = r.execute_command('JSON.GET', 'doc2', '$')
8989
r.assertEqual(json.loads(res), [[True, {"answer": 42}]])
9090

91+
def testDelCommand_issue529(env):
92+
r = env
93+
r.assertOk(r.execute_command('JSON.SET', 'doc1', '$', '[{"a00": [{"a00": "a00_00"}, {"a01": "a00_01"}, {"a02": "a00_02"}, {"a03": "a00_03"}]}, {"a01": [{"a00": "a01_00"}, {"a01": "a01_01"}, {"a02": "a01_02"}, {"a03": "a01_03"}]}, {"a02": [{"a00": "a02_00"}, {"a01": "a02_01"}, {"a02": "a02_02"}, {"a03": "a02_03"}]}, {"a03": [{"a00": "a03_00"}, {"a01": "a03_01"}, {"a02": "a03_02"}, {"a03": "a03_03"}]}]'))
94+
res = r.execute_command('JSON.DEL', 'doc1', '$..[2]')
95+
r.assertEqual(res, 4)
96+
res = r.execute_command('JSON.ARRLEN', 'doc1', '$.*[*]')
97+
r.assertEqual(res, [3, 3, 3])
98+
9199

92100
def testForgetCommand(env):
93101
"""Test REJSON.FORGET command"""

0 commit comments

Comments
 (0)