wedb_embed 0.1.12

Embedded database engine providing Redis-like APIs, built on fjall / 嵌入式数据库引擎,提供类似 Redis 的接口,底层基于 fjall 开发
Documentation
use sonic_rs::{JsonContainerTrait, JsonValueMutTrait, Value};

use super::{
  r#const::*,
  r#impl::{read_json_meta_and_val, write_json_meta_and_val},
  mutate::extract_node_lengths,
  opt::JsonArrIndex,
  path::{get_path_values, mutate_path_values},
};
use crate::{
  engine::Engine,
  error::{Error, Result},
  wedb::Db,
};

/// Parses a slice of JSON strings into Value array.
#[inline]
pub(crate) fn parse_json_values(values_json: &[&str]) -> Result<Vec<Value>> {
  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);
  }
  Ok(parsed_vals)
}

/// JSON array operations interface (JSON.ARRAPPEND, JSON.ARRINSERT, etc.).
/// JSON 数组操作接口实现
impl<E: Engine> Db<E>
where
  Error: From<E::Error>,
{
  #[inline]
  pub fn json_arrappend<K: AsRef<[u8]>>(
    &self,
    key: K,
    path: &str,
    values_json: &[&str],
  ) -> Result<Vec<Option<usize>>> {
    let kc = self.kc();
    let parsed_vals = parse_json_values(values_json)?;

    let (meta, mut root_val) = match read_json_meta_and_val(self, &kc, 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()) {
      write_json_meta_and_val(self, &kc, key, &meta, &root_val)?;
    }

    Ok(lengths)
  }

  #[inline]
  pub fn json_arrinsert<K: AsRef<[u8]>>(
    &self,
    key: K,
    path: &str,
    index: isize,
    values_json: &[&str],
  ) -> Result<Vec<Option<usize>>> {
    let kc = self.kc();
    let parsed_vals = parse_json_values(values_json)?;

    let (meta, mut root_val) = match read_json_meta_and_val(self, &kc, 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()) {
      write_json_meta_and_val(self, &kc, key, &meta, &root_val)?;
    }

    Ok(lengths)
  }

  #[inline]
  pub fn json_arrindex<K: AsRef<[u8]>>(
    &self,
    key: K,
    path: &str,
    needle_json: &str,
    opt_li: impl IntoIterator<Item = JsonArrIndex>,
  ) -> Result<Vec<Option<isize>>> {
    let mut start = 0isize;
    let mut stop = None;
    for opt in opt_li {
      match opt {
        JsonArrIndex::Start(s) => start = s,
        JsonArrIndex::Stop(e) => stop = Some(e),
        JsonArrIndex::Range(s, e) => {
          start = s;
          stop = Some(e);
        }
      }
    }
    self.json_arrindex_internal(key, path, needle_json, start, stop)
  }

  #[inline]
  pub(crate) fn json_arrindex_internal<K: AsRef<[u8]>>(
    &self,
    key: K,
    path: &str,
    needle_json: &str,
    start: isize,
    stop: Option<isize>,
  ) -> Result<Vec<Option<isize>>> {
    let kc = self.kc();
    let needle: Value = sonic_rs::from_str(needle_json)
      .map_err(|e| Error::invalid_data(format!("{ERR_INVALID_JSON_NEEDLE}: {e}")))?;

    let (_, root_val) = match read_json_meta_and_val(self, &kc, 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)
  }

  #[inline]
  pub fn json_arrlen<K: AsRef<[u8]>>(
    &self,
    key: K,
    path: Option<&str>,
  ) -> Result<Vec<Option<usize>>> {
    let kc = self.kc();
    let (_, root_val) = match read_json_meta_and_val(self, &kc, key)? {
      Some(pair) => pair,
      None => return Ok(Vec::new()),
    };

    let p = path.unwrap_or(JSON_ROOT_PATH);
    let nodes = get_path_values(&root_val, p)?;
    Ok(extract_node_lengths(&nodes, |n| {
      n.as_array().map(|a| a.len())
    }))
  }

  #[inline]
  pub fn json_arrpop<K: AsRef<[u8]>>(
    &self,
    key: K,
    path: Option<&str>,
    index: Option<isize>,
  ) -> Result<Vec<Option<String>>> {
    let kc = self.kc();
    let (meta, mut root_val) = match read_json_meta_and_val(self, &kc, key.as_ref())? {
      Some(pair) => pair,
      None => return Ok(Vec::new()),
    };

    let p = path.unwrap_or(JSON_ROOT_PATH);
    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()) {
      write_json_meta_and_val(self, &kc, key, &meta, &root_val)?;
    }

    Ok(popped)
  }

  #[inline]
  pub fn json_arrtrim<K: AsRef<[u8]>>(
    &self,
    key: K,
    path: &str,
    start: isize,
    stop: isize,
  ) -> Result<Vec<Option<usize>>> {
    let kc = self.kc();
    let (meta, mut root_val) = match read_json_meta_and_val(self, &kc, 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
        };
        let end_index = if stop < 0 {
          (len + stop).max(0)
        } else {
          stop.min(len - 1)
        };

        if begin_index >= len || begin_index > end_index {
          arr.clear();
          lengths.push(Some(0));
        } else {
          let b = begin_index as usize;
          let e = ((end_index + 1) as usize).min(arr.len());
          arr.truncate(e);
          if b > 0 {
            arr.drain(0..b);
          }
          lengths.push(Some(arr.len()));
        }
      } else {
        lengths.push(None);
      }
    })?;

    if lengths.iter().any(|opt| opt.is_some()) {
      write_json_meta_and_val(self, &kc, key, &meta, &root_val)?;
    }

    Ok(lengths)
  }
}