@@ -4,6 +4,7 @@ use crate::redisjson::{normalize_arr_indices, Format, Path};
4
4
use jsonpath_lib:: select:: select_value:: { SelectValue , SelectValueType } ;
5
5
use redis_module:: { Context , RedisValue } ;
6
6
use redis_module:: { NextArg , RedisError , RedisResult , RedisString , REDIS_OK } ;
7
+ use std:: cmp:: Ordering ;
7
8
8
9
use jsonpath_lib:: select:: Selector ;
9
10
@@ -15,8 +16,11 @@ use crate::redisjson::SetOptions;
15
16
16
17
use serde_json:: { Number , Value } ;
17
18
18
- use serde:: Serialize ;
19
+ use itertools:: FoldWhile :: { Continue , Done } ;
20
+ use itertools:: Itertools ;
21
+ use serde:: { Serialize , Serializer } ;
19
22
use std:: collections:: HashMap ;
23
+
20
24
const JSON_ROOT_PATH : & str = "$" ;
21
25
const JSON_ROOT_PATH_LEGACY : & str = "." ;
22
26
const CMD_ARG_NOESCAPE : & str = "NOESCAPE" ;
@@ -53,6 +57,23 @@ const JSONGET_SUBCOMMANDS_MAXSTRLEN: usize = max_strlen(&[
53
57
CMD_ARG_FORMAT ,
54
58
] ) ;
55
59
60
+ enum Values < ' a , V : SelectValue > {
61
+ Single ( & ' a V ) ,
62
+ Multi ( Vec < & ' a V > ) ,
63
+ }
64
+
65
+ impl < ' a , V : SelectValue > Serialize for Values < ' a , V > {
66
+ fn serialize < S > ( & self , serializer : S ) -> Result < S :: Ok , S :: Error >
67
+ where
68
+ S : Serializer ,
69
+ {
70
+ match self {
71
+ Values :: Single ( v) => v. serialize ( serializer) ,
72
+ Values :: Multi ( v) => v. serialize ( serializer) ,
73
+ }
74
+ }
75
+ }
76
+
56
77
pub struct KeyValue < ' a , V : SelectValue > {
57
78
val : & ' a V ,
58
79
}
@@ -151,71 +172,55 @@ impl<'a, V: SelectValue> KeyValue<'a, V> {
151
172
String :: from_utf8 ( out. into_inner ( ) ) . unwrap ( )
152
173
}
153
174
154
- fn to_json_legacy (
175
+ fn to_json_multi (
155
176
& ' a self ,
156
177
paths : & mut Vec < Path > ,
157
178
indent : Option < & str > ,
158
179
newline : Option < & str > ,
159
180
space : Option < & str > ,
181
+ is_legacy : bool ,
160
182
) -> Result < RedisValue , Error > {
161
- if paths. len ( ) > 1 {
162
- // TODO: Creating a temp doc here duplicates memory usage. This can be very memory inefficient.
163
- // A better way would be to create a doc of references to the original doc but no current support
164
- // in serde_json. I'm going for this implementation anyway because serde_json isn't supposed to be
165
- // memory efficient and we're using it anyway. See https://github.com/serde-rs/json/issues/635.
166
- let mut missing_path = None ;
167
- let temp_doc = paths. drain ( ..) . fold ( HashMap :: new ( ) , |mut acc, path| {
168
- let mut selector = Selector :: new ( ) ;
169
- selector. value ( self . val ) ;
170
- if selector. str_path ( path. get_path ( ) ) . is_err ( ) {
171
- return acc;
172
- }
173
- let value = match selector. select ( ) {
174
- Ok ( s) => s. first ( ) . copied ( ) ,
175
- Err ( _) => None ,
176
- } ;
177
- if value. is_none ( ) && missing_path. is_none ( ) {
178
- missing_path = Some ( path. get_original ( ) . to_string ( ) ) ;
179
- }
180
- acc. insert ( path. get_original ( ) , value) ;
181
- acc
182
- } ) ;
183
- if let Some ( p) = missing_path {
184
- return Err ( format ! ( "ERR path {} does not exist" , p) . into ( ) ) ;
183
+ // TODO: Creating a temp doc here duplicates memory usage. This can be very memory inefficient.
184
+ // A better way would be to create a doc of references to the original doc but no current support
185
+ // in serde_json. I'm going for this implementation anyway because serde_json isn't supposed to be
186
+ // memory efficient and we're using it anyway. See https://github.com/serde-rs/json/issues/635.
187
+ let mut missing_path = None ;
188
+ let temp_doc = paths. drain ( ..) . fold ( HashMap :: new ( ) , |mut acc, path : Path | {
189
+ let mut selector = Selector :: new ( ) ;
190
+ selector. value ( self . val ) ;
191
+ if selector. str_path ( path. get_path ( ) ) . is_err ( ) {
192
+ return acc;
185
193
}
194
+ let value = match selector. select ( ) {
195
+ Ok ( s) if is_legacy && !s. is_empty ( ) => Some ( Values :: Single ( s[ 0 ] ) ) ,
196
+ Ok ( s) if !is_legacy => Some ( Values :: Multi ( s) ) ,
197
+ _ => None ,
198
+ } ;
186
199
187
- Ok ( Self :: serialize_object ( & temp_doc, indent, newline, space) . into ( ) )
188
- } else {
189
- Ok (
190
- Self :: serialize_object (
191
- self . get_first ( paths[ 0 ] . get_path ( ) ) ?,
192
- indent,
193
- newline,
194
- space,
195
- )
196
- . into ( ) ,
197
- )
200
+ if value. is_none ( ) && missing_path. is_none ( ) {
201
+ missing_path = Some ( path. get_original ( ) . to_string ( ) ) ;
202
+ }
203
+ acc. insert ( path. get_original ( ) , value) ;
204
+ acc
205
+ } ) ;
206
+ if let Some ( p) = missing_path {
207
+ return Err ( format ! ( "ERR path {} does not exist" , p) . into ( ) ) ;
198
208
}
209
+ Ok ( Self :: serialize_object ( & temp_doc, indent, newline, space) . into ( ) )
199
210
}
200
211
201
- fn to_json_multi (
212
+ fn to_json_single (
202
213
& ' a self ,
203
- paths : & Vec < Path > ,
214
+ path : & str ,
204
215
indent : Option < & str > ,
205
216
newline : Option < & str > ,
206
217
space : Option < & str > ,
218
+ is_legacy : bool ,
207
219
) -> Result < RedisValue , Error > {
208
- if paths. len ( ) > 1 {
209
- let mut res: Vec < Vec < & V > > = vec ! [ ] ;
210
- for path in paths {
211
- let values = self . get_values ( path. get_path ( ) ) ?;
212
- res. push ( values) ;
213
- }
214
- Ok ( Self :: serialize_object ( & res, indent, newline, space) . into ( ) )
220
+ if is_legacy {
221
+ Ok ( self . to_string_single ( path, indent, newline, space) ?. into ( ) )
215
222
} else {
216
- Ok ( self
217
- . to_string_multi ( paths[ 0 ] . get_path ( ) , indent, newline, space) ?
218
- . into ( ) )
223
+ Ok ( self . to_string_multi ( path, indent, newline, space) ?. into ( ) )
219
224
}
220
225
}
221
226
@@ -231,10 +236,10 @@ impl<'a, V: SelectValue> KeyValue<'a, V> {
231
236
return Err ( "Soon to come..." . into ( ) ) ;
232
237
}
233
238
let is_legacy = !paths. iter ( ) . any ( |p| !p. is_legacy ( ) ) ;
234
- if is_legacy {
235
- self . to_json_legacy ( paths, indent, newline, space)
239
+ if paths . len ( ) > 1 {
240
+ self . to_json_multi ( paths, indent, newline, space, is_legacy )
236
241
} else {
237
- self . to_json_multi ( paths, indent, newline, space)
242
+ self . to_json_single ( paths[ 0 ] . get_path ( ) , indent, newline, space, is_legacy )
238
243
}
239
244
}
240
245
@@ -330,6 +335,17 @@ impl<'a, V: SelectValue> KeyValue<'a, V> {
330
335
Self :: serialize ( results, format)
331
336
}
332
337
338
+ pub fn to_string_single (
339
+ & self ,
340
+ path : & str ,
341
+ indent : Option < & str > ,
342
+ newline : Option < & str > ,
343
+ space : Option < & str > ,
344
+ ) -> Result < String , Error > {
345
+ let result = self . get_first ( path) ?;
346
+ Ok ( Self :: serialize_object ( & result, indent, newline, space) )
347
+ }
348
+
333
349
pub fn to_string_multi (
334
350
& self ,
335
351
path : & str ,
@@ -730,6 +746,43 @@ where
730
746
. collect :: < Vec < Value > > ( )
731
747
}
732
748
749
+ /// Sort the paths so higher indices precede lower indices on the same on the same array
750
+ /// And objects with higher hierarchy (closer to the top-level) preceded objects with deeper hierarchy
751
+ fn prepare_paths_for_deletion ( paths : & mut Vec < Vec < String > > ) {
752
+ paths. sort_by ( |v1, v2| match ( v1. len ( ) , v2. len ( ) ) {
753
+ ( l1, l2) if l1 < l2 => Ordering :: Less , // Shorter paths before longer paths
754
+ ( l1, l2) if l1 > l2 => Ordering :: Greater , // Shorter paths before longer paths
755
+ _ => v1
756
+ . iter ( )
757
+ . zip ( v2. iter ( ) )
758
+ . fold_while ( Ordering :: Equal , |_acc, ( p1, p2) | {
759
+ let i1 = p1. parse :: < usize > ( ) ;
760
+ let i2 = p2. parse :: < usize > ( ) ;
761
+ match ( i1, i2) {
762
+ ( Err ( _) , Err ( _) ) => match p1. cmp ( p2) {
763
+ // String compare
764
+ Ordering :: Less => Done ( Ordering :: Less ) ,
765
+ Ordering :: Equal => Continue ( Ordering :: Equal ) ,
766
+ Ordering :: Greater => Done ( Ordering :: Greater ) ,
767
+ } ,
768
+ ( Ok ( _) , Err ( _) ) => Done ( Ordering :: Greater ) , //String before Numeric
769
+ ( Err ( _) , Ok ( _) ) => Done ( Ordering :: Less ) , //String before Numeric
770
+ ( Ok ( i1) , Ok ( i2) ) => {
771
+ // Numeric compare - higher indices before lower ones
772
+ if i1 < i2 {
773
+ Done ( Ordering :: Greater )
774
+ } else if i2 < i1 {
775
+ Done ( Ordering :: Less )
776
+ } else {
777
+ Continue ( Ordering :: Equal )
778
+ }
779
+ }
780
+ }
781
+ } )
782
+ . into_inner ( ) ,
783
+ } ) ;
784
+ }
785
+
733
786
pub fn command_json_del < M : Manager > (
734
787
manager : M ,
735
788
ctx : & Context ,
@@ -750,7 +803,8 @@ pub fn command_json_del<M: Manager>(
750
803
redis_key. delete ( ) ?;
751
804
1
752
805
} else {
753
- let paths = find_paths ( path. get_path ( ) , doc, |_| true ) ?;
806
+ let mut paths = find_paths ( path. get_path ( ) , doc, |_| true ) ?;
807
+ prepare_paths_for_deletion ( & mut paths) ;
754
808
let mut changed = 0 ;
755
809
for p in paths {
756
810
if redis_key. delete_path ( p) ? {
@@ -785,7 +839,7 @@ pub fn command_json_mget<M: Manager>(
785
839
let to_string =
786
840
|doc : & M :: V | KeyValue :: new ( doc) . to_string_multi ( path. get_path ( ) , None , None , None ) ;
787
841
let to_string_legacy =
788
- |doc : & M :: V | KeyValue :: new ( doc) . to_string ( path. get_path ( ) , Format :: JSON ) ;
842
+ |doc : & M :: V | KeyValue :: new ( doc) . to_string_single ( path. get_path ( ) , None , None , None ) ;
789
843
let is_legacy = path. is_legacy ( ) ;
790
844
791
845
let results: Result < Vec < RedisValue > , RedisError > = keys
0 commit comments