From 9fa668dab84c8c390526060aafc44d801e253553 Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Sun, 21 Jul 2019 01:23:40 +0300 Subject: [PATCH 1/2] 1. json_del: backward fix on json_del to support full key delete 2. json_set: error on creation of key with path!=root 3. mark commands as "write" and open keys with open_key() on readonly 4. set_value: add support for root set and returns error when path not found 5. get_doc: return error on wrong path --- src/lib.rs | 74 ++++++++++++++++++++++++++++++++++-------------- src/redisjson.rs | 54 ++++++++++++++++++++++------------- 2 files changed, 86 insertions(+), 42 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 309150d..16feaad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,7 +40,14 @@ fn json_del(ctx: &Context, args: Vec) -> RedisResult { let key = ctx.open_key_writable(&key); let deleted = match key.get_value::(&REDIS_JSON_TYPE)? { - Some(doc) => doc.delete_path(&path)?, + Some(doc) => { + if path == "$" { + key.delete()?; + 1 + } else { + doc.delete_path(&path)? + } + } None => 0, }; Ok(deleted.into()) @@ -74,12 +81,25 @@ fn json_set(ctx: &Context, args: Vec) -> RedisResult { (None, Some(SetOptions::AlreadyExists)) => Ok(().into()), (None, _) => { let doc = RedisJSON::from_str(&value)?; - key.set_value(&REDIS_JSON_TYPE, doc)?; - REDIS_OK + if path == "$" { + key.set_value(&REDIS_JSON_TYPE, doc)?; + REDIS_OK + } else { + Err("ERR new objects must be created at the root".into()) + } } } } +/// +/// JSON.GET +// [INDENT indentation-string] +// [NEWLINE line-break-string] +// [SPACE space-string] +// [NOESCAPE] +// [path ...] +/// +// TODO add support for multi path fn json_get(ctx: &Context, args: Vec) -> RedisResult { let mut args = args.into_iter().skip(1); @@ -111,6 +131,9 @@ fn json_get(ctx: &Context, args: Vec) -> RedisResult { Ok(value) } +/// +/// JSON.MGET [key ...] +/// fn json_mget(ctx: &Context, args: Vec) -> RedisResult { if args.len() < 3 { return Err(RedisError::WrongArity); @@ -119,7 +142,7 @@ fn json_mget(ctx: &Context, args: Vec) -> RedisResult { let path = backward_path(path.to_string()); let mut results: Vec = Vec::with_capacity(args.len() - 2); for key in &args[1..args.len() - 1] { - let redis_key = ctx.open_key_writable(&key); + let redis_key = ctx.open_key(&key); match redis_key.get_value::(&REDIS_JSON_TYPE)? { Some(doc) => { let result = doc.to_string(&path)?; @@ -139,7 +162,7 @@ fn json_str_len(ctx: &Context, args: Vec) -> RedisResult { let key = args.next_string()?; let path = backward_path(args.next_string()?); - let key = ctx.open_key_writable(&key); + let key = ctx.open_key(&key); let length = match key.get_value::(&REDIS_JSON_TYPE)? { Some(doc) => doc.str_len(&path)?.into(), @@ -154,7 +177,7 @@ fn json_type(ctx: &Context, args: Vec) -> RedisResult { let key = args.next_string()?; let path = backward_path(args.next_string()?); - let key = ctx.open_key_writable(&key); + let key = ctx.open_key(&key); let value = match key.get_value::(&REDIS_JSON_TYPE)? { Some(doc) => doc.get_type(&path)?.into(), @@ -214,6 +237,9 @@ fn json_arr_len(ctx: &Context, args: Vec) -> RedisResult { json_len(ctx, args, |doc, path| doc.arr_len(path)) } +/// +/// JSON.ARRPOP [path [index]] +/// fn json_arr_pop(_ctx: &Context, _args: Vec) -> RedisResult { Err("Command was not implemented".into()) } @@ -234,10 +260,6 @@ fn json_debug(_ctx: &Context, _args: Vec) -> RedisResult { Err("Command was not implemented".into()) } -fn json_forget(_ctx: &Context, _args: Vec) -> RedisResult { - Err("Command was not implemented".into()) -} - fn json_resp(_ctx: &Context, _args: Vec) -> RedisResult { Err("Command was not implemented".into()) } @@ -249,10 +271,9 @@ fn json_len Result>( ) -> RedisResult { let mut args = args.into_iter().skip(1); let key = args.next_string()?; - let path = args.next_string()?; - - let key = ctx.open_key_writable(&key); + let path = backward_path(args.next_string()?); + let key = ctx.open_key(&key); let length = match key.get_value::(&REDIS_JSON_TYPE)? { Some(doc) => fun(&doc, &path)?.into(), None => ().into(), @@ -261,6 +282,13 @@ fn json_len Result>( Ok(length) } +fn json_cache_info(_ctx: &Context, _args: Vec) -> RedisResult { + Err("Command was not implemented".into()) +} + +fn json_cache_init(_ctx: &Context, _args: Vec) -> RedisResult { + Err("Command was not implemented".into()) +} ////////////////////////////////////////////////////// redis_module! { @@ -275,21 +303,23 @@ redis_module! { ["json.mget", json_mget, ""], ["json.set", json_set, "write"], ["json.type", json_type, ""], - ["json.numincrby", json_num_incrby, ""], - ["json.nummultby", json_num_multby, ""], - ["json.numpowby", json_num_powby, ""], - ["json.strappend", json_str_append, ""], + ["json.numincrby", json_num_incrby, "write"], + ["json.nummultby", json_num_multby, "write"], + ["json.numpowby", json_num_powby, "write"], + ["json.strappend", json_str_append, "write"], ["json.strlen", json_str_len, ""], - ["json.arrappend", json_arr_append, ""], + ["json.arrappend", json_arr_append, "write"], ["json.arrindex", json_arr_index, ""], - ["json.arrinsert", json_arr_insert, ""], + ["json.arrinsert", json_arr_insert, "write"], ["json.arrlen", json_arr_len, ""], - ["json.arrpop", json_arr_pop, ""], - ["json.arrtrim", json_arr_trim, ""], + ["json.arrpop", json_arr_pop, "write"], + ["json.arrtrim", json_arr_trim, "write"], ["json.objkeys", json_obj_keys, ""], ["json.objlen", json_obj_len, ""], ["json.debug", json_debug, ""], - ["json.forget", json_forget, ""], + ["json.forget", json_del, "write"], ["json.resp", json_resp, ""], + ["json._cacheinfo", json_cache_info, ""], + ["json._cacheinit", json_cache_init, "write"], ], } diff --git a/src/redisjson.rs b/src/redisjson.rs index eaa8ced..e10a13b 100644 --- a/src/redisjson.rs +++ b/src/redisjson.rs @@ -60,11 +60,22 @@ impl RedisJSON { // Parse the string of data into serde_json::Value. let json: Value = serde_json::from_str(data)?; - let current_data = mem::replace(&mut self.data, Value::Null); - let new_data = jsonpath_lib::replace_with(current_data, path, &mut |_v| json.clone())?; - self.data = new_data; - - Ok(()) + if path == "$" { + self.data = json; + Ok(()) + } else { + let mut replaced = false; + let current_data = mem::replace(&mut self.data, Value::Null); + self.data = jsonpath_lib::replace_with(current_data, path, &mut |_v| { + replaced = true; + json.clone() + })?; + if replaced { + Ok(()) + } else { + Err(format!("ERR missing path {}", path).into()) + } + } } pub fn delete_path(&mut self, path: &str) -> Result { @@ -133,16 +144,16 @@ impl RedisJSON { let mut errors = vec![]; let mut result: f64 = 0.0; - self.data = jsonpath_lib::replace_with(current_data, path, &mut |v| { - match apply_op(v, number, &fun) { - Ok((res, new_value)) => { - result = res; - new_value - } - Err(e) => { - errors.push(e); - v.clone() - } + self.data = jsonpath_lib::replace_with(current_data, path, &mut |v| match apply_op( + v, number, &fun, + ) { + Ok((res, new_value)) => { + result = res; + new_value + } + Err(e) => { + errors.push(e); + v.clone() } })?; if errors.is_empty() { @@ -156,13 +167,15 @@ impl RedisJSON { let results = jsonpath_lib::select(&self.data, path)?; match results.first() { Some(s) => Ok(s), - None => Ok(&Value::Null), + None => Err("ERR path does not exist".into()), } } } fn apply_op(v: &Value, number: f64, fun: F) -> Result<(f64, Value), String> - where F: Fn(f64, f64) -> f64 { +where + F: Fn(f64, f64) -> f64, +{ if let Value::Number(curr) = v { if let Some(curr_value) = curr.as_f64() { let res = fun(curr_value, number); @@ -176,8 +189,9 @@ fn apply_op(v: &Value, number: f64, fun: F) -> Result<(f64, Value), String> Err("ERR can not convert current value as f64".to_string()) } } else { - Err(format!("ERR wrong type of path value - expected a number but found {}", - RedisJSON::value_name(&v))) + Err(format!( + "ERR wrong type of path value - expected a number but found {}", + RedisJSON::value_name(&v) + )) } } - From 2bce06317f3eb5b33380d4bffefa63220d77754b Mon Sep 17 00:00:00 2001 From: Guy Korland Date: Mon, 22 Jul 2019 17:09:55 +0300 Subject: [PATCH 2/2] fix comment --- src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 16feaad..01c1c67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,13 +93,13 @@ fn json_set(ctx: &Context, args: Vec) -> RedisResult { /// /// JSON.GET -// [INDENT indentation-string] -// [NEWLINE line-break-string] -// [SPACE space-string] -// [NOESCAPE] -// [path ...] +/// [INDENT indentation-string] +/// [NEWLINE line-break-string] +/// [SPACE space-string] +/// [NOESCAPE] +/// [path ...] /// -// TODO add support for multi path +/// TODO add support for multi path fn json_get(ctx: &Context, args: Vec) -> RedisResult { let mut args = args.into_iter().skip(1);