pub mod conf;
pub mod meta;
pub mod path;
pub use conf::{JsonArrIndex, JsonGetOptions, JsonNumberOp, JsonSet};
pub use meta::{JsonMeta, JsonStorageFormat, encode_json_value};
pub use path::{
FilterExpr, FilterOp, PathSegment, SliceIndex, delete_path_values, eval_slice, format_json,
get_path_values, json_merge_patch, mutate_path_values, parse_json_path,
};
use rapidhash::RapidHashMap;
use std::str;
use crate::db::WeDb;
use crate::error::{Error, Result};
use crate::key_composer::KeyComposer;
use sonic_rs::{JsonContainerTrait, JsonValueMutTrait, JsonValueTrait, Value};
use wedb_proto::RespValue;
pub fn json_set_path(root: &mut Value, path_str: &str, new_val: Value) -> Result<bool> {
let path_str = path_str.trim();
if path_str == "$" || path_str == "." || path_str.is_empty() {
*root = new_val;
return Ok(true);
}
let mutated = mutate_path_values(root, path_str, |node| {
*node = new_val.clone();
})?;
if mutated > 0 {
return Ok(true);
}
let segments = parse_json_path(path_str)?;
let mut field_names = Vec::new();
for seg in &segments {
match seg {
PathSegment::Root => {}
PathSegment::Field(name) => field_names.push(name.as_str()),
_ => {
return Err(Error::invalid_data(
"Target path does not exist and cannot be created automatically",
));
}
}
}
if field_names.is_empty() {
*root = new_val;
return Ok(true);
}
let mut cur = root;
for (i, &seg) in field_names.iter().enumerate() {
if i == field_names.len() - 1 {
if let Some(obj) = cur.as_object_mut() {
obj.insert(seg, new_val);
return Ok(true);
}
return Err(Error::invalid_data(
"Target parent is not a JSON object to insert into",
));
} else {
if !cur.is_object() {
*cur = sonic_rs::json!({});
}
let obj = cur.as_object_mut().unwrap();
if !obj.contains_key(&seg) {
obj.insert(seg, sonic_rs::json!({}));
}
cur = obj.get_mut(&seg).unwrap();
}
}
Ok(true)
}
pub fn json_get_path<'a>(root: &'a Value, path_str: &str) -> Option<&'a Value> {
let values = get_path_values(root, path_str).ok()?;
values.into_iter().next()
}
pub fn json_path_query<'a>(root: &'a Value, path: &str) -> Vec<&'a Value> {
get_path_values(root, path).unwrap_or_default()
}
pub fn json_to_resp(val: &Value) -> RespValue {
if val.is_null() {
RespValue::Null
} else if let Some(b) = val.as_bool() {
RespValue::Bool(b)
} else if let Some(i) = val.as_i64() {
RespValue::Int(i)
} else if let Some(f) = val.as_f64() {
RespValue::Float(f)
} else if let Some(s) = val.as_str() {
RespValue::Blob(s.as_bytes().to_vec())
} else if let Some(arr) = val.as_array() {
let elements = arr.iter().map(json_to_resp).collect();
RespValue::Arr(elements)
} else if let Some(obj) = val.as_object() {
let mut map = Vec::with_capacity(obj.len());
for (k, v) in obj.iter() {
map.push((RespValue::Blob(k.as_bytes().to_vec()), json_to_resp(v)));
}
RespValue::Map(map)
} else {
RespValue::Null
}
}
pub fn json_to_resp_flat(val: &Value) -> RespValue {
if val.is_null() {
RespValue::Null
} else if let Some(b) = val.as_bool() {
RespValue::Simple(if b {
"true".to_string()
} else {
"false".to_string()
})
} else if let Some(i) = val.as_i64() {
RespValue::Int(i)
} else if let Some(u) = val.as_u64() {
RespValue::Int(u as i64)
} else if let Some(f) = val.as_f64() {
RespValue::Blob(f.to_string().into_bytes())
} else if let Some(s) = val.as_str() {
RespValue::Blob(s.as_bytes().to_vec())
} else if let Some(arr) = val.as_array() {
let mut elements = Vec::with_capacity(arr.len() + 1);
elements.push(RespValue::Simple("[".to_string()));
for item in arr.iter() {
elements.push(json_to_resp_flat(item));
}
RespValue::Arr(elements)
} else if let Some(obj) = val.as_object() {
let mut elements = Vec::with_capacity(obj.len() * 2 + 1);
elements.push(RespValue::Simple("{".to_string()));
for (k, v) in obj.iter() {
elements.push(RespValue::Blob(k.as_bytes().to_vec()));
elements.push(json_to_resp_flat(v));
}
RespValue::Arr(elements)
} else {
RespValue::Null
}
}
impl WeDb {
fn read_json_meta_and_val<K: AsRef<[u8]>>(&self, key: K) -> Result<Option<(JsonMeta, Value)>> {
let kc = KeyComposer::new("default");
let k_str = str::from_utf8(key.as_ref()).unwrap_or("");
let raw_k = kc.raw_key(k_str);
let raw_bytes = match self.data_ks.get(raw_k.as_bytes())? {
Some(v) => v,
None => return Ok(None),
};
let now_ms = coarsetime::Clock::now_since_epoch().as_millis();
let (meta, payload) = match JsonMeta::decode(&raw_bytes) {
Some((m, p)) => {
if m.is_expired(now_ms) {
let _ = self.data_ks.remove(raw_k.as_bytes());
return Ok(None);
}
(m, p)
}
None => return Ok(None),
};
let val: Value = sonic_rs::from_slice(payload)
.map_err(|e| Error::invalid_data(format!("ERR corrupted JSON: {e}")))?;
Ok(Some((meta, val)))
}
fn write_json_meta_and_val<K: AsRef<[u8]>>(
&self,
key: K,
meta: &JsonMeta,
val: &Value,
) -> Result<()> {
let kc = KeyComposer::new("default");
let k_str = str::from_utf8(key.as_ref()).unwrap_or("");
let raw_k = kc.raw_key(k_str);
let payload = sonic_rs::to_vec(val)
.map_err(|e| Error::invalid_data(format!("ERR JSON serialize: {e}")))?;
let mut updated_meta = *meta;
updated_meta.base.size = payload.len() as u64;
let full_encoded = encode_json_value(&updated_meta, &payload);
self.data_ks.insert(raw_k.as_bytes(), full_encoded)?;
Ok(())
}
pub fn json_set<K: AsRef<[u8]>>(&self, key: K, path: &str, json_str: &str) -> Result<bool> {
self.json_set_opt(key, path, json_str, None)
}
pub fn json_set_opt<K: AsRef<[u8]>>(
&self,
key: K,
path: &str,
json_str: &str,
opt: Option<JsonSet>,
) -> Result<bool> {
let parsed_new_val: Value = sonic_rs::from_str(json_str)
.map_err(|e| Error::invalid_data(format!("ERR invalid JSON: {e}")))?;
let existing = self.read_json_meta_and_val(key.as_ref())?;
let key_exists = existing.is_some();
if let Some(option) = opt {
match option {
JsonSet::Nx if key_exists => return Ok(false),
JsonSet::Xx if !key_exists => return Ok(false),
_ => {}
}
}
match existing {
Some((meta, mut root_val)) => {
json_set_path(&mut root_val, path, parsed_new_val)?;
self.write_json_meta_and_val(key, &meta, &root_val)?;
Ok(true)
}
None => {
let p = path.trim();
if p != "$" && p != "." && !p.is_empty() {
return Err(Error::invalid_data(
"new objects must be created at the root",
));
}
let meta = JsonMeta::new_with_version(0, json_str.len() as u64);
self.write_json_meta_and_val(key, &meta, &parsed_new_val)?;
Ok(true)
}
}
}
pub fn json_get<K: AsRef<[u8]>>(&self, key: K, path: Option<&str>) -> Result<Option<String>> {
let paths: Vec<&str> = match path {
Some(p) => vec![p],
None => vec!["$"],
};
self.json_get_formatted(key, &paths, None, None, None)
}
pub fn json_get_formatted<K: AsRef<[u8]>>(
&self,
key: K,
paths: &[&str],
indent: Option<&str>,
newline: Option<&str>,
space: Option<&str>,
) -> Result<Option<String>> {
let (_meta, root_val) = match self.read_json_meta_and_val(key)? {
Some(pair) => pair,
None => return Ok(None),
};
if paths.is_empty() || (paths.len() == 1 && (paths[0] == "$" || paths[0] == ".")) {
let out = format_json(&root_val, indent, newline, space);
return Ok(Some(out));
}
if paths.len() == 1 {
let path_str = paths[0];
let nodes = get_path_values(&root_val, path_str)?;
if nodes.is_empty() {
if path_str == "$" || path_str == "." {
let out = format_json(&root_val, indent, newline, space);
return Ok(Some(out));
}
return Ok(None);
}
if nodes.len() == 1 {
let out = format_json(nodes[0], indent, newline, space);
return Ok(Some(out));
}
let arr_val: Value = nodes.into_iter().cloned().collect();
let out = format_json(&arr_val, indent, newline, space);
return Ok(Some(out));
}
let mut result_obj = sonic_rs::json!({});
let obj_mut = result_obj.as_object_mut().unwrap();
for &p in paths {
let nodes = get_path_values(&root_val, p)?;
let arr_val: Value = nodes.into_iter().cloned().collect();
obj_mut.insert(p, arr_val);
}
let out = format_json(&result_obj, indent, newline, space);
Ok(Some(out))
}
pub fn json_mget<K: AsRef<[u8]>>(&self, keys: &[K], path: &str) -> Result<Vec<Option<String>>> {
let mut results = Vec::with_capacity(keys.len());
for k in keys {
let res = match self.read_json_meta_and_val(k)? {
Some((_meta, root_val)) => {
let nodes = get_path_values(&root_val, path)?;
if nodes.is_empty() {
if path == "$" || path == "." {
Some(sonic_rs::to_string(&root_val).unwrap_or_default())
} else {
None
}
} else if nodes.len() == 1 {
Some(sonic_rs::to_string(nodes[0]).unwrap_or_default())
} else {
Some(sonic_rs::to_string(&nodes).unwrap_or_default())
}
}
None => None,
};
results.push(res);
}
Ok(results)
}
pub fn json_mset<K: AsRef<[u8]>>(&self, triplets: &[(K, &str, &str)]) -> Result<()> {
let mut dirty_keys: RapidHashMap<String, (Value, JsonMeta)> = RapidHashMap::default();
for (k, path, val_str) in triplets {
let k_str = str::from_utf8(k.as_ref()).unwrap_or("").to_string();
let parsed_new_val: Value = sonic_rs::from_str(val_str)
.map_err(|e| Error::invalid_data(format!("ERR invalid JSON: {e}")))?;
if let Some((existing_val, _meta)) = dirty_keys.get_mut(&k_str) {
json_set_path(existing_val, path, parsed_new_val)?;
} else {
match self.read_json_meta_and_val(k.as_ref())? {
Some((meta, mut val)) => {
json_set_path(&mut val, path, parsed_new_val)?;
dirty_keys.insert(k_str, (val, meta));
}
None => {
let p = path.trim();
if p != "$" && p != "." && !p.is_empty() {
return Err(Error::invalid_data(
"new objects must be created at the root",
));
}
let meta = JsonMeta::new_with_version(0, val_str.len() as u64);
dirty_keys.insert(k_str, (parsed_new_val, meta));
}
}
}
}
for (k_str, (val, meta)) in dirty_keys {
self.write_json_meta_and_val(k_str.as_bytes(), &meta, &val)?;
}
Ok(())
}
pub fn json_del<K: AsRef<[u8]>>(&self, key: K, path: Option<&str>) -> Result<usize> {
let kc = KeyComposer::new("default");
let k_str = str::from_utf8(key.as_ref()).unwrap_or("");
let raw_k = kc.raw_key(k_str);
if path.is_none() || path == Some("$") || path == Some(".") {
let exists = self.data_ks.contains_key(raw_k.as_bytes())?;
if exists {
self.data_ks.remove(raw_k.as_bytes())?;
return Ok(1);
}
return Ok(0);
}
let (meta, mut root_val) = match self.read_json_meta_and_val(key.as_ref())? {
Some(pair) => pair,
None => return Ok(0),
};
let p = path.unwrap().trim();
let deleted = delete_path_values(&mut root_val, p)?;
if deleted > 0 {
self.write_json_meta_and_val(key, &meta, &root_val)?;
}
Ok(deleted)
}
pub fn json_type<K: AsRef<[u8]>>(&self, key: K, path: Option<&str>) -> Result<Vec<String>> {
let (_meta, root_val) = match self.read_json_meta_and_val(key)? {
Some(pair) => pair,
None => return Ok(Vec::new()),
};
let p = path.unwrap_or("$");
let nodes = get_path_values(&root_val, p)?;
let mut types = Vec::with_capacity(nodes.len());
for n in nodes {
let t = if n.is_null() {
"null"
} else if n.is_boolean() {
"boolean"
} else if n.is_i64() || n.is_u64() {
"integer"
} else if n.is_f64() {
"number"
} else if n.is_str() {
"string"
} else if n.is_array() {
"array"
} else if n.is_object() {
"object"
} else {
"unknown"
};
types.push(t.to_string());
}
Ok(types)
}
pub fn json_numincrby<K: AsRef<[u8]>>(
&self,
key: K,
path: &str,
num_str: &str,
) -> Result<Option<String>> {
let delta: f64 = num_str
.parse()
.map_err(|e| Error::invalid_data(format!("ERR invalid number: {e}")))?;
let (meta, mut root_val) = match self.read_json_meta_and_val(key.as_ref())? {
Some(pair) => pair,
None => return Ok(None),
};
let mut res_values = Vec::new();
let mut has_err = false;
mutate_path_values(&mut root_val, path, |node| {
if has_err {
return;
}
if let Some(i) = node.as_i64() {
let new_v = (i as f64) + delta;
if new_v.is_infinite() || new_v.is_nan() {
has_err = true;
return;
}
if new_v.fract() == 0.0 && new_v >= (i64::MIN as f64) && new_v <= (i64::MAX as f64)
{
*node = sonic_rs::json!(new_v as i64);
res_values.push(sonic_rs::json!(new_v as i64));
} else {
*node = sonic_rs::json!(new_v);
res_values.push(sonic_rs::json!(new_v));
}
} else if let Some(f) = node.as_f64() {
let new_v = f + delta;
if new_v.is_infinite() || new_v.is_nan() {
has_err = true;
return;
}
if new_v.fract() == 0.0 && new_v >= (i64::MIN as f64) && new_v <= (i64::MAX as f64)
{
*node = sonic_rs::json!(new_v as i64);
res_values.push(sonic_rs::json!(new_v as i64));
} else {
*node = sonic_rs::json!(new_v);
res_values.push(sonic_rs::json!(new_v));
}
} else {
res_values.push(sonic_rs::json!(null));
}
})?;
if has_err {
return Err(Error::invalid_data("the result is an infinite number"));
}
if !res_values.is_empty() {
self.write_json_meta_and_val(key, &meta, &root_val)?;
}
let out = sonic_rs::to_string(&res_values).unwrap_or_default();
Ok(Some(out))
}
pub fn json_nummultby<K: AsRef<[u8]>>(
&self,
key: K,
path: &str,
num_str: &str,
) -> Result<Option<String>> {
let mult: f64 = num_str
.parse()
.map_err(|e| Error::invalid_data(format!("ERR invalid number: {e}")))?;
let (meta, mut root_val) = match self.read_json_meta_and_val(key.as_ref())? {
Some(pair) => pair,
None => return Ok(None),
};
let mut res_values = Vec::new();
let mut has_err = false;
mutate_path_values(&mut root_val, path, |node| {
if has_err {
return;
}
if let Some(i) = node.as_i64() {
let new_v = (i as f64) * mult;
if new_v.is_infinite() || new_v.is_nan() {
has_err = true;
return;
}
if new_v.fract() == 0.0 && new_v >= (i64::MIN as f64) && new_v <= (i64::MAX as f64)
{
*node = sonic_rs::json!(new_v as i64);
res_values.push(sonic_rs::json!(new_v as i64));
} else {
*node = sonic_rs::json!(new_v);
res_values.push(sonic_rs::json!(new_v));
}
} else if let Some(f) = node.as_f64() {
let new_v = f * mult;
if new_v.is_infinite() || new_v.is_nan() {
has_err = true;
return;
}
if new_v.fract() == 0.0 && new_v >= (i64::MIN as f64) && new_v <= (i64::MAX as f64)
{
*node = sonic_rs::json!(new_v as i64);
res_values.push(sonic_rs::json!(new_v as i64));
} else {
*node = sonic_rs::json!(new_v);
res_values.push(sonic_rs::json!(new_v));
}
} else {
res_values.push(sonic_rs::json!(null));
}
})?;
if has_err {
return Err(Error::invalid_data("the result is an infinite number"));
}
if !res_values.is_empty() {
self.write_json_meta_and_val(key, &meta, &root_val)?;
}
let out = sonic_rs::to_string(&res_values).unwrap_or_default();
Ok(Some(out))
}
pub fn json_strappend<K: AsRef<[u8]>>(
&self,
key: K,
path: Option<&str>,
str_to_append: &str,
) -> Result<Vec<Option<usize>>> {
let (meta, mut root_val) = match self.read_json_meta_and_val(key.as_ref())? {
Some(pair) => pair,
None => return Ok(Vec::new()),
};
let append_str = if (str_to_append.starts_with('"') && str_to_append.ends_with('"'))
|| (str_to_append.starts_with('\'') && str_to_append.ends_with('\''))
{
if let Ok(parsed) = sonic_rs::from_str::<String>(str_to_append) {
parsed
} else {
str_to_append.to_string()
}
} else {
str_to_append.to_string()
};
let p = path.unwrap_or("$");
let mut lengths = Vec::new();
mutate_path_values(&mut root_val, p, |node| {
if let Some(s) = node.as_str() {
let new_str = format!("{s}{append_str}");
let len = new_str.len();
*node = sonic_rs::json!(new_str);
lengths.push(Some(len));
} else {
lengths.push(None);
}
})?;
if lengths.iter().any(|opt| opt.is_some()) {
self.write_json_meta_and_val(key, &meta, &root_val)?;
}
Ok(lengths)
}
pub fn json_strlen<K: AsRef<[u8]>>(
&self,
key: K,
path: Option<&str>,
) -> Result<Vec<Option<usize>>> {
let (_meta, root_val) = match self.read_json_meta_and_val(key)? {
Some(pair) => pair,
None => return Ok(Vec::new()),
};
let p = path.unwrap_or("$");
let nodes = get_path_values(&root_val, p)?;
let mut lengths = Vec::with_capacity(nodes.len());
for n in nodes {
if let Some(s) = n.as_str() {
lengths.push(Some(s.len()));
} else {
lengths.push(None);
}
}
Ok(lengths)
}
pub fn json_arrappend<K: AsRef<[u8]>>(
&self,
key: K,
path: &str,
values_json: &[&str],
) -> Result<Vec<Option<usize>>> {
let mut parsed_vals = Vec::with_capacity(values_json.len());
for &s in values_json {
let v: Value = sonic_rs::from_str(s)
.map_err(|e| Error::invalid_data(format!("ERR invalid JSON value: {e}")))?;
parsed_vals.push(v);
}
let (meta, mut root_val) = match self.read_json_meta_and_val(key.as_ref())? {
Some(pair) => pair,
None => return Ok(Vec::new()),
};
let mut lengths = Vec::new();
mutate_path_values(&mut root_val, path, |node| {
if let Some(arr) = node.as_array_mut() {
for item in &parsed_vals {
arr.push(item.clone());
}
lengths.push(Some(arr.len()));
} else {
lengths.push(None);
}
})?;
if lengths.iter().any(|opt| opt.is_some()) {
self.write_json_meta_and_val(key, &meta, &root_val)?;
}
Ok(lengths)
}
pub fn json_arrinsert<K: AsRef<[u8]>>(
&self,
key: K,
path: &str,
index: isize,
values_json: &[&str],
) -> Result<Vec<Option<usize>>> {
let mut parsed_vals = Vec::with_capacity(values_json.len());
for &s in values_json {
let v: Value = sonic_rs::from_str(s)
.map_err(|e| Error::invalid_data(format!("ERR invalid JSON value: {e}")))?;
parsed_vals.push(v);
}
let (meta, mut root_val) = match self.read_json_meta_and_val(key.as_ref())? {
Some(pair) => pair,
None => return Ok(Vec::new()),
};
let mut lengths = Vec::new();
mutate_path_values(&mut root_val, path, |node| {
if let Some(arr) = node.as_array_mut() {
let len = arr.len() as isize;
if index > len || index < -len {
lengths.push(None);
return;
}
let pos = if index >= 0 {
index as usize
} else {
(len + index) as usize
};
for (offset, item) in parsed_vals.iter().enumerate() {
arr.insert(pos + offset, item.clone());
}
lengths.push(Some(arr.len()));
} else {
lengths.push(None);
}
})?;
if lengths.iter().any(|opt| opt.is_some()) {
self.write_json_meta_and_val(key, &meta, &root_val)?;
}
Ok(lengths)
}
pub fn json_arrindex<K: AsRef<[u8]>>(
&self,
key: K,
path: &str,
needle_json: &str,
start: isize,
stop: Option<isize>,
) -> Result<Vec<Option<isize>>> {
let needle: Value = sonic_rs::from_str(needle_json)
.map_err(|e| Error::invalid_data(format!("ERR invalid JSON needle: {e}")))?;
let (_meta, root_val) = match self.read_json_meta_and_val(key)? {
Some(pair) => pair,
None => return Ok(Vec::new()),
};
let nodes = get_path_values(&root_val, path)?;
let mut results = Vec::with_capacity(nodes.len());
for n in nodes {
if let Some(arr) = n.as_array() {
let len = arr.len() as isize;
if len == 0 {
results.push(Some(-1));
continue;
}
let s = if start < 0 {
(len + start).max(0)
} else {
start.min(len.saturating_sub(1))
};
let e = match stop {
Some(0) | None => len,
Some(v) if v < 0 => (len + v).max(0).min(len),
Some(v) => v.min(len),
};
if s >= e {
results.push(Some(-1));
continue;
}
let mut found = -1;
for i in s..e {
if (i as usize) < arr.len() && arr[i as usize] == needle {
found = i;
break;
}
}
results.push(Some(found));
} else {
results.push(None);
}
}
Ok(results)
}
pub fn json_arrlen<K: AsRef<[u8]>>(
&self,
key: K,
path: Option<&str>,
) -> Result<Vec<Option<usize>>> {
let (_meta, root_val) = match self.read_json_meta_and_val(key)? {
Some(pair) => pair,
None => return Ok(Vec::new()),
};
let p = path.unwrap_or("$");
let nodes = get_path_values(&root_val, p)?;
let mut lengths = Vec::with_capacity(nodes.len());
for n in nodes {
if let Some(arr) = n.as_array() {
lengths.push(Some(arr.len()));
} else {
lengths.push(None);
}
}
Ok(lengths)
}
pub fn json_arrpop<K: AsRef<[u8]>>(
&self,
key: K,
path: Option<&str>,
index: Option<isize>,
) -> Result<Vec<Option<String>>> {
let (meta, mut root_val) = match self.read_json_meta_and_val(key.as_ref())? {
Some(pair) => pair,
None => return Ok(Vec::new()),
};
let p = path.unwrap_or("$");
let idx = index.unwrap_or(-1);
let mut popped = Vec::new();
mutate_path_values(&mut root_val, p, |node| {
if let Some(arr) = node.as_array_mut() {
if arr.is_empty() {
popped.push(None);
} else {
let len = arr.len() as isize;
let target_i = if idx < 0 {
len - len.min(-idx)
} else {
(len - 1).min(idx)
} as usize;
if target_i < arr.len() {
let removed = arr[target_i].clone();
arr.remove(target_i);
popped.push(Some(sonic_rs::to_string(&removed).unwrap_or_default()));
} else {
popped.push(None);
}
}
} else {
popped.push(None);
}
})?;
if popped.iter().any(|opt| opt.is_some()) {
self.write_json_meta_and_val(key, &meta, &root_val)?;
}
Ok(popped)
}
pub fn json_arrtrim<K: AsRef<[u8]>>(
&self,
key: K,
path: &str,
start: isize,
stop: isize,
) -> Result<Vec<Option<usize>>> {
let (meta, mut root_val) = match self.read_json_meta_and_val(key.as_ref())? {
Some(pair) => pair,
None => return Ok(Vec::new()),
};
let mut lengths = Vec::new();
mutate_path_values(&mut root_val, path, |node| {
if let Some(arr) = node.as_array_mut() {
let len = arr.len() as isize;
let begin_index = if start < 0 {
(len + start).max(0)
} else {
start.min(len)
} as usize;
let end_index = (if stop < 0 {
(len + stop).max(0)
} else {
stop.min(len - 1)
}) as usize;
if begin_index >= arr.len() || begin_index > end_index {
arr.clear();
lengths.push(Some(0));
} else {
let end = (end_index + 1).min(arr.len());
arr.truncate(end);
if begin_index > 0 {
arr.drain(0..begin_index);
}
lengths.push(Some(arr.len()));
}
} else {
lengths.push(None);
}
})?;
if lengths.iter().any(|opt| opt.is_some()) {
self.write_json_meta_and_val(key, &meta, &root_val)?;
}
Ok(lengths)
}
pub fn json_clear<K: AsRef<[u8]>>(&self, key: K, path: Option<&str>) -> Result<usize> {
let (meta, mut root_val) = match self.read_json_meta_and_val(key.as_ref())? {
Some(pair) => pair,
None => return Ok(0),
};
let p = path.unwrap_or("$");
let mut cleared = 0;
mutate_path_values(&mut root_val, p, |node| {
if let Some(arr) = node.as_array_mut()
&& !arr.is_empty()
{
arr.clear();
cleared += 1;
} else if let Some(obj) = node.as_object_mut()
&& !obj.is_empty()
{
obj.clear();
cleared += 1;
} else if let Some(f) = node.as_f64()
&& f != 0.0
{
*node = sonic_rs::json!(0);
cleared += 1;
} else if let Some(i) = node.as_i64()
&& i != 0
{
*node = sonic_rs::json!(0);
cleared += 1;
}
})?;
if cleared > 0 {
self.write_json_meta_and_val(key, &meta, &root_val)?;
}
Ok(cleared)
}
pub fn json_toggle<K: AsRef<[u8]>>(
&self,
key: K,
path: Option<&str>,
) -> Result<Vec<Option<bool>>> {
let (meta, mut root_val) = match self.read_json_meta_and_val(key.as_ref())? {
Some(pair) => pair,
None => return Ok(Vec::new()),
};
let p = path.unwrap_or("$");
let mut toggled = Vec::new();
mutate_path_values(&mut root_val, p, |node| {
if let Some(b) = node.as_bool() {
let new_b = !b;
*node = sonic_rs::json!(new_b);
toggled.push(Some(new_b));
} else {
toggled.push(None);
}
})?;
if toggled.iter().any(|opt| opt.is_some()) {
self.write_json_meta_and_val(key, &meta, &root_val)?;
}
Ok(toggled)
}
pub fn json_merge<K: AsRef<[u8]>>(&self, key: K, path: &str, patch_json: &str) -> Result<bool> {
let patch_val: Value = sonic_rs::from_str(patch_json)
.map_err(|e| Error::invalid_data(format!("ERR invalid JSON patch: {e}")))?;
match self.read_json_meta_and_val(key.as_ref())? {
Some((meta, mut root_val)) => {
if path == "$" || path == "." || path.is_empty() {
json_merge_patch(&mut root_val, &patch_val);
} else if patch_val.is_null() {
delete_path_values(&mut root_val, path)?;
} else {
mutate_path_values(&mut root_val, path, |node| {
json_merge_patch(node, &patch_val);
})?;
}
self.write_json_meta_and_val(key, &meta, &root_val)?;
Ok(true)
}
None => {
let p = path.trim();
if p != "$" && p != "." && !p.is_empty() {
return Err(Error::invalid_data(
"new objects must be created at the root",
));
}
let mut root = sonic_rs::json!({});
json_merge_patch(&mut root, &patch_val);
let meta = JsonMeta::new_with_version(0, patch_json.len() as u64);
self.write_json_meta_and_val(key, &meta, &root)?;
Ok(true)
}
}
}
pub fn json_objkeys<K: AsRef<[u8]>>(
&self,
key: K,
path: Option<&str>,
) -> Result<Vec<Option<Vec<String>>>> {
let (_meta, root_val) = match self.read_json_meta_and_val(key)? {
Some(pair) => pair,
None => return Ok(Vec::new()),
};
let p = path.unwrap_or("$");
let nodes = get_path_values(&root_val, p)?;
let mut keys_list = Vec::with_capacity(nodes.len());
for n in nodes {
if let Some(obj) = n.as_object() {
let keys: Vec<String> = obj.iter().map(|(k, _)| k.to_string()).collect();
keys_list.push(Some(keys));
} else {
keys_list.push(None);
}
}
Ok(keys_list)
}
pub fn json_objlen<K: AsRef<[u8]>>(
&self,
key: K,
path: Option<&str>,
) -> Result<Vec<Option<usize>>> {
let (_meta, root_val) = match self.read_json_meta_and_val(key)? {
Some(pair) => pair,
None => return Ok(Vec::new()),
};
let p = path.unwrap_or("$");
let nodes = get_path_values(&root_val, p)?;
let mut lengths = Vec::with_capacity(nodes.len());
for n in nodes {
if let Some(obj) = n.as_object() {
lengths.push(Some(obj.len()));
} else {
lengths.push(None);
}
}
Ok(lengths)
}
pub fn json_debug_memory<K: AsRef<[u8]>>(
&self,
key: K,
path: Option<&str>,
) -> Result<Vec<usize>> {
let (meta, root_val) = match self.read_json_meta_and_val(key.as_ref())? {
Some(pair) => pair,
None => return Ok(Vec::new()),
};
let p = path.unwrap_or("$");
if p == "$" || p == "." {
return Ok(vec![meta.base.size as usize]);
}
let nodes = get_path_values(&root_val, p)?;
let mut sizes = Vec::with_capacity(nodes.len());
for n in nodes {
let s = sonic_rs::to_vec(n).unwrap_or_default();
sizes.push(s.len());
}
Ok(sizes)
}
pub fn json_resp<K: AsRef<[u8]>>(
&self,
key: K,
path: Option<&str>,
) -> Result<Option<RespValue>> {
let (_meta, root_val) = match self.read_json_meta_and_val(key)? {
Some(pair) => pair,
None => return Ok(None),
};
let p = path.unwrap_or("$");
let nodes = get_path_values(&root_val, p)?;
if nodes.is_empty() {
return Ok(None);
}
if nodes.len() == 1 {
return Ok(Some(json_to_resp_flat(nodes[0])));
}
let elements = nodes.into_iter().map(json_to_resp_flat).collect();
Ok(Some(RespValue::Arr(elements)))
}
pub fn json_info<K: AsRef<[u8]>>(&self, key: K) -> Result<Option<(JsonStorageFormat, usize)>> {
if let Some((meta, _val)) = self.read_json_meta_and_val(key)? {
Ok(Some((meta.format, meta.base.size as usize)))
} else {
Ok(None)
}
}
}