Skip to content

Commit fe6835a

Browse files
authored
Merge pull request #25 from b41sh/feat-traverse_check_string
Feat: Support traverse_check_string function
2 parents d81fbee + c3d6227 commit fe6835a

File tree

2 files changed

+90
-1
lines changed

2 files changed

+90
-1
lines changed

src/functions.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,59 @@ fn rand_scalar_value() -> Value<'static> {
12751275
val
12761276
}
12771277

1278+
/// Traverse all the string fields in a jsonb value and check whether the conditions are met.
1279+
pub fn traverse_check_string(value: &[u8], func: impl Fn(&[u8]) -> bool) -> bool {
1280+
if !is_jsonb(value) {
1281+
match parse_value(value) {
1282+
Ok(val) => {
1283+
let val_buf = val.to_vec();
1284+
return traverse_check_string(&val_buf, func);
1285+
}
1286+
Err(_) => {
1287+
return false;
1288+
}
1289+
}
1290+
}
1291+
1292+
let mut offsets = VecDeque::new();
1293+
offsets.push_back(0);
1294+
1295+
while let Some(offset) = offsets.pop_front() {
1296+
let header = read_u32(value, offset).unwrap();
1297+
let length = (header & CONTAINER_HEADER_LEN_MASK) as usize;
1298+
1299+
let size = match header & CONTAINER_HEADER_TYPE_MASK {
1300+
SCALAR_CONTAINER_TAG => 1,
1301+
ARRAY_CONTAINER_TAG => length,
1302+
OBJECT_CONTAINER_TAG => length * 2,
1303+
_ => unreachable!("invalid jsonb value"),
1304+
};
1305+
1306+
let mut jentry_offset = offset + 4;
1307+
let mut val_offset = offset + 4 + 4 * size;
1308+
for _ in 0..size {
1309+
let encoded = read_u32(value, jentry_offset).unwrap();
1310+
let jentry = JEntry::decode_jentry(encoded);
1311+
match jentry.type_code {
1312+
CONTAINER_TAG => {
1313+
offsets.push_back(val_offset);
1314+
}
1315+
STRING_TAG => {
1316+
let val_length = jentry.length as usize;
1317+
if func(&value[val_offset..val_offset + val_length]) {
1318+
return true;
1319+
}
1320+
}
1321+
_ => {}
1322+
}
1323+
jentry_offset += 4;
1324+
val_offset += jentry.length as usize;
1325+
}
1326+
}
1327+
1328+
false
1329+
}
1330+
12781331
// Check whether the value is `JSONB` format,
12791332
// for compatibility with previous `JSON` string.
12801333
fn is_jsonb(value: &[u8]) -> bool {

tests/it/functions.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use jsonb::{
1919
array_length, array_values, as_bool, as_null, as_number, as_str, build_array, build_object,
2020
compare, convert_to_comparable, from_slice, get_by_index, get_by_name, get_by_path, is_array,
2121
is_object, object_keys, parse_value, to_bool, to_f64, to_i64, to_str, to_string, to_u64,
22-
Number, Object, Value,
22+
traverse_check_string, Number, Object, Value,
2323
};
2424

2525
use jsonb::jsonpath::parse_json_path;
@@ -781,3 +781,39 @@ fn test_to_string() {
781781
buf.clear();
782782
}
783783
}
784+
785+
#[test]
786+
fn test_traverse_check_string() {
787+
let sources = vec![
788+
(r#"null"#, false),
789+
(r#"11"#, false),
790+
(r#""a""#, false),
791+
(r#""c""#, true),
792+
(r#"[1,2,3,4,"b"]"#, false),
793+
(r#"[1,2,[3,[4,"c"]]]"#, true),
794+
(r#"[true,false,[1,2,3],{"a":"b"}]"#, false),
795+
(
796+
r#"{"a":true,"b":{"b1":"v1","b2":11},"c":[true,12,"c1","c2"]}"#,
797+
true,
798+
),
799+
(
800+
r#"{"a0":true,"b0":{"b1":"v1","b2":11},"c0":[true,12,"c1","c"]}"#,
801+
true,
802+
),
803+
(
804+
r#"{"a0":true,"b0":{"b1":"v1","b2":11},"c0":[true,12,"c1","c2"]}"#,
805+
false,
806+
),
807+
];
808+
let mut buf: Vec<u8> = Vec::new();
809+
for (s, expect) in sources {
810+
let value = parse_value(s.as_bytes()).unwrap();
811+
value.write_to_vec(&mut buf);
812+
let res = traverse_check_string(&buf, |v| {
813+
let s = unsafe { std::str::from_utf8_unchecked(v) };
814+
s == "c"
815+
});
816+
assert_eq!(res, expect);
817+
buf.clear();
818+
}
819+
}

0 commit comments

Comments
 (0)