@@ -172,27 +172,10 @@ pub fn json_set<M: Manager>(manager: M, ctx: &Context, args: Vec<RedisString>) -
172
172
Ok ( RedisValue :: Null )
173
173
}
174
174
} else {
175
- let mut update_info = KeyValue :: new ( doc) . find_paths ( path. get_path ( ) , & op) ?;
175
+ let update_info = KeyValue :: new ( doc) . find_paths ( path. get_path ( ) , & op) ?;
176
176
if !update_info. is_empty ( ) {
177
- let mut res = false ;
178
- if update_info. len ( ) == 1 {
179
- res = match update_info. pop ( ) . unwrap ( ) {
180
- UpdateInfo :: SUI ( sui) => redis_key. set_value ( sui. path , val) ?,
181
- UpdateInfo :: AUI ( aui) => redis_key. dict_add ( aui. path , & aui. key , val) ?,
182
- }
183
- } else {
184
- for ui in update_info {
185
- res = match ui {
186
- UpdateInfo :: SUI ( sui) => {
187
- redis_key. set_value ( sui. path , val. clone ( ) ) ?
188
- }
189
- UpdateInfo :: AUI ( aui) => {
190
- redis_key. dict_add ( aui. path , & aui. key , val. clone ( ) ) ?
191
- }
192
- }
193
- }
194
- }
195
- if res {
177
+ let updated = apply_updates :: < M > ( & mut redis_key, val, update_info) ;
178
+ if updated {
196
179
redis_key. apply_changes ( ctx, "json.set" ) ?;
197
180
REDIS_OK
198
181
} else {
@@ -218,6 +201,88 @@ pub fn json_set<M: Manager>(manager: M, ctx: &Context, args: Vec<RedisString>) -
218
201
}
219
202
}
220
203
204
+ ///
205
+ /// JSON.MSET <key> <path> <json> [[<key> <path> <json>]...]
206
+ ///
207
+ pub fn json_mset < M : Manager > ( manager : M , ctx : & Context , args : Vec < RedisString > ) -> RedisResult {
208
+ let mut args = args. into_iter ( ) . skip ( 1 ) ;
209
+
210
+ if args. len ( ) < 3 {
211
+ return Err ( RedisError :: WrongArity ) ;
212
+ }
213
+
214
+ // Collect all the actions from the args (redis_key, update_info, value)
215
+ let mut actions = Vec :: new ( ) ;
216
+ while let Ok ( key) = args. next_arg ( ) {
217
+ let mut redis_key = manager. open_key_write ( ctx, key) ?;
218
+
219
+ // Verify the key is a JSON type
220
+ let key_value = redis_key. get_value ( ) ?;
221
+
222
+ // Verify the path is valid and get all the update info
223
+ let path = Path :: new ( args. next_str ( ) ?) ;
224
+ let update_info = if path. get_path ( ) == JSON_ROOT_PATH {
225
+ None
226
+ } else if let Some ( value) = key_value {
227
+ Some ( KeyValue :: new ( value) . find_paths ( path. get_path ( ) , & SetOptions :: None ) ?)
228
+ } else {
229
+ return Err ( RedisError :: Str (
230
+ "ERR new objects must be created at the root" ,
231
+ ) ) ;
232
+ } ;
233
+
234
+ // Parse the input and validate it's valid JSON
235
+ let value_str = args. next_str ( ) ?;
236
+ let value = manager. from_str ( value_str, Format :: JSON , true ) ?;
237
+
238
+ actions. push ( ( redis_key, update_info, value) ) ;
239
+ }
240
+
241
+ actions
242
+ . drain ( ..)
243
+ . fold ( REDIS_OK , |res, ( mut redis_key, update_info, value) | {
244
+ let updated = if let Some ( update_info) = update_info {
245
+ !update_info. is_empty ( ) && apply_updates :: < M > ( & mut redis_key, value, update_info)
246
+ } else {
247
+ // In case it is a root path
248
+ redis_key. set_value ( Vec :: new ( ) , value) ?
249
+ } ;
250
+ if updated {
251
+ redis_key. apply_changes ( ctx, "json.mset" ) ?
252
+ }
253
+ res
254
+ } )
255
+ }
256
+
257
+ fn apply_updates < M : Manager > (
258
+ redis_key : & mut M :: WriteHolder ,
259
+ value : M :: O ,
260
+ mut update_info : Vec < UpdateInfo > ,
261
+ ) -> bool {
262
+ // If there is only one update info, we can avoid cloning the value
263
+ if update_info. len ( ) == 1 {
264
+ match update_info. pop ( ) . unwrap ( ) {
265
+ UpdateInfo :: SUI ( sui) => redis_key. set_value ( sui. path , value) . unwrap_or ( false ) ,
266
+ UpdateInfo :: AUI ( aui) => redis_key
267
+ . dict_add ( aui. path , & aui. key , value)
268
+ . unwrap_or ( false ) ,
269
+ }
270
+ } else {
271
+ let mut updated = false ;
272
+ for ui in update_info {
273
+ updated = match ui {
274
+ UpdateInfo :: SUI ( sui) => redis_key
275
+ . set_value ( sui. path , value. clone ( ) )
276
+ . unwrap_or ( false ) ,
277
+ UpdateInfo :: AUI ( aui) => redis_key
278
+ . dict_add ( aui. path , & aui. key , value. clone ( ) )
279
+ . unwrap_or ( false ) ,
280
+ } || updated
281
+ }
282
+ updated
283
+ }
284
+ }
285
+
221
286
fn find_paths < T : SelectValue , F : FnMut ( & T ) -> bool > (
222
287
path : & str ,
223
288
doc : & T ,
0 commit comments