Skip to content

Commit d11c9a9

Browse files
authored
Merge pull request #38 from akoshchiy/11270-keys-exists-api
feat: add exists_any_keys & exists_all_keys
2 parents 14143b5 + f2cd167 commit d11c9a9

File tree

2 files changed

+265
-4
lines changed

2 files changed

+265
-4
lines changed

src/functions.rs

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use core::convert::TryInto;
1616
use std::borrow::Cow;
1717
use std::cmp::Ordering;
1818
use std::collections::VecDeque;
19+
use std::str::from_utf8;
20+
use std::str::from_utf8_unchecked;
1921

2022
use crate::constants::*;
2123
use crate::error::*;
@@ -366,6 +368,120 @@ pub fn get_by_keypath<'a, I: Iterator<Item = &'a KeyPath<'a>>>(
366368
.map(|jentry| extract_by_jentry(&jentry, curr_jentry_encoded, curr_val_offset, value))
367369
}
368370

371+
/// Checks whether all of the strings exist as top-level keys or array elements.
372+
pub fn exists_all_keys<'a, I: Iterator<Item = &'a [u8]>>(value: &[u8], keys: I) -> bool {
373+
if !is_jsonb(value) {
374+
match parse_value(value) {
375+
Ok(val) => {
376+
for key in keys {
377+
match from_utf8(key) {
378+
Ok(key) => {
379+
if !exists_value_key(&val, key) {
380+
return false;
381+
}
382+
}
383+
Err(_) => return false,
384+
}
385+
}
386+
}
387+
Err(_) => return false,
388+
};
389+
return true;
390+
}
391+
392+
let header = read_u32(value, 0).unwrap();
393+
394+
for key in keys {
395+
match from_utf8(key) {
396+
Ok(key) => {
397+
if !exists_jsonb_key(value, header, key) {
398+
return false;
399+
}
400+
}
401+
Err(_) => return false,
402+
}
403+
}
404+
true
405+
}
406+
407+
/// Checks whether any of the strings exist as top-level keys or array elements.
408+
pub fn exists_any_keys<'a, I: Iterator<Item = &'a [u8]>>(value: &[u8], keys: I) -> bool {
409+
if !is_jsonb(value) {
410+
if let Ok(val) = parse_value(value) {
411+
for key in keys {
412+
if let Ok(key) = from_utf8(key) {
413+
if exists_value_key(&val, key) {
414+
return true;
415+
}
416+
}
417+
}
418+
}
419+
return false;
420+
}
421+
422+
let header = read_u32(value, 0).unwrap();
423+
424+
for key in keys {
425+
if let Ok(key) = from_utf8(key) {
426+
if exists_jsonb_key(value, header, key) {
427+
return true;
428+
}
429+
}
430+
}
431+
false
432+
}
433+
434+
fn exists_value_key(value: &Value, key: &str) -> bool {
435+
match value {
436+
Value::Array(arr) => {
437+
let mut found = false;
438+
for item in arr {
439+
let matches = match item {
440+
Value::String(v) => key.eq(v),
441+
_ => false,
442+
};
443+
if matches {
444+
found = true;
445+
break;
446+
}
447+
}
448+
found
449+
}
450+
Value::Object(obj) => obj.contains_key(key),
451+
_ => false,
452+
}
453+
}
454+
455+
fn exists_jsonb_key(value: &[u8], header: u32, key: &str) -> bool {
456+
match header & CONTAINER_HEADER_TYPE_MASK {
457+
OBJECT_CONTAINER_TAG => {
458+
let mut matches = false;
459+
for obj_key in iteate_object_keys(value, header) {
460+
if obj_key.eq(key) {
461+
matches = true;
462+
break;
463+
}
464+
}
465+
matches
466+
}
467+
ARRAY_CONTAINER_TAG => {
468+
let mut matches = false;
469+
for (jentry, val) in iterate_array(value, header) {
470+
if jentry.type_code != STRING_TAG {
471+
continue;
472+
}
473+
let val = unsafe { from_utf8_unchecked(val) };
474+
if val.eq(key) {
475+
matches = true;
476+
break;
477+
}
478+
}
479+
matches
480+
}
481+
_ => false,
482+
}
483+
}
484+
369485
fn get_jentry_by_name(
370486
value: &[u8],
371487
offset: usize,
@@ -1683,3 +1799,89 @@ fn read_u32(buf: &[u8], idx: usize) -> Result<u32, Error> {
16831799
.unwrap();
16841800
Ok(u32::from_be_bytes(bytes))
16851801
}
1802+
1803+
fn iterate_array(value: &[u8], header: u32) -> ArrayIterator<'_> {
1804+
let length = (header & CONTAINER_HEADER_LEN_MASK) as usize;
1805+
ArrayIterator {
1806+
value,
1807+
jentry_offset: 4,
1808+
val_offset: 4 * length + 4,
1809+
length,
1810+
idx: 0,
1811+
}
1812+
}
1813+
1814+
fn iteate_object_keys(value: &[u8], header: u32) -> ObjectKeyIterator<'_> {
1815+
let length = (header & CONTAINER_HEADER_LEN_MASK) as usize;
1816+
ObjectKeyIterator {
1817+
value,
1818+
jentry_offset: 4,
1819+
key_offset: 8 * length + 4,
1820+
length,
1821+
idx: 0,
1822+
}
1823+
}
1824+
1825+
struct ArrayIterator<'a> {
1826+
value: &'a [u8],
1827+
jentry_offset: usize,
1828+
val_offset: usize,
1829+
length: usize,
1830+
idx: usize,
1831+
}
1832+
1833+
impl<'a> Iterator for ArrayIterator<'a> {
1834+
type Item = (JEntry, &'a [u8]);
1835+
1836+
fn next(&mut self) -> Option<Self::Item> {
1837+
if self.idx >= self.length {
1838+
return None;
1839+
}
1840+
let encoded = read_u32(self.value, self.jentry_offset).unwrap();
1841+
let jentry = JEntry::decode_jentry(encoded);
1842+
let val_length = jentry.length as usize;
1843+
1844+
let item = (
1845+
jentry,
1846+
&self.value[self.val_offset..self.val_offset + val_length],
1847+
);
1848+
1849+
self.idx += 1;
1850+
self.val_offset += val_length;
1851+
self.jentry_offset += 4;
1852+
1853+
Some(item)
1854+
}
1855+
}
1856+
1857+
struct ObjectKeyIterator<'a> {
1858+
value: &'a [u8],
1859+
jentry_offset: usize,
1860+
key_offset: usize,
1861+
length: usize,
1862+
idx: usize,
1863+
}
1864+
1865+
impl<'a> Iterator for ObjectKeyIterator<'a> {
1866+
type Item = &'a str;
1867+
1868+
fn next(&mut self) -> Option<Self::Item> {
1869+
if self.idx >= self.length {
1870+
return None;
1871+
}
1872+
1873+
let encoded = read_u32(self.value, self.jentry_offset).unwrap();
1874+
let jentry = JEntry::decode_jentry(encoded);
1875+
let key_length = jentry.length as usize;
1876+
1877+
let key = unsafe {
1878+
from_utf8_unchecked(&self.value[self.key_offset..self.key_offset + key_length])
1879+
};
1880+
1881+
self.idx += 1;
1882+
self.key_offset += key_length;
1883+
self.jentry_offset += 4;
1884+
1885+
Some(key)
1886+
}
1887+
}

