Skip to content

Commit a85fc03

Browse files
rshkvtustvold
andauthored
Support setting key field in MapBuilder (#7101)
* Support setting key field metadata in MapBuilder * Return ArrowError for nullable keys * Panic instead of returning Result * Update doc * Move non-nullable keys field validation into finish * Oops, update doc after moving panic to finish * Assert that keys_field type mismatch results in panic * Update arrow-array/src/builder/map_builder.rs --------- Co-authored-by: Raphael Taylor-Davies <[email protected]>
1 parent 27d2a75 commit a85fc03

File tree

2 files changed

+94
-9
lines changed

2 files changed

+94
-9
lines changed

arrow-array/src/builder/map_builder.rs

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ pub struct MapBuilder<K: ArrayBuilder, V: ArrayBuilder> {
6161
field_names: MapFieldNames,
6262
key_builder: K,
6363
value_builder: V,
64+
key_field: Option<FieldRef>,
6465
value_field: Option<FieldRef>,
6566
}
6667

@@ -107,13 +108,27 @@ impl<K: ArrayBuilder, V: ArrayBuilder> MapBuilder<K, V> {
107108
field_names: field_names.unwrap_or_default(),
108109
key_builder,
109110
value_builder,
111+
key_field: None,
110112
value_field: None,
111113
}
112114
}
113115

114116
/// Override the field passed to [`MapBuilder::new`]
115117
///
116-
/// By default a nullable field is created with the name `values`
118+
/// By default, a non-nullable field is created with the name `keys`
119+
///
120+
/// Note: [`Self::finish`] and [`Self::finish_cloned`] will panic if the
121+
/// field's data type does not match that of `K` or the field is nullable
122+
pub fn with_keys_field(self, field: impl Into<FieldRef>) -> Self {
123+
Self {
124+
key_field: Some(field.into()),
125+
..self
126+
}
127+
}
128+
129+
/// Override the field passed to [`MapBuilder::new`]
130+
///
131+
/// By default, a nullable field is created with the name `values`
117132
///
118133
/// Note: [`Self::finish`] and [`Self::finish_cloned`] will panic if the
119134
/// field's data type does not match that of `V`
@@ -194,11 +209,17 @@ impl<K: ArrayBuilder, V: ArrayBuilder> MapBuilder<K, V> {
194209
keys_arr.null_count()
195210
);
196211

197-
let keys_field = Arc::new(Field::new(
198-
self.field_names.key.as_str(),
199-
keys_arr.data_type().clone(),
200-
false, // always non-nullable
201-
));
212+
let keys_field = match &self.key_field {
213+
Some(f) => {
214+
assert!(!f.is_nullable(), "Keys field must not be nullable");
215+
f.clone()
216+
}
217+
None => Arc::new(Field::new(
218+
self.field_names.key.as_str(),
219+
keys_arr.data_type().clone(),
220+
false, // always non-nullable
221+
)),
222+
};
202223
let values_field = match &self.value_field {
203224
Some(f) => f.clone(),
204225
None => Arc::new(Field::new(
@@ -262,10 +283,10 @@ impl<K: ArrayBuilder, V: ArrayBuilder> ArrayBuilder for MapBuilder<K, V> {
262283

263284
#[cfg(test)]
264285
mod tests {
286+
use super::*;
265287
use crate::builder::{make_builder, Int32Builder, StringBuilder};
266288
use crate::{Int32Array, StringArray};
267-
268-
use super::*;
289+
use std::collections::HashMap;
269290

270291
#[test]
271292
#[should_panic(expected = "Keys array must have no null values, found 1 null value(s)")]
@@ -377,4 +398,67 @@ mod tests {
377398
)
378399
);
379400
}
401+
402+
#[test]
403+
fn test_with_keys_field() {
404+
let mut key_metadata = HashMap::new();
405+
key_metadata.insert("foo".to_string(), "bar".to_string());
406+
let key_field = Arc::new(
407+
Field::new("keys", DataType::Int32, false).with_metadata(key_metadata.clone()),
408+
);
409+
let mut builder = MapBuilder::new(None, Int32Builder::new(), Int32Builder::new())
410+
.with_keys_field(key_field.clone());
411+
builder.keys().append_value(1);
412+
builder.values().append_value(2);
413+
builder.append(true).unwrap();
414+
let map = builder.finish();
415+
416+
assert_eq!(map.len(), 1);
417+
assert_eq!(
418+
map.data_type(),
419+
&DataType::Map(
420+
Arc::new(Field::new(
421+
"entries",
422+
DataType::Struct(
423+
vec![
424+
Arc::new(
425+
Field::new("keys", DataType::Int32, false)
426+
.with_metadata(key_metadata)
427+
),
428+
Arc::new(Field::new("values", DataType::Int32, true))
429+
]
430+
.into()
431+
),
432+
false,
433+
)),
434+
false
435+
)
436+
);
437+
}
438+
439+
#[test]
440+
#[should_panic(expected = "Keys field must not be nullable")]
441+
fn test_with_nullable_keys_field() {
442+
let mut builder = MapBuilder::new(None, Int32Builder::new(), Int32Builder::new())
443+
.with_keys_field(Arc::new(Field::new("keys", DataType::Int32, true)));
444+
445+
builder.keys().append_value(1);
446+
builder.values().append_value(2);
447+
builder.append(true).unwrap();
448+
449+
builder.finish();
450+
}
451+
452+
#[test]
453+
#[should_panic(expected = "Incorrect datatype")]
454+
fn test_keys_field_type_mismatch() {
455+
let mut builder = MapBuilder::new(None, Int32Builder::new(), Int32Builder::new())
456+
.with_keys_field(Arc::new(Field::new("keys", DataType::Utf8, false)));
457+
458+
builder.keys().append_value(1);
459+
builder.values().append_value(2);
460+
builder.append(true).unwrap();
461+
462+
builder.finish();
463+
}
380464
}

arrow-array/src/builder/struct_builder.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,10 +296,11 @@ pub fn make_builder(datatype: &DataType, capacity: usize) -> Box<dyn ArrayBuilde
296296
value_builder,
297297
capacity,
298298
)
299+
.with_keys_field(fields[0].clone())
299300
.with_values_field(fields[1].clone()),
300301
)
301302
}
302-
t => panic!("The field of Map data type {t:?} should has a child Struct field"),
303+
t => panic!("The field of Map data type {t:?} should have a child Struct field"),
303304
},
304305
DataType::Struct(fields) => Box::new(StructBuilder::from_fields(fields.clone(), capacity)),
305306
t @ DataType::Dictionary(key_type, value_type) => {

0 commit comments

Comments
 (0)