Skip to content

Commit fc84214

Browse files
authored
feat: extract_scalar_key_values function support ignore array (#99)
1 parent 1868abf commit fc84214

File tree

5 files changed

+277
-26
lines changed

5 files changed

+277
-26
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ nom = "8.0.0"
3535
num-traits = "0.2.19"
3636
ordered-float = { version = "5.1.0", default-features = false }
3737
rand = { version = "0.9.2", features = ["small_rng"] }
38-
serde = "1.0"
38+
serde = { version = "1.0", features = ["derive"] }
3939
serde_json = { version = "1.0", default-features = false, features = ["std"] }
4040
zmij = "1.0"
4141

src/functions/path.rs

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,12 +1053,20 @@ impl RawJsonb<'_> {
10531053
/// along with their corresponding key paths. The key path represents the navigation
10541054
/// path from the root to reach each scalar value.
10551055
///
1056+
/// # Arguments
1057+
///
1058+
/// * `ignore_array` - When true, arrays are treated as leaf values and returned as
1059+
/// `Value::Array` without descending into their elements.
1060+
///
10561061
/// # Returns
10571062
///
10581063
/// * `Result<Vec<(KeyPaths<'_>, Value<'_>)>>` - A vector of tuples, each containing:
10591064
/// - `KeyPaths`: The path to reach the scalar value
10601065
/// - `Value`: The scalar value itself
10611066
///
1067+
/// Empty objects or arrays are treated as leaf values and returned as `Value::Object` or
1068+
/// `Value::Array`.
1069+
///
10621070
/// # Examples
10631071
///
10641072
/// ```rust
@@ -1067,7 +1075,7 @@ impl RawJsonb<'_> {
10671075
/// let json = r#"{"user": {"name": "Alice", "scores": [85, 92, 78]}}"#;
10681076
/// let jsonb = json.parse::<OwnedJsonb>().unwrap();
10691077
/// let raw_jsonb = jsonb.as_raw();
1070-
/// let result = raw_jsonb.extract_scalar_key_values();
1078+
/// let result = raw_jsonb.extract_scalar_key_values(false);
10711079
/// assert!(result.is_ok());
10721080
/// let result = result.unwrap();
10731081
/// assert_eq!(result.len(), 4);
@@ -1077,18 +1085,43 @@ impl RawJsonb<'_> {
10771085
/// // - path: "user", "scores", 1 -> value: 92
10781086
/// // - path: "user", "scores", 2 -> value: 78
10791087
/// ```
1080-
pub fn extract_scalar_key_values(&self) -> Result<Vec<(KeyPaths<'_>, Value<'_>)>> {
1088+
///
1089+
/// ```rust
1090+
/// use jsonb::{OwnedJsonb, Value};
1091+
///
1092+
/// let json = r#"{"user": {"name": "Alice", "scores": [85, 92, 78]}}"#;
1093+
/// let jsonb = json.parse::<OwnedJsonb>().unwrap();
1094+
/// let raw_jsonb = jsonb.as_raw();
1095+
/// let result = raw_jsonb.extract_scalar_key_values(true).unwrap();
1096+
///
1097+
/// assert_eq!(result.len(), 2);
1098+
/// assert!(result.iter().any(|(_, value)| matches!(value, Value::Array(_))));
1099+
/// // Result contains:
1100+
/// // - path: "user", "name" -> value: "Alice"
1101+
/// // - path: "user", "scores" -> value: [85, 92, 78]
1102+
/// ```
1103+
pub fn extract_scalar_key_values(
1104+
&self,
1105+
ignore_array: bool,
1106+
) -> Result<Vec<(KeyPaths<'_>, Value<'_>)>> {
10811107
let item = JsonbItem::from_raw_jsonb(*self)?;
10821108
let mut result = Vec::with_capacity(16);
10831109
let mut current_paths = Vec::with_capacity(3);
1084-
Self::extract_scalar_key_values_recursive(item, &mut current_paths, &mut result)?;
1110+
Self::extract_scalar_key_values_recursive(
1111+
item,
1112+
ignore_array,
1113+
&mut current_paths,
1114+
&mut result,
1115+
)?;
10851116
Ok(result)
10861117
}
10871118

10881119
/// Helper function for `extract_scalar_key_values` that recursively traverses the JSONB structure.
10891120
///
10901121
/// This function implements a depth-first traversal of the JSONB document, building up the
10911122
/// key path as it goes and collecting scalar values when it reaches leaf nodes.
1123+
/// Empty objects or arrays are treated as leaf values and returned as `Value::Object` or
1124+
/// `Value::Array` instead of being skipped.
10921125
///
10931126
/// # Arguments
10941127
///
@@ -1101,32 +1134,56 @@ impl RawJsonb<'_> {
11011134
/// * `Result<()>` - Success or error during traversal
11021135
fn extract_scalar_key_values_recursive<'a>(
11031136
current_item: JsonbItem<'a>,
1137+
ignore_array: bool,
11041138
current_paths: &mut Vec<KeyPath<'a>>,
11051139
result: &mut Vec<(KeyPaths<'a>, Value<'a>)>,
11061140
) -> Result<()> {
11071141
match current_item {
11081142
JsonbItem::Raw(raw) => {
11091143
let object_iter_opt = ObjectIterator::new(raw)?;
11101144
if let Some(mut object_iter) = object_iter_opt {
1111-
for object_result in &mut object_iter {
1112-
let (key, val_item) = object_result?;
1113-
current_paths.push(KeyPath::Name(Cow::Borrowed(key)));
1114-
// Recursively handle object values
1115-
Self::extract_scalar_key_values_recursive(val_item, current_paths, result)?;
1116-
current_paths.pop();
1145+
if object_iter.len() > 0 {
1146+
for object_result in &mut object_iter {
1147+
let (key, val_item) = object_result?;
1148+
current_paths.push(KeyPath::Name(Cow::Borrowed(key)));
1149+
// Recursively handle object values
1150+
Self::extract_scalar_key_values_recursive(
1151+
val_item,
1152+
ignore_array,
1153+
current_paths,
1154+
result,
1155+
)?;
1156+
current_paths.pop();
1157+
}
1158+
return Ok(());
11171159
}
1118-
return Ok(());
1119-
}
1120-
let array_iter_opt = ArrayIterator::new(raw)?;
1121-
if let Some(array_iter) = array_iter_opt {
1122-
for (index, array_result) in &mut array_iter.enumerate() {
1123-
let val_item = array_result?;
1124-
current_paths.push(KeyPath::Index(index as i32));
1125-
// Recursively handle array values
1126-
Self::extract_scalar_key_values_recursive(val_item, current_paths, result)?;
1127-
current_paths.pop();
1160+
} else if !ignore_array {
1161+
let array_iter_opt = ArrayIterator::new(raw)?;
1162+
if let Some(array_iter) = array_iter_opt {
1163+
if array_iter.len() > 0 {
1164+
for (index, array_result) in &mut array_iter.enumerate() {
1165+
let val_item = array_result?;
1166+
current_paths.push(KeyPath::Index(index as i32));
1167+
// Recursively handle array values
1168+
Self::extract_scalar_key_values_recursive(
1169+
val_item,
1170+
ignore_array,
1171+
current_paths,
1172+
result,
1173+
)?;
1174+
current_paths.pop();
1175+
}
1176+
return Ok(());
1177+
}
11281178
}
11291179
}
1180+
if !current_paths.is_empty() {
1181+
let key_paths = KeyPaths {
1182+
paths: current_paths.clone(),
1183+
};
1184+
let value = raw.to_value()?;
1185+
result.push((key_paths, value));
1186+
}
11301187
}
11311188
JsonbItem::Owned(_) => unreachable!(),
11321189
_ => {

src/keypath.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,23 @@ pub enum KeyPath<'a> {
5050
Name(Cow<'a, str>),
5151
}
5252

53+
/// Represents a set of owned key path chains.
54+
#[derive(Debug, Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
55+
pub struct OwnedKeyPaths {
56+
pub paths: Vec<OwnedKeyPath>,
57+
}
58+
59+
/// Represents a valid owned key path.
60+
#[derive(Debug, Clone, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
61+
pub enum OwnedKeyPath {
62+
/// represents the index of an Array, allow negative indexing.
63+
Index(i32),
64+
/// represents the quoted field name of an Object.
65+
QuotedName(String),
66+
/// represents the field name of an Object.
67+
Name(String),
68+
}
69+
5370
impl Display for KeyPaths<'_> {
5471
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
5572
write!(f, "{{")?;
@@ -81,6 +98,73 @@ impl Display for KeyPath<'_> {
8198
}
8299
}
83100

101+
impl Display for OwnedKeyPaths {
102+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
103+
write!(f, "{{")?;
104+
for (i, path) in self.paths.iter().enumerate() {
105+
if i > 0 {
106+
write!(f, ",")?;
107+
}
108+
write!(f, "{path}")?;
109+
}
110+
write!(f, "}}")?;
111+
Ok(())
112+
}
113+
}
114+
115+
impl Display for OwnedKeyPath {
116+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
117+
match self {
118+
OwnedKeyPath::Index(idx) => {
119+
write!(f, "{idx}")?;
120+
}
121+
OwnedKeyPath::QuotedName(name) => {
122+
write!(f, "\"{name}\"")?;
123+
}
124+
OwnedKeyPath::Name(name) => {
125+
write!(f, "{name}")?;
126+
}
127+
}
128+
Ok(())
129+
}
130+
}
131+
132+
impl<'a> KeyPaths<'a> {
133+
pub fn to_owned(&self) -> OwnedKeyPaths {
134+
OwnedKeyPaths {
135+
paths: self.paths.iter().map(KeyPath::to_owned).collect(),
136+
}
137+
}
138+
}
139+
140+
impl OwnedKeyPaths {
141+
pub fn as_key_paths(&self) -> KeyPaths<'_> {
142+
KeyPaths {
143+
paths: self.paths.iter().map(OwnedKeyPath::as_key_path).collect(),
144+
}
145+
}
146+
}
147+
148+
impl<'a> KeyPath<'a> {
149+
pub fn to_owned(&self) -> OwnedKeyPath {
150+
match self {
151+
KeyPath::Index(idx) => OwnedKeyPath::Index(*idx),
152+
KeyPath::QuotedName(name) => OwnedKeyPath::QuotedName(name.to_string()),
153+
KeyPath::Name(name) => OwnedKeyPath::Name(name.to_string()),
154+
}
155+
}
156+
}
157+
158+
impl OwnedKeyPath {
159+
pub fn as_key_path(&self) -> KeyPath<'_> {
160+
match self {
161+
OwnedKeyPath::Index(idx) => KeyPath::Index(*idx),
162+
OwnedKeyPath::QuotedName(name) => KeyPath::QuotedName(Cow::Borrowed(name.as_str())),
163+
OwnedKeyPath::Name(name) => KeyPath::Name(Cow::Borrowed(name.as_str())),
164+
}
165+
}
166+
}
167+
84168
/// Parsing the input string to key paths.
85169
pub fn parse_key_paths(input: &[u8]) -> Result<KeyPaths<'_>, Error> {
86170
match key_paths(input) {

0 commit comments

Comments
 (0)