tests/it/functions.rs

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +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, convert_to_comparable, from_slice, get_by_index, get_by_keypath, get_by_name,
22-
get_by_path, is_array, is_object, keypath::parse_key_paths, object_each, object_keys,
23-
parse_value, path_exists, strip_nulls, to_bool, to_f64, to_i64, to_pretty_string, to_str,
24-
to_string, to_u64, traverse_check_string, type_of, Number, Object, Value,
21+
compare, convert_to_comparable, exists_all_keys, exists_any_keys, from_slice, get_by_index,
22+
get_by_keypath, get_by_name, get_by_path, is_array, is_object, keypath::parse_key_paths,
23+
object_each, object_keys, parse_value, path_exists, strip_nulls, to_bool, to_f64, to_i64,
24+
to_pretty_string, to_str, to_string, to_u64, traverse_check_string, type_of, Number, Object,
25+
Value,
2526
};
2627

2728
use jsonb::jsonpath::parse_json_path;
@@ -1088,6 +1089,64 @@ fn test_get_by_keypath() {
10881089
}
10891090
}
10901091

1092+
#[test]
1093+
fn test_exists_all_keys() {
1094+
let sources = vec![
1095+
(r#"true"#, vec!["10", "20", "40"], false),
1096+
(r#"[]"#, vec!["10", "20", "40"], false),
1097+
(r#"{}"#, vec!["10", "20", "40"], false),
1098+
(r#"["10","20","30"]"#, vec!["10", "20", "40"], false),
1099+
(r#"["10","20","30"]"#, vec!["10", "20", "30"], true),
1100+
(r#"[10,20,30]"#, vec!["10", "20", "30"], false),
1101+
(r#"["10","20","30"]"#, vec!["10", "20", "20"], true),
1102+
(r#"{"a":1,"b":2,"c":3}"#, vec!["c", "b", "a"], true),
1103+
(r#"{"a":1,"b":2,"c":3}"#, vec!["a", "b", "a"], true),
1104+
(r#"{"a":1,"b":2,"c":3}"#, vec!["c", "f", "a"], false),
1105+
];
1106+
for (json, keys, expected) in sources {
1107+
let keys = keys.iter().map(|k| k.as_bytes());
1108+
{
1109+
let json = parse_value(json.as_bytes()).unwrap().to_vec();
1110+
let result = exists_all_keys(&json, keys.clone());
1111+
assert_eq!(result, expected);
1112+
}
1113+
{
1114+
let json = json.as_bytes();
1115+
let result = exists_all_keys(json, keys.clone());
1116+
assert_eq!(result, expected);
1117+
}
1118+
}
1119+
}
1120+
1121+
#[test]
1122+
fn test_exists_any_keys() {
1123+
let sources = vec![
1124+
(r#"true"#, vec!["10", "20", "40"], false),
1125+
(r#"[]"#, vec!["10", "20", "40"], false),
1126+
(r#"{}"#, vec!["10", "20", "40"], false),
1127+
(r#"[10,20,30]"#, vec!["10", "20", "30"], false),
1128+
(r#"["10","20","30"]"#, vec!["10", "20", "40"], true),
1129+
(r#"["10","20","30"]"#, vec!["10", "20", "30"], true),
1130+
(r#"["10","20","30"]"#, vec!["40", "50", "60"], false),
1131+
(r#"{"a":1,"b":2,"c":3}"#, vec!["c", "b", "a"], true),
1132+
(r#"{"a":1,"b":2,"c":3}"#, vec!["a", "b", "a"], true),
1133+
(r#"{"a":1,"b":2,"c":3}"#, vec!["z", "f", "x"], false),
1134+
];
1135+
for (json, keys, expected) in sources {
1136+
let keys = keys.iter().map(|k| k.as_bytes());
1137+
{
1138+
let json = parse_value(json.as_bytes()).unwrap().to_vec();
1139+
let result = exists_any_keys(&json, keys.clone());
1140+
assert_eq!(result, expected);
1141+
}
1142+
{
1143+
let json = json.as_bytes();
1144+
let result = exists_any_keys(json, keys.clone());
1145+
assert_eq!(result, expected);
1146+
}
1147+
}
1148+
}
1149+
10911150
fn init_object<'a>(entries: Vec<(&str, Value<'a>)>) -> Value<'a> {
10921151
let mut map = BTreeMap::new();
10931152
for (key, val) in entries {

0 commit comments

Comments
 (0)