Skip to content

Commit e21b273

Browse files
authored
Merge pull request #44 from akoshchiy/11270-json-delete
feat: add delete_by_index & delete_by_name
2 parents a0669bf + 74e7738 commit e21b273

File tree

5 files changed

+233
-6
lines changed

5 files changed

+233
-6
lines changed

src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ pub enum Error {
8383
InvalidJsonPathPredicate,
8484
InvalidKeyPath,
8585

86+
InvalidJsonType,
87+
8688
Syntax(ParseErrorCode, usize),
8789
}
8890

src/functions.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2015,6 +2015,104 @@ fn concat_jsonb(left: &[u8], right: &[u8], buf: &mut Vec<u8>) -> Result<(), Erro
20152015
Ok(())
20162016
}
20172017

2018+
/// Deletes a key (and its value) from a JSON object, or matching string value(s) from a JSON array.
2019+
pub fn delete_by_name(value: &[u8], name: &str, buf: &mut Vec<u8>) -> Result<(), Error> {
2020+
if !is_jsonb(value) {
2021+
let mut val = parse_value(value)?;
2022+
match &mut val {
2023+
Value::Array(arr) => {
2024+
arr.retain(|item| !matches!(item, Value::String(v) if v.eq(name)));
2025+
}
2026+
Value::Object(obj) => {
2027+
obj.remove(name);
2028+
}
2029+
_ => return Err(Error::InvalidJsonType),
2030+
};
2031+
val.write_to_vec(buf);
2032+
return Ok(());
2033+
}
2034+
delete_jsonb_by_name(value, name, buf)
2035+
}
2036+
2037+
fn delete_jsonb_by_name(value: &[u8], name: &str, buf: &mut Vec<u8>) -> Result<(), Error> {
2038+
let header = read_u32(value, 0)?;
2039+
2040+
match header & CONTAINER_HEADER_TYPE_MASK {
2041+
OBJECT_CONTAINER_TAG => {
2042+
let mut builder = ObjectBuilder::new();
2043+
for (key, jentry, item) in iterate_object_entries(value, header) {
2044+
if !key.eq(name) {
2045+
builder.push_raw(key, jentry, item);
2046+
}
2047+
}
2048+
builder.build_into(buf);
2049+
}
2050+
ARRAY_CONTAINER_TAG => {
2051+
let mut builder = ArrayBuilder::new((header & CONTAINER_HEADER_LEN_MASK) as usize);
2052+
for (jentry, item) in iterate_array(value, header) {
2053+
let matches = match jentry.type_code {
2054+
STRING_TAG => {
2055+
let v = unsafe { from_utf8_unchecked(item) };
2056+
v.eq(name)
2057+
}
2058+
_ => false,
2059+
};
2060+
if !matches {
2061+
builder.push_raw(jentry, item);
2062+
}
2063+
}
2064+
builder.build_into(buf);
2065+
}
2066+
_ => return Err(Error::InvalidJsonType),
2067+
}
2068+
Ok(())
2069+
}
2070+
2071+
/// Deletes the array element with specified index (negative integers count from the end).
2072+
pub fn delete_by_index(value: &[u8], index: i32, buf: &mut Vec<u8>) -> Result<(), Error> {
2073+
if !is_jsonb(value) {
2074+
let mut val = parse_value(value)?;
2075+
match &mut val {
2076+
Value::Array(arr) => {
2077+
let len = arr.len() as i32;
2078+
let index = if index < 0 { len - index.abs() } else { index };
2079+
if index >= 0 && index < len {
2080+
arr.remove(index as usize);
2081+
}
2082+
}
2083+
_ => return Err(Error::InvalidJsonType),
2084+
};
2085+
val.write_to_vec(buf);
2086+
return Ok(());
2087+
}
2088+
delete_jsonb_by_index(value, index, buf)
2089+
}
2090+
2091+
fn delete_jsonb_by_index(value: &[u8], index: i32, buf: &mut Vec<u8>) -> Result<(), Error> {
2092+
let header = read_u32(value, 0)?;
2093+
2094+
match header & CONTAINER_HEADER_TYPE_MASK {
2095+
ARRAY_CONTAINER_TAG => {
2096+
let len = (header & CONTAINER_HEADER_LEN_MASK) as i32;
2097+
let index = if index < 0 { len - index.abs() } else { index };
2098+
if index < 0 || index >= len {
2099+
buf.extend_from_slice(value);
2100+
} else {
2101+
let mut builder = ArrayBuilder::new((header & CONTAINER_HEADER_LEN_MASK) as usize);
2102+
let index = index as usize;
2103+
for (i, entry) in iterate_array(value, header).enumerate() {
2104+
if i != index {
2105+
builder.push_raw(entry.0, entry.1);
2106+
}
2107+
}
2108+
builder.build_into(buf);
2109+
}
2110+
}
2111+
_ => return Err(Error::InvalidJsonType),
2112+
}
2113+
Ok(())
2114+
}
2115+
20182116
/// Deletes all object fields that have null values from the given JSON value, recursively.
20192117
/// Null values that are not object fields are untouched.
20202118
pub fn strip_nulls(value: &[u8], buf: &mut Vec<u8>) -> Result<(), Error> {

src/jsonpath/selector.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ impl<'a> Selector<'a> {
463463
Expr::Paths(paths) => {
464464
// get value from path and convert to `ExprValue`.
465465
let mut poses = VecDeque::new();
466-
if let Some(Path::Current) = paths.get(0) {
466+
if let Some(Path::Current) = paths.first() {
467467
poses.push_back(pos.clone());
468468
} else {
469469
poses.push_back(Position::Container((0, root.len())));

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ mod value;
8181

8282
pub use de::from_slice;
8383
pub use error::Error;
84+
#[allow(unused_imports)]
8485
pub use from::*;
8586
pub use functions::*;
8687
pub use number::Number;

tests/it/functions.rs

Lines changed: 131 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ use std::collections::BTreeMap;
1818

1919
use jsonb::{
2020
array_length, array_values, as_bool, as_null, as_number, as_str, build_array, build_object,
21-
compare, concat, contains, convert_to_comparable, exists_all_keys, exists_any_keys, from_slice,
22-
get_by_index, get_by_keypath, get_by_name, get_by_path, is_array, is_object,
23-
keypath::parse_key_paths, object_each, object_keys, parse_value, path_exists, path_match,
24-
strip_nulls, to_bool, to_f64, to_i64, to_pretty_string, to_str, to_string, to_u64,
25-
traverse_check_string, type_of, Number, Object, Value,
21+
compare, concat, contains, convert_to_comparable, delete_by_index, delete_by_name,
22+
exists_all_keys, exists_any_keys, from_slice, get_by_index, get_by_keypath, get_by_name,
23+
get_by_path, is_array, is_object, keypath::parse_key_paths, object_each, object_keys,
24+
parse_value, path_exists, path_match, strip_nulls, to_bool, to_f64, to_i64, to_pretty_string,
25+
to_str, to_string, to_u64, traverse_check_string, type_of, Error, Number, Object, Value,
2626
};
2727

2828
use jsonb::jsonpath::parse_json_path;
@@ -1290,6 +1290,132 @@ fn test_concat() {
12901290
}
12911291
}
12921292

1293+
#[test]
1294+
fn test_delete_by_name() {
1295+
let sources = vec![
1296+
("[1,2,3]", "1", "[1,2,3]"),
1297+
(r#"["1","2","3"]"#, "0", r#"["1","2","3"]"#),
1298+
(r#"["1","2","3"]"#, "1", r#"["2","3"]"#),
1299+
(
1300+
r#"["1","2","3",{"a":1,"b":2}]"#,
1301+
"1",
1302+
r#"["2","3",{"a":1,"b":2}]"#,
1303+
),
1304+
(r#"{"a":1,"b":2}"#, "c", r#"{"a":1,"b":2}"#),
1305+
(r#"{"a":1,"b":2}"#, "a", r#"{"b":2}"#),
1306+
(r#"{"b":2}"#, "b", "{}"),
1307+
];
1308+
for (json, name, result) in sources {
1309+
{
1310+
let mut buf = Vec::new();
1311+
delete_by_name(json.as_bytes(), name, &mut buf).unwrap();
1312+
1313+
let actual = from_slice(&buf).unwrap();
1314+
let expected = parse_value(result.as_bytes()).unwrap();
1315+
1316+
assert_eq!(actual, expected);
1317+
}
1318+
{
1319+
let json = parse_value(json.as_bytes()).unwrap().to_vec();
1320+
let mut buf = Vec::new();
1321+
1322+
delete_by_name(&json, name, &mut buf).unwrap();
1323+
1324+
let actual = from_slice(&buf).unwrap();
1325+
let expected = parse_value(result.as_bytes()).unwrap();
1326+
1327+
assert_eq!(actual, expected);
1328+
}
1329+
}
1330+
}
1331+
1332+
#[test]
1333+
fn test_delete_by_name_errors() {
1334+
let sources = vec![(r#""asd""#, "asd"), ("true", "true"), ("1", "1")];
1335+
for (json, name) in sources {
1336+
{
1337+
let mut buf = Vec::new();
1338+
let result = delete_by_name(json.as_bytes(), name, &mut buf);
1339+
1340+
assert!(result.is_err());
1341+
assert!(matches!(result.unwrap_err(), Error::InvalidJsonType));
1342+
}
1343+
{
1344+
let json = parse_value(json.as_bytes()).unwrap().to_vec();
1345+
let mut buf = Vec::new();
1346+
1347+
let result = delete_by_name(&json, name, &mut buf);
1348+
1349+
assert!(result.is_err());
1350+
assert!(matches!(result.unwrap_err(), Error::InvalidJsonType));
1351+
}
1352+
}
1353+
}
1354+
1355+
#[test]
1356+
fn test_delete_by_index() {
1357+
let sources = vec![
1358+
("[1,2,3]", 0, "[2,3]"),
1359+
("[1,2,3]", 1, "[1,3]"),
1360+
("[1,2,3]", 2, "[1,2]"),
1361+
("[1,2,3]", -1, "[1,2]"),
1362+
("[1,2,3]", -2, "[1,3]"),
1363+
("[1,2,3]", -3, "[2,3]"),
1364+
("[1,2,3]", -4, "[1,2,3]"),
1365+
(r#"[1,2,{"a":[1,2,3],"b":[40,50,60]}]"#, 2, "[1,2]"),
1366+
];
1367+
for (json, index, result) in sources {
1368+
{
1369+
let mut buf = Vec::new();
1370+
delete_by_index(json.as_bytes(), index, &mut buf).unwrap();
1371+
1372+
let actual = from_slice(&buf).unwrap();
1373+
let expected = parse_value(result.as_bytes()).unwrap();
1374+
1375+
assert_eq!(actual, expected);
1376+
}
1377+
{
1378+
let json = parse_value(json.as_bytes()).unwrap().to_vec();
1379+
let mut buf = Vec::new();
1380+
1381+
delete_by_index(&json, index, &mut buf).unwrap();
1382+
1383+
let actual = from_slice(&buf).unwrap();
1384+
let expected = parse_value(result.as_bytes()).unwrap();
1385+
1386+
assert_eq!(actual, expected);
1387+
}
1388+
}
1389+
}
1390+
1391+
#[test]
1392+
fn test_delete_by_index_errors() {
1393+
let sources = vec![
1394+
(r#""asd""#, 1),
1395+
("true", 0),
1396+
("1", 10),
1397+
(r#"{"a":1,"b":2}"#, 20),
1398+
];
1399+
for (json, index) in sources {
1400+
{
1401+
let mut buf = Vec::new();
1402+
let result = delete_by_index(json.as_bytes(), index, &mut buf);
1403+
1404+
assert!(result.is_err());
1405+
assert!(matches!(result.unwrap_err(), Error::InvalidJsonType));
1406+
}
1407+
{
1408+
let json = parse_value(json.as_bytes()).unwrap().to_vec();
1409+
let mut buf = Vec::new();
1410+
1411+
let result = delete_by_index(&json, index, &mut buf);
1412+
1413+
assert!(result.is_err());
1414+
assert!(matches!(result.unwrap_err(), Error::InvalidJsonType));
1415+
}
1416+
}
1417+
}
1418+
12931419
fn init_object<'a>(entries: Vec<(&str, Value<'a>)>) -> Value<'a> {
12941420
let mut map = BTreeMap::new();
12951421
for (key, val) in entries {

0 commit comments

Comments
 (0)