Skip to content
This repository was archived by the owner on Apr 20, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 52 additions & 22 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,14 @@ fn json_del(ctx: &Context, args: Vec<String>) -> RedisResult {

let key = ctx.open_key_writable(&key);
let deleted = match key.get_value::<RedisJSON>(&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())
Expand Down Expand Up @@ -74,12 +81,25 @@ fn json_set(ctx: &Context, args: Vec<String>) -> 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 <key>
/// [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<String>) -> RedisResult {
let mut args = args.into_iter().skip(1);

Expand Down Expand Up @@ -111,6 +131,9 @@ fn json_get(ctx: &Context, args: Vec<String>) -> RedisResult {
Ok(value)
}

///
/// JSON.MGET <key> [key ...] <path>
///
fn json_mget(ctx: &Context, args: Vec<String>) -> RedisResult {
if args.len() < 3 {
return Err(RedisError::WrongArity);
Expand All @@ -119,7 +142,7 @@ fn json_mget(ctx: &Context, args: Vec<String>) -> RedisResult {
let path = backward_path(path.to_string());
let mut results: Vec<String> = 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::<RedisJSON>(&REDIS_JSON_TYPE)? {
Some(doc) => {
let result = doc.to_string(&path)?;
Expand All @@ -139,7 +162,7 @@ fn json_str_len(ctx: &Context, args: Vec<String>) -> 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::<RedisJSON>(&REDIS_JSON_TYPE)? {
Some(doc) => doc.str_len(&path)?.into(),
Expand All @@ -154,7 +177,7 @@ fn json_type(ctx: &Context, args: Vec<String>) -> 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::<RedisJSON>(&REDIS_JSON_TYPE)? {
Some(doc) => doc.get_type(&path)?.into(),
Expand Down Expand Up @@ -214,6 +237,9 @@ fn json_arr_len(ctx: &Context, args: Vec<String>) -> RedisResult {
json_len(ctx, args, |doc, path| doc.arr_len(path))
}

///
/// JSON.ARRPOP <key> [path [index]]
///
fn json_arr_pop(_ctx: &Context, _args: Vec<String>) -> RedisResult {
Err("Command was not implemented".into())
}
Expand All @@ -234,10 +260,6 @@ fn json_debug(_ctx: &Context, _args: Vec<String>) -> RedisResult {
Err("Command was not implemented".into())
}

fn json_forget(_ctx: &Context, _args: Vec<String>) -> RedisResult {
Err("Command was not implemented".into())
}

fn json_resp(_ctx: &Context, _args: Vec<String>) -> RedisResult {
Err("Command was not implemented".into())
}
Expand All @@ -249,10 +271,9 @@ fn json_len<F: Fn(&RedisJSON, &String) -> Result<usize, Error>>(
) -> 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::<RedisJSON>(&REDIS_JSON_TYPE)? {
Some(doc) => fun(&doc, &path)?.into(),
None => ().into(),
Expand All @@ -261,6 +282,13 @@ fn json_len<F: Fn(&RedisJSON, &String) -> Result<usize, Error>>(
Ok(length)
}

fn json_cache_info(_ctx: &Context, _args: Vec<String>) -> RedisResult {
Err("Command was not implemented".into())
}

fn json_cache_init(_ctx: &Context, _args: Vec<String>) -> RedisResult {
Err("Command was not implemented".into())
}
//////////////////////////////////////////////////////

redis_module! {
Expand All @@ -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"],
],
}
54 changes: 34 additions & 20 deletions src/redisjson.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize, Error> {
Expand Down Expand Up @@ -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() {
Expand All @@ -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<F>(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);
Expand All @@ -176,8 +189,9 @@ fn apply_op<F>(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)
))
}
}