From dd1f712fd298e8e0a88b6538b3e6b05a7d914269 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Wed, 7 May 2025 11:35:55 -0700 Subject: [PATCH 1/6] feat: Support `IndexByName` and `IndexById` for Namemapping --- crates/iceberg/src/spec/name_mapping/mod.rs | 400 ++++++++++++++++++-- 1 file changed, 367 insertions(+), 33 deletions(-) diff --git a/crates/iceberg/src/spec/name_mapping/mod.rs b/crates/iceberg/src/spec/name_mapping/mod.rs index af18e05b23..1f164661e4 100644 --- a/crates/iceberg/src/spec/name_mapping/mod.rs +++ b/crates/iceberg/src/spec/name_mapping/mod.rs @@ -17,31 +17,72 @@ //! Iceberg name mapping. -use std::sync::Arc; +use std::collections::HashMap; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DefaultOnNull}; +use crate::spec::{ + visit_schema, ListType, MapType, NestedFieldRef, PrimitiveType, Schema, SchemaVisitor, + StructType, +}; +use crate::Result; + /// Property name for name mapping. pub const DEFAULT_SCHEMA_NAME_MAPPING: &str = "schema.name-mapping.default"; /// Iceberg fallback field name to ID mapping. #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] -#[serde(transparent)] pub struct NameMapping { root: Vec, + id_to_field: HashMap, + name_to_field: HashMap, } impl NameMapping { - /// Create a new [`NameMapping`] given a collection of mapped fields. - pub fn new(fields: Vec) -> Self { - Self { root: fields } + /// Creates new `NameMapping` instance + pub fn new( + root: Vec, + id_to_field: HashMap, + name_to_field: HashMap, + ) -> Self { + Self { + root, + id_to_field, + name_to_field, + } } /// Get a reference to fields which are to be mapped from name to field ID. pub fn fields(&self) -> &[MappedField] { &self.root } + + /// Parses name_mapping from JSON. + pub fn parse_name_mapping(name_mapping: &str) -> Result { + let parsed_name_mapping: NameMapping = serde_json::from_str(name_mapping)?; + Ok(parsed_name_mapping) + } + + /// Returns an index mapping id to `MappedField`` by visiting the schema. + fn index_by_id(mapping: &Vec) -> HashMap { + visit_name_mapping(mapping, &IndexById {}) + } + + /// Returns an index mapping names to `MappedField`` by visiting the schema. + fn index_by_name(mapping: &Vec) -> HashMap { + visit_name_mapping(mapping, &IndexByName {}) + } + + /// Create mapping from schema + pub fn create_mapping_from_schema(schema: &Schema) -> Self { + let mapped_fields = visit_schema(schema, &mut CreateMapping).unwrap(); + + let id_to_field = NameMapping::index_by_id(&mapped_fields); + let name_to_field = NameMapping::index_by_name(&mapped_fields); + + NameMapping::new(mapped_fields, id_to_field, name_to_field) + } } /// Maps field names to IDs. @@ -55,7 +96,7 @@ pub struct MappedField { #[serde(default)] #[serde(skip_serializing_if = "Vec::is_empty")] #[serde_as(deserialize_as = "DefaultOnNull")] - fields: Vec>, + fields: Vec, } impl MappedField { @@ -64,7 +105,7 @@ impl MappedField { Self { field_id, names, - fields: fields.into_iter().map(Arc::new).collect(), + fields: fields, } } @@ -79,15 +120,188 @@ impl MappedField { } /// Get a reference to the field mapping for any child fields. - pub fn fields(&self) -> &[Arc] { + pub fn fields(&self) -> &[MappedField] { &self.fields } } +/// A trait for visiting and transforming a name mapping +trait NameMappingVisitor { + /// Aggregated result of `MappedField`s + type S; + /// Result type for processing one `MappedField` + type T; + + /// Handles entire `NameMapping` field + fn mapping(&self, field_result: Self::S) -> Self::S; + + /// Handles accessing multiple `MappedField` + fn fields(&self, field_results: Vec) -> Self::S; + + /// Handles a single `MappedField` + fn field(&self, field: &MappedField, field_result: Self::S) -> Self::T; +} + +/// Recursively visits the entire name mapping using visitor +fn visit_name_mapping(namespace: &Vec, visitor: &V) -> V::S +where V: NameMappingVisitor { + let root_result = visit_fields(namespace, visitor); + visitor.mapping(root_result) +} + +/// Recursively visits a slice of mapped fields using visitor +fn visit_fields(fields: &Vec, visitor: &V) -> V::S +where V: NameMappingVisitor { + let mut results: Vec = Vec::new(); + + for field in fields { + let child_result = visit_fields(&field.fields, visitor); + let field_result = visitor.field(field, child_result); + results.push(field_result); + } + + visitor.fields(results) +} + +struct IndexByName {} + +impl NameMappingVisitor for IndexByName { + type S = HashMap; + type T = HashMap; + + fn mapping(&self, field_result: HashMap) -> HashMap { + field_result + } + + fn fields( + &self, + field_results: Vec>, + ) -> HashMap { + field_results + .into_iter() + .fold(HashMap::new(), |mut acc, map| { + acc.extend(map); + acc + }) + } + + fn field( + &self, + field: &MappedField, + child_result: HashMap, + ) -> HashMap { + let mut result = child_result; + + for name in &field.names { + for (child_key, child_field) in result.clone().iter() { + let composite_key = format!("{}.{}", name, child_key); + result.insert(composite_key, child_field.clone()); + } + } + + for name in &field.names { + result.insert(name.clone(), field.clone()); + } + result + } +} + +struct IndexById {} + +impl NameMappingVisitor for IndexById { + type S = HashMap; + type T = HashMap; + + fn mapping(&self, field_result: HashMap) -> HashMap { + field_result + } + + fn fields(&self, field_results: Vec>) -> HashMap { + field_results + .into_iter() + .fold(HashMap::new(), |mut acc, map| { + acc.extend(map); + acc + }) + } + + fn field( + &self, + field: &MappedField, + field_results: HashMap, + ) -> HashMap { + let mut result = field_results; + + if let Some(id) = field.field_id { + result.insert(id, field.clone()); + } + result + } +} + +struct CreateMapping; + +impl SchemaVisitor for CreateMapping { + type T = Vec; + + fn schema(&mut self, _schema: &Schema, value: Self::T) -> Result { + Ok(value) + } + + fn field(&mut self, _field: &NestedFieldRef, value: Self::T) -> Result { + Ok(value) + } + + fn r#struct(&mut self, struct_type: &StructType, results: Vec) -> Result { + let mapped_fields = struct_type + .fields() + .iter() + .zip(results) + .map(|(field, result)| { + MappedField::new(Some(field.id), vec![field.name.clone()], result) + }) + .collect::>(); + + Ok(mapped_fields) + } + + fn list(&mut self, list: &ListType, value: Self::T) -> Result { + Ok(vec![MappedField::new( + Some(list.element_field.id), + vec!["element".to_string()], + value, + )]) + } + + fn map(&mut self, map: &MapType, key_value: Self::T, value: Self::T) -> Result { + Ok(vec![ + MappedField::new(Some(map.key_field.id), vec!["key".to_string()], key_value), + MappedField::new(Some(map.value_field.id), vec!["value".to_string()], value), + ]) + } + + fn primitive(&mut self, _p: &PrimitiveType) -> Result { + Ok([].to_vec()) + } +} + #[cfg(test)] mod tests { + use super::*; + fn make_field( + field_id: Option, + names: Vec<&str>, + fields: Vec, + ) -> MappedField { + MappedField { + field_id, + names: names.into_iter().map(String::from).collect(), + fields, + } + } + #[test] fn test_json_mapped_field_deserialization() { let expected = MappedField { @@ -179,44 +393,48 @@ mod tests { #[test] fn test_json_name_mapping_deserialization() { let name_mapping = r#" - [ - { - "field-id": 1, - "names": [ - "id", - "record_id" - ] - }, - { - "field-id": 2, - "names": [ - "data" - ] - }, { - "field-id": 3, - "names": [ - "location" - ], - "fields": [ + "root": [ + { + "field-id": 1, + "names": [ + "id", + "record_id" + ] + }, { + "field-id": 2, + "names": [ + "data" + ] + }, + { + "field-id": 3, + "names": [ + "location" + ], + "fields": [ + { "field-id": 4, "names": [ "latitude", "lat" ] - }, - { + }, + { "field-id": 5, "names": [ "longitude", "long" ] + } + ] } - ] + ], + "id_to_field": {}, + "name_to_field": {} } - ] - "#; + "#; let name_mapping: NameMapping = serde_json::from_str(name_mapping).unwrap(); assert_eq!(name_mapping, NameMapping { @@ -250,6 +468,8 @@ mod tests { ] } ], + id_to_field: HashMap::new(), + name_to_field: HashMap::new(), }); } @@ -355,8 +575,122 @@ mod tests { ], }, ], + id_to_field: HashMap::new(), + name_to_field: HashMap::new(), }; - let expected = r#"[{"names":["foo"]},{"field-id":2,"names":["bar"]},{"field-id":3,"names":["baz"]},{"field-id":4,"names":["qux"],"fields":[{"field-id":5,"names":["element"]}]},{"field-id":6,"names":["quux"],"fields":[{"field-id":7,"names":["key"]},{"field-id":8,"names":["value"],"fields":[{"field-id":9,"names":["key"]},{"field-id":10,"names":["value"]}]}]},{"field-id":11,"names":["location"],"fields":[{"field-id":12,"names":["element"],"fields":[{"field-id":13,"names":["latitude"]},{"field-id":14,"names":["longitude"]}]}]},{"field-id":15,"names":["person"],"fields":[{"field-id":16,"names":["name"]},{"field-id":17,"names":["age"]}]}]"#; + let expected = r#"{"root":[{"names":["foo"]},{"field-id":2,"names":["bar"]},{"field-id":3,"names":["baz"]},{"field-id":4,"names":["qux"],"fields":[{"field-id":5,"names":["element"]}]},{"field-id":6,"names":["quux"],"fields":[{"field-id":7,"names":["key"]},{"field-id":8,"names":["value"],"fields":[{"field-id":9,"names":["key"]},{"field-id":10,"names":["value"]}]}]},{"field-id":11,"names":["location"],"fields":[{"field-id":12,"names":["element"],"fields":[{"field-id":13,"names":["latitude"]},{"field-id":14,"names":["longitude"]}]}]},{"field-id":15,"names":["person"],"fields":[{"field-id":16,"names":["name"]},{"field-id":17,"names":["age"]}]}],"id_to_field":{},"name_to_field":{}}"#; assert_eq!(serde_json::to_string(&name_mapping).unwrap(), expected); } + + #[test] + fn test_index_by_id_and_index_by_name() { + let field1 = make_field(Some(1), vec!["a", "alpha"], vec![]); + let field3 = make_field(Some(3), vec!["c", "charlie"], vec![]); + let field2 = make_field(Some(2), vec!["b"], vec![field3.clone()]); + + let root = vec![field1.clone(), field2.clone()]; + + let id_to_field = NameMapping::index_by_id(&root); + let expected_id: HashMap = vec![ + (1, field1.clone()), + (2, field2.clone()), + (3, field3.clone()), + ] + .into_iter() + .collect(); + assert_eq!(id_to_field, expected_id); + + let name_to_field = NameMapping::index_by_name(&root); + let expected_name: HashMap = vec![ + ("a".to_string(), field1.clone()), + ("alpha".to_string(), field1.clone()), + ("b".to_string(), field2.clone()), + ("b.c".to_string(), field3.clone()), + ("b.charlie".to_string(), field3.clone()), + ("c".to_string(), field3.clone()), + ("charlie".to_string(), field3.clone()), + ] + .into_iter() + .collect(); + assert_eq!(name_to_field, expected_name); + } + + #[test] + fn test_create_name_mapping() { + let field1 = make_field(Some(10), vec!["id", "record_id"], vec![]); + let field2 = make_field(Some(20), vec!["data"], vec![]); + let field3 = make_field(Some(30), vec!["location"], vec![ + make_field(Some(31), vec!["latitude", "lat"], vec![]), + make_field(Some(32), vec!["longitude", "long"], vec![]), + ]); + let root = vec![field1.clone(), field2.clone(), field3.clone()]; + + let id_to_field = NameMapping::index_by_id(&root); + let name_to_field = NameMapping::index_by_name(&root); + + let mapping = NameMapping::new(root, id_to_field, name_to_field); + + let mut expected_id = HashMap::new(); + expected_id.insert(10, field1); + expected_id.insert(20, field2); + expected_id.insert(30, field3.clone()); + expected_id.insert(31, field3.fields[0].clone()); + expected_id.insert(32, field3.fields[1].clone()); + assert_eq!(mapping.id_to_field, expected_id); + + let expected_name: HashMap = vec![ + ( + "id".to_string(), + mapping.id_to_field.get(&10).unwrap().clone(), + ), + ( + "record_id".to_string(), + mapping.id_to_field.get(&10).unwrap().clone(), + ), + ( + "data".to_string(), + mapping.id_to_field.get(&20).unwrap().clone(), + ), + ( + "location".to_string(), + mapping.id_to_field.get(&30).unwrap().clone(), + ), + ( + "location.latitude".to_string(), + mapping.id_to_field.get(&31).unwrap().clone(), + ), + ( + "location.lat".to_string(), + mapping.id_to_field.get(&31).unwrap().clone(), + ), + ( + "location.longitude".to_string(), + mapping.id_to_field.get(&32).unwrap().clone(), + ), + ( + "location.long".to_string(), + mapping.id_to_field.get(&32).unwrap().clone(), + ), + ( + "latitude".to_string(), + mapping.id_to_field.get(&31).unwrap().clone(), + ), + ( + "lat".to_string(), + mapping.id_to_field.get(&31).unwrap().clone(), + ), + ( + "longitude".to_string(), + mapping.id_to_field.get(&32).unwrap().clone(), + ), + ( + "long".to_string(), + mapping.id_to_field.get(&32).unwrap().clone(), + ), + ] + .into_iter() + .collect(); + + assert_eq!(mapping.name_to_field, expected_name); + } } From 7710dd4d8f93e759ea26e65a501ea82a883e1ca4 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Wed, 7 May 2025 11:43:29 -0700 Subject: [PATCH 2/6] Update mod.rs --- crates/iceberg/src/spec/name_mapping/mod.rs | 38 +++++++-------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/crates/iceberg/src/spec/name_mapping/mod.rs b/crates/iceberg/src/spec/name_mapping/mod.rs index 1f164661e4..8556726fd5 100644 --- a/crates/iceberg/src/spec/name_mapping/mod.rs +++ b/crates/iceberg/src/spec/name_mapping/mod.rs @@ -105,7 +105,7 @@ impl MappedField { Self { field_id, names, - fields: fields, + fields, } } @@ -457,14 +457,12 @@ mod tests { field_id: Some(4), names: vec!["latitude".to_string(), "lat".to_string()], fields: vec![] - } - .into(), + }, MappedField { field_id: Some(5), names: vec!["longitude".to_string(), "long".to_string()], fields: vec![] - } - .into(), + }, ] } ], @@ -499,8 +497,7 @@ mod tests { field_id: Some(5), names: vec!["element".to_string()], fields: vec![], - } - .into()], + }], }, MappedField { field_id: Some(6), @@ -510,8 +507,7 @@ mod tests { field_id: Some(7), names: vec!["key".to_string()], fields: vec![], - } - .into(), + }, MappedField { field_id: Some(8), names: vec!["value".to_string()], @@ -520,17 +516,14 @@ mod tests { field_id: Some(9), names: vec!["key".to_string()], fields: vec![], - } - .into(), + }, MappedField { field_id: Some(10), names: vec!["value".to_string()], fields: vec![], - } - .into(), + }, ], - } - .into(), + }, ], }, MappedField { @@ -544,17 +537,14 @@ mod tests { field_id: Some(13), names: vec!["latitude".to_string()], fields: vec![], - } - .into(), + }, MappedField { field_id: Some(14), names: vec!["longitude".to_string()], fields: vec![], - } - .into(), + }, ], - } - .into()], + }], }, MappedField { field_id: Some(15), @@ -564,14 +554,12 @@ mod tests { field_id: Some(16), names: vec!["name".to_string()], fields: vec![], - } - .into(), + }, MappedField { field_id: Some(17), names: vec!["age".to_string()], fields: vec![], - } - .into(), + }, ], }, ], From 283c02768b8240463f86507a514c5130e5b5c705 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Fri, 9 May 2025 12:49:40 -0700 Subject: [PATCH 3/6] fix --- crates/iceberg/src/expr/visitors/mod.rs | 1 + .../src/expr/visitors/name_mapping_visitor.rs | 35 ++ crates/iceberg/src/spec/name_mapping/mod.rs | 352 +++++++++--------- 3 files changed, 216 insertions(+), 172 deletions(-) create mode 100644 crates/iceberg/src/expr/visitors/name_mapping_visitor.rs diff --git a/crates/iceberg/src/expr/visitors/mod.rs b/crates/iceberg/src/expr/visitors/mod.rs index 0066bbc6d8..57ca7f7ad5 100644 --- a/crates/iceberg/src/expr/visitors/mod.rs +++ b/crates/iceberg/src/expr/visitors/mod.rs @@ -20,6 +20,7 @@ pub(crate) mod expression_evaluator; pub(crate) mod inclusive_metrics_evaluator; pub(crate) mod inclusive_projection; pub(crate) mod manifest_evaluator; +pub(crate) mod name_mapping_visitor; pub(crate) mod page_index_evaluator; pub(crate) mod row_group_metrics_evaluator; pub(crate) mod strict_metrics_evaluator; diff --git a/crates/iceberg/src/expr/visitors/name_mapping_visitor.rs b/crates/iceberg/src/expr/visitors/name_mapping_visitor.rs new file mode 100644 index 0000000000..dfa6e2971d --- /dev/null +++ b/crates/iceberg/src/expr/visitors/name_mapping_visitor.rs @@ -0,0 +1,35 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use crate::spec::MappedField; + +/// A trait for visiting and transforming a name mapping +pub trait NameMappingVisitor { + /// Aggregated result of `MappedField`s + type S; + /// Result type for processing one `MappedField` + type T; + + /// Handles entire `NameMapping` field + fn mapping(&self, field_result: Self::S) -> Self::S; + + /// Takes all visited child maps and merges into one map + fn fields(&self, field_results: Vec) -> Self::S; + + /// Handles a single `MappedField` + fn field(&self, field: &MappedField, field_result: Self::S) -> Self::T; +} diff --git a/crates/iceberg/src/spec/name_mapping/mod.rs b/crates/iceberg/src/spec/name_mapping/mod.rs index 8556726fd5..f7403aff8f 100644 --- a/crates/iceberg/src/spec/name_mapping/mod.rs +++ b/crates/iceberg/src/spec/name_mapping/mod.rs @@ -18,34 +18,44 @@ //! Iceberg name mapping. use std::collections::HashMap; +use std::str::FromStr; +use std::sync::Arc; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DefaultOnNull}; +use crate::expr::visitors::name_mapping_visitor::NameMappingVisitor; use crate::spec::{ visit_schema, ListType, MapType, NestedFieldRef, PrimitiveType, Schema, SchemaVisitor, StructType, }; -use crate::Result; +use crate::{Error, Result}; /// Property name for name mapping. pub const DEFAULT_SCHEMA_NAME_MAPPING: &str = "schema.name-mapping.default"; /// Iceberg fallback field name to ID mapping. #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] +#[serde(transparent)] pub struct NameMapping { - root: Vec, - id_to_field: HashMap, - name_to_field: HashMap, + // this is the one field that actually gets round‑tripped + root: Vec>, + // both of these get skipped during (de)serialization and default to empty + #[serde(skip)] + id_to_field: HashMap>, + #[serde(skip)] + name_to_field: HashMap>, } impl NameMapping { /// Creates new `NameMapping` instance - pub fn new( - root: Vec, - id_to_field: HashMap, - name_to_field: HashMap, - ) -> Self { + pub fn new(schema: &Schema) -> Self { + // Create `MappedField` instances by visiting schema. + let root = visit_schema(schema, &mut CreateMapping).unwrap(); + + let id_to_field = NameMapping::index_by_id(&root); + let name_to_field = NameMapping::index_by_name(&root); + Self { root, id_to_field, @@ -54,7 +64,7 @@ impl NameMapping { } /// Get a reference to fields which are to be mapped from name to field ID. - pub fn fields(&self) -> &[MappedField] { + pub fn fields(&self) -> &[Arc] { &self.root } @@ -65,23 +75,21 @@ impl NameMapping { } /// Returns an index mapping id to `MappedField`` by visiting the schema. - fn index_by_id(mapping: &Vec) -> HashMap { + fn index_by_id(mapping: &Vec>) -> HashMap> { visit_name_mapping(mapping, &IndexById {}) } /// Returns an index mapping names to `MappedField`` by visiting the schema. - fn index_by_name(mapping: &Vec) -> HashMap { + fn index_by_name(mapping: &Vec>) -> HashMap> { visit_name_mapping(mapping, &IndexByName {}) } +} - /// Create mapping from schema - pub fn create_mapping_from_schema(schema: &Schema) -> Self { - let mapped_fields = visit_schema(schema, &mut CreateMapping).unwrap(); - - let id_to_field = NameMapping::index_by_id(&mapped_fields); - let name_to_field = NameMapping::index_by_name(&mapped_fields); +impl FromStr for NameMapping { + type Err = Error; - NameMapping::new(mapped_fields, id_to_field, name_to_field) + fn from_str(s: &str) -> Result { + NameMapping::parse_name_mapping(s) } } @@ -96,12 +104,12 @@ pub struct MappedField { #[serde(default)] #[serde(skip_serializing_if = "Vec::is_empty")] #[serde_as(deserialize_as = "DefaultOnNull")] - fields: Vec, + fields: Vec>, } impl MappedField { /// Create a new [`MappedField`]. - pub fn new(field_id: Option, names: Vec, fields: Vec) -> Self { + pub fn new(field_id: Option, names: Vec, fields: Vec>) -> Self { Self { field_id, names, @@ -120,37 +128,20 @@ impl MappedField { } /// Get a reference to the field mapping for any child fields. - pub fn fields(&self) -> &[MappedField] { + pub fn fields(&self) -> &[Arc] { &self.fields } } -/// A trait for visiting and transforming a name mapping -trait NameMappingVisitor { - /// Aggregated result of `MappedField`s - type S; - /// Result type for processing one `MappedField` - type T; - - /// Handles entire `NameMapping` field - fn mapping(&self, field_result: Self::S) -> Self::S; - - /// Handles accessing multiple `MappedField` - fn fields(&self, field_results: Vec) -> Self::S; - - /// Handles a single `MappedField` - fn field(&self, field: &MappedField, field_result: Self::S) -> Self::T; -} - /// Recursively visits the entire name mapping using visitor -fn visit_name_mapping(namespace: &Vec, visitor: &V) -> V::S +fn visit_name_mapping(namespace: &Vec>, visitor: &V) -> V::S where V: NameMappingVisitor { let root_result = visit_fields(namespace, visitor); visitor.mapping(root_result) } /// Recursively visits a slice of mapped fields using visitor -fn visit_fields(fields: &Vec, visitor: &V) -> V::S +fn visit_fields(fields: &Vec>, visitor: &V) -> V::S where V: NameMappingVisitor { let mut results: Vec = Vec::new(); @@ -166,17 +157,20 @@ where V: NameMappingVisitor { struct IndexByName {} impl NameMappingVisitor for IndexByName { - type S = HashMap; - type T = HashMap; + type S = HashMap>; + type T = HashMap>; - fn mapping(&self, field_result: HashMap) -> HashMap { + fn mapping( + &self, + field_result: HashMap>, + ) -> HashMap> { field_result } fn fields( &self, - field_results: Vec>, - ) -> HashMap { + field_results: Vec>>, + ) -> HashMap> { field_results .into_iter() .fold(HashMap::new(), |mut acc, map| { @@ -188,8 +182,8 @@ impl NameMappingVisitor for IndexByName { fn field( &self, field: &MappedField, - child_result: HashMap, - ) -> HashMap { + child_result: HashMap>, + ) -> HashMap> { let mut result = child_result; for name in &field.names { @@ -200,7 +194,7 @@ impl NameMappingVisitor for IndexByName { } for name in &field.names { - result.insert(name.clone(), field.clone()); + result.insert(name.clone(), Arc::new(field.clone())); } result } @@ -209,14 +203,20 @@ impl NameMappingVisitor for IndexByName { struct IndexById {} impl NameMappingVisitor for IndexById { - type S = HashMap; - type T = HashMap; + type S = HashMap>; + type T = HashMap>; - fn mapping(&self, field_result: HashMap) -> HashMap { + fn mapping( + &self, + field_result: HashMap>, + ) -> HashMap> { field_result } - fn fields(&self, field_results: Vec>) -> HashMap { + fn fields( + &self, + field_results: Vec>>, + ) -> HashMap> { field_results .into_iter() .fold(HashMap::new(), |mut acc, map| { @@ -228,12 +228,12 @@ impl NameMappingVisitor for IndexById { fn field( &self, field: &MappedField, - field_results: HashMap, - ) -> HashMap { + field_results: HashMap>, + ) -> HashMap> { let mut result = field_results; if let Some(id) = field.field_id { - result.insert(id, field.clone()); + result.insert(id, Arc::new(field.clone())); } result } @@ -242,7 +242,7 @@ impl NameMappingVisitor for IndexById { struct CreateMapping; impl SchemaVisitor for CreateMapping { - type T = Vec; + type T = Vec>; fn schema(&mut self, _schema: &Schema, value: Self::T) -> Result { Ok(value) @@ -258,25 +258,37 @@ impl SchemaVisitor for CreateMapping { .iter() .zip(results) .map(|(field, result)| { - MappedField::new(Some(field.id), vec![field.name.clone()], result) + Arc::new(MappedField::new( + Some(field.id), + vec![field.name.clone()], + result, + )) }) - .collect::>(); + .collect::>>(); Ok(mapped_fields) } fn list(&mut self, list: &ListType, value: Self::T) -> Result { - Ok(vec![MappedField::new( + Ok(vec![Arc::new(MappedField::new( Some(list.element_field.id), vec!["element".to_string()], value, - )]) + ))]) } fn map(&mut self, map: &MapType, key_value: Self::T, value: Self::T) -> Result { Ok(vec![ - MappedField::new(Some(map.key_field.id), vec!["key".to_string()], key_value), - MappedField::new(Some(map.value_field.id), vec!["value".to_string()], value), + Arc::new(MappedField::new( + Some(map.key_field.id), + vec!["key".to_string()], + key_value, + )), + Arc::new(MappedField::new( + Some(map.value_field.id), + vec!["value".to_string()], + value, + )), ]) } @@ -289,17 +301,18 @@ impl SchemaVisitor for CreateMapping { mod tests { use super::*; + use crate::spec::{NestedField, Type}; fn make_field( field_id: Option, names: Vec<&str>, - fields: Vec, - ) -> MappedField { - MappedField { + fields: Vec>, + ) -> Arc { + Arc::new(MappedField { field_id, names: names.into_iter().map(String::from).collect(), fields, - } + }) } #[test] @@ -393,78 +406,74 @@ mod tests { #[test] fn test_json_name_mapping_deserialization() { let name_mapping = r#" + [ { - "root": [ - { - "field-id": 1, - "names": [ - "id", - "record_id" - ] - }, - { - "field-id": 2, - "names": [ - "data" - ] - }, + "field-id": 1, + "names": [ + "id", + "record_id" + ] + }, + { + "field-id": 2, + "names": [ + "data" + ] + }, + { + "field-id": 3, + "names": [ + "location" + ], + "fields": [ { - "field-id": 3, - "names": [ - "location" - ], - "fields": [ - { "field-id": 4, "names": [ "latitude", "lat" ] - }, - { + }, + { "field-id": 5, "names": [ "longitude", "long" ] - } - ] } - ], - "id_to_field": {}, - "name_to_field": {} + ] } - "#; + ] + "#; let name_mapping: NameMapping = serde_json::from_str(name_mapping).unwrap(); assert_eq!(name_mapping, NameMapping { root: vec![ - MappedField { + Arc::new(MappedField { field_id: Some(1), names: vec!["id".to_string(), "record_id".to_string()], fields: vec![] - }, - MappedField { + }), + Arc::new(MappedField { field_id: Some(2), names: vec!["data".to_string()], fields: vec![] - }, - MappedField { + }), + Arc::new(MappedField { field_id: Some(3), names: vec!["location".to_string()], fields: vec![ - MappedField { + Arc::new(MappedField { field_id: Some(4), names: vec!["latitude".to_string(), "lat".to_string()], fields: vec![] - }, - MappedField { + }), + Arc::new(MappedField { field_id: Some(5), names: vec!["longitude".to_string(), "long".to_string()], fields: vec![] - }, + }), ] - } + }) ], id_to_field: HashMap::new(), name_to_field: HashMap::new(), @@ -475,98 +484,98 @@ mod tests { fn test_json_name_mapping_serialization() { let name_mapping = NameMapping { root: vec![ - MappedField { + Arc::new(MappedField { field_id: None, names: vec!["foo".to_string()], fields: vec![], - }, - MappedField { + }), + Arc::new(MappedField { field_id: Some(2), names: vec!["bar".to_string()], fields: vec![], - }, - MappedField { + }), + Arc::new(MappedField { field_id: Some(3), names: vec!["baz".to_string()], fields: vec![], - }, - MappedField { + }), + Arc::new(MappedField { field_id: Some(4), names: vec!["qux".to_string()], - fields: vec![MappedField { + fields: vec![Arc::new(MappedField { field_id: Some(5), names: vec!["element".to_string()], fields: vec![], - }], - }, - MappedField { + })], + }), + Arc::new(MappedField { field_id: Some(6), names: vec!["quux".to_string()], fields: vec![ - MappedField { + Arc::new(MappedField { field_id: Some(7), names: vec!["key".to_string()], fields: vec![], - }, - MappedField { + }), + Arc::new(MappedField { field_id: Some(8), names: vec!["value".to_string()], fields: vec![ - MappedField { + Arc::new(MappedField { field_id: Some(9), names: vec!["key".to_string()], fields: vec![], - }, - MappedField { + }), + Arc::new(MappedField { field_id: Some(10), names: vec!["value".to_string()], fields: vec![], - }, + }), ], - }, + }), ], - }, - MappedField { + }), + Arc::new(MappedField { field_id: Some(11), names: vec!["location".to_string()], - fields: vec![MappedField { + fields: vec![Arc::new(MappedField { field_id: Some(12), names: vec!["element".to_string()], fields: vec![ - MappedField { + Arc::new(MappedField { field_id: Some(13), names: vec!["latitude".to_string()], fields: vec![], - }, - MappedField { + }), + Arc::new(MappedField { field_id: Some(14), names: vec!["longitude".to_string()], fields: vec![], - }, + }), ], - }], - }, - MappedField { + })], + }), + Arc::new(MappedField { field_id: Some(15), names: vec!["person".to_string()], fields: vec![ - MappedField { + Arc::new(MappedField { field_id: Some(16), names: vec!["name".to_string()], fields: vec![], - }, - MappedField { + }), + Arc::new(MappedField { field_id: Some(17), names: vec!["age".to_string()], fields: vec![], - }, + }), ], - }, + }), ], id_to_field: HashMap::new(), name_to_field: HashMap::new(), }; - let expected = r#"{"root":[{"names":["foo"]},{"field-id":2,"names":["bar"]},{"field-id":3,"names":["baz"]},{"field-id":4,"names":["qux"],"fields":[{"field-id":5,"names":["element"]}]},{"field-id":6,"names":["quux"],"fields":[{"field-id":7,"names":["key"]},{"field-id":8,"names":["value"],"fields":[{"field-id":9,"names":["key"]},{"field-id":10,"names":["value"]}]}]},{"field-id":11,"names":["location"],"fields":[{"field-id":12,"names":["element"],"fields":[{"field-id":13,"names":["latitude"]},{"field-id":14,"names":["longitude"]}]}]},{"field-id":15,"names":["person"],"fields":[{"field-id":16,"names":["name"]},{"field-id":17,"names":["age"]}]}],"id_to_field":{},"name_to_field":{}}"#; + let expected = r#"[{"names":["foo"]},{"field-id":2,"names":["bar"]},{"field-id":3,"names":["baz"]},{"field-id":4,"names":["qux"],"fields":[{"field-id":5,"names":["element"]}]},{"field-id":6,"names":["quux"],"fields":[{"field-id":7,"names":["key"]},{"field-id":8,"names":["value"],"fields":[{"field-id":9,"names":["key"]},{"field-id":10,"names":["value"]}]}]},{"field-id":11,"names":["location"],"fields":[{"field-id":12,"names":["element"],"fields":[{"field-id":13,"names":["latitude"]},{"field-id":14,"names":["longitude"]}]}]},{"field-id":15,"names":["person"],"fields":[{"field-id":16,"names":["name"]},{"field-id":17,"names":["age"]}]}]"#; assert_eq!(serde_json::to_string(&name_mapping).unwrap(), expected); } @@ -579,7 +588,7 @@ mod tests { let root = vec![field1.clone(), field2.clone()]; let id_to_field = NameMapping::index_by_id(&root); - let expected_id: HashMap = vec![ + let expected_id: HashMap> = vec![ (1, field1.clone()), (2, field2.clone()), (3, field3.clone()), @@ -589,7 +598,7 @@ mod tests { assert_eq!(id_to_field, expected_id); let name_to_field = NameMapping::index_by_name(&root); - let expected_name: HashMap = vec![ + let expected_name: HashMap> = vec![ ("a".to_string(), field1.clone()), ("alpha".to_string(), field1.clone()), ("b".to_string(), field2.clone()), @@ -605,20 +614,39 @@ mod tests { #[test] fn test_create_name_mapping() { - let field1 = make_field(Some(10), vec!["id", "record_id"], vec![]); + let field_1 = NestedField::required(10, "id", Type::Primitive(PrimitiveType::Int)); + let field_2 = NestedField::optional(20, "data", Type::Primitive(PrimitiveType::String)); + let struct_1 = NestedField::optional(31, "latitude", Type::Primitive(PrimitiveType::Float)); + let struct_2 = + NestedField::optional(32, "longitude", Type::Primitive(PrimitiveType::Float)); + let struct_type = Type::Struct(StructType::new(vec![ + struct_1.clone().into(), + struct_2.clone().into(), + ])); + let field_3 = NestedField::required(30, "location", struct_type); + + let schema = Schema::builder() + .with_schema_id(100) + .with_identifier_field_ids(vec![10]) + .with_fields(vec![ + field_1.clone().into(), + field_2.clone().into(), + field_3.clone().into(), + ]) + .build() + .unwrap(); + + let mapping = NameMapping::new(&schema); + + let field1 = make_field(Some(10), vec!["id"], vec![]); let field2 = make_field(Some(20), vec!["data"], vec![]); let field3 = make_field(Some(30), vec!["location"], vec![ - make_field(Some(31), vec!["latitude", "lat"], vec![]), - make_field(Some(32), vec!["longitude", "long"], vec![]), + make_field(Some(31), vec!["latitude"], vec![]), + make_field(Some(32), vec!["longitude"], vec![]), ]); - let root = vec![field1.clone(), field2.clone(), field3.clone()]; - - let id_to_field = NameMapping::index_by_id(&root); - let name_to_field = NameMapping::index_by_name(&root); - - let mapping = NameMapping::new(root, id_to_field, name_to_field); let mut expected_id = HashMap::new(); + expected_id.insert(10, field1); expected_id.insert(20, field2); expected_id.insert(30, field3.clone()); @@ -626,15 +654,11 @@ mod tests { expected_id.insert(32, field3.fields[1].clone()); assert_eq!(mapping.id_to_field, expected_id); - let expected_name: HashMap = vec![ + let expected_name: HashMap> = vec![ ( "id".to_string(), mapping.id_to_field.get(&10).unwrap().clone(), ), - ( - "record_id".to_string(), - mapping.id_to_field.get(&10).unwrap().clone(), - ), ( "data".to_string(), mapping.id_to_field.get(&20).unwrap().clone(), @@ -647,34 +671,18 @@ mod tests { "location.latitude".to_string(), mapping.id_to_field.get(&31).unwrap().clone(), ), - ( - "location.lat".to_string(), - mapping.id_to_field.get(&31).unwrap().clone(), - ), ( "location.longitude".to_string(), mapping.id_to_field.get(&32).unwrap().clone(), ), - ( - "location.long".to_string(), - mapping.id_to_field.get(&32).unwrap().clone(), - ), ( "latitude".to_string(), mapping.id_to_field.get(&31).unwrap().clone(), ), - ( - "lat".to_string(), - mapping.id_to_field.get(&31).unwrap().clone(), - ), ( "longitude".to_string(), mapping.id_to_field.get(&32).unwrap().clone(), ), - ( - "long".to_string(), - mapping.id_to_field.get(&32).unwrap().clone(), - ), ] .into_iter() .collect(); From cf0624c71b9d7cd489dad20a97e2e538b2719450 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Tue, 13 May 2025 15:53:18 -0400 Subject: [PATCH 4/6] fixes --- crates/iceberg/src/expr/visitors/mod.rs | 1 - crates/iceberg/src/spec/name_mapping/mod.rs | 94 +++++++------------ .../name_mapping/visitor.rs} | 7 +- 3 files changed, 37 insertions(+), 65 deletions(-) rename crates/iceberg/src/{expr/visitors/name_mapping_visitor.rs => spec/name_mapping/visitor.rs} (85%) diff --git a/crates/iceberg/src/expr/visitors/mod.rs b/crates/iceberg/src/expr/visitors/mod.rs index 57ca7f7ad5..0066bbc6d8 100644 --- a/crates/iceberg/src/expr/visitors/mod.rs +++ b/crates/iceberg/src/expr/visitors/mod.rs @@ -20,7 +20,6 @@ pub(crate) mod expression_evaluator; pub(crate) mod inclusive_metrics_evaluator; pub(crate) mod inclusive_projection; pub(crate) mod manifest_evaluator; -pub(crate) mod name_mapping_visitor; pub(crate) mod page_index_evaluator; pub(crate) mod row_group_metrics_evaluator; pub(crate) mod strict_metrics_evaluator; diff --git a/crates/iceberg/src/spec/name_mapping/mod.rs b/crates/iceberg/src/spec/name_mapping/mod.rs index f7403aff8f..c8de9d774d 100644 --- a/crates/iceberg/src/spec/name_mapping/mod.rs +++ b/crates/iceberg/src/spec/name_mapping/mod.rs @@ -17,17 +17,19 @@ //! Iceberg name mapping. +mod visitor; + use std::collections::HashMap; use std::str::FromStr; use std::sync::Arc; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DefaultOnNull}; +use visitor::NameMappingVisitor; -use crate::expr::visitors::name_mapping_visitor::NameMappingVisitor; use crate::spec::{ visit_schema, ListType, MapType, NestedFieldRef, PrimitiveType, Schema, SchemaVisitor, - StructType, + StructType, LIST_FIELD_NAME, MAP_KEY_FIELD_NAME, MAP_VALUE_FIELD_NAME, }; use crate::{Error, Result}; @@ -76,12 +78,12 @@ impl NameMapping { /// Returns an index mapping id to `MappedField`` by visiting the schema. fn index_by_id(mapping: &Vec>) -> HashMap> { - visit_name_mapping(mapping, &IndexById {}) + visit_name_mapping(mapping, &IndexById {}).unwrap() } - /// Returns an index mapping names to `MappedField`` by visiting the schema. + /// Returns an index mapping names to `MappedField` by visiting the schema. fn index_by_name(mapping: &Vec>) -> HashMap> { - visit_name_mapping(mapping, &IndexByName {}) + visit_name_mapping(mapping, &IndexByName {}).unwrap() } } @@ -126,39 +128,40 @@ impl MappedField { pub fn names(&self) -> &[String] { &self.names } - - /// Get a reference to the field mapping for any child fields. - pub fn fields(&self) -> &[Arc] { - &self.fields - } } /// Recursively visits the entire name mapping using visitor -fn visit_name_mapping(namespace: &Vec>, visitor: &V) -> V::S -where V: NameMappingVisitor { - let root_result = visit_fields(namespace, visitor); - visitor.mapping(root_result) +fn visit_name_mapping(name_mapping: &Vec>, visitor: &V) -> Result +where + V: NameMappingVisitor, + V::S: IntoIterator + FromIterator<::Item>, +{ + let root_result = visit_fields(name_mapping, visitor); + Ok(visitor.mapping(root_result)) } /// Recursively visits a slice of mapped fields using visitor fn visit_fields(fields: &Vec>, visitor: &V) -> V::S -where V: NameMappingVisitor { - let mut results: Vec = Vec::new(); - - for field in fields { - let child_result = visit_fields(&field.fields, visitor); - let field_result = visitor.field(field, child_result); - results.push(field_result); +where + V: NameMappingVisitor, + V::S: IntoIterator + FromIterator<::Item>, +{ + let mut results: Vec = Vec::with_capacity(fields.len()); + for f in fields { + let child_s = visit_fields(&f.fields, visitor); + let this_s = visitor.field(f, child_s); + results.push(this_s); } - visitor.fields(results) + let merged: V::S = results.into_iter().flat_map(|m| m.into_iter()).collect(); + + visitor.mapping(merged) } struct IndexByName {} impl NameMappingVisitor for IndexByName { type S = HashMap>; - type T = HashMap>; fn mapping( &self, @@ -167,18 +170,6 @@ impl NameMappingVisitor for IndexByName { field_result } - fn fields( - &self, - field_results: Vec>>, - ) -> HashMap> { - field_results - .into_iter() - .fold(HashMap::new(), |mut acc, map| { - acc.extend(map); - acc - }) - } - fn field( &self, field: &MappedField, @@ -204,7 +195,6 @@ struct IndexById {} impl NameMappingVisitor for IndexById { type S = HashMap>; - type T = HashMap>; fn mapping( &self, @@ -213,18 +203,6 @@ impl NameMappingVisitor for IndexById { field_result } - fn fields( - &self, - field_results: Vec>>, - ) -> HashMap> { - field_results - .into_iter() - .fold(HashMap::new(), |mut acc, map| { - acc.extend(map); - acc - }) - } - fn field( &self, field: &MappedField, @@ -272,7 +250,7 @@ impl SchemaVisitor for CreateMapping { fn list(&mut self, list: &ListType, value: Self::T) -> Result { Ok(vec![Arc::new(MappedField::new( Some(list.element_field.id), - vec!["element".to_string()], + vec![LIST_FIELD_NAME.to_string()], value, ))]) } @@ -281,12 +259,12 @@ impl SchemaVisitor for CreateMapping { Ok(vec![ Arc::new(MappedField::new( Some(map.key_field.id), - vec!["key".to_string()], + vec![MAP_KEY_FIELD_NAME.to_string()], key_value, )), Arc::new(MappedField::new( Some(map.value_field.id), - vec!["value".to_string()], + vec![MAP_VALUE_FIELD_NAME.to_string()], value, )), ]) @@ -301,7 +279,7 @@ impl SchemaVisitor for CreateMapping { mod tests { use super::*; - use crate::spec::{NestedField, Type}; + use crate::spec::{NestedField, Type, MAP_VALUE_FIELD_NAME}; fn make_field( field_id: Option, @@ -504,7 +482,7 @@ mod tests { names: vec!["qux".to_string()], fields: vec![Arc::new(MappedField { field_id: Some(5), - names: vec!["element".to_string()], + names: vec![LIST_FIELD_NAME.to_string()], fields: vec![], })], }), @@ -514,21 +492,21 @@ mod tests { fields: vec![ Arc::new(MappedField { field_id: Some(7), - names: vec!["key".to_string()], + names: vec![MAP_KEY_FIELD_NAME.to_string()], fields: vec![], }), Arc::new(MappedField { field_id: Some(8), - names: vec!["value".to_string()], + names: vec![MAP_VALUE_FIELD_NAME.to_string()], fields: vec![ Arc::new(MappedField { field_id: Some(9), - names: vec!["key".to_string()], + names: vec![MAP_KEY_FIELD_NAME.to_string()], fields: vec![], }), Arc::new(MappedField { field_id: Some(10), - names: vec!["value".to_string()], + names: vec![MAP_VALUE_FIELD_NAME.to_string()], fields: vec![], }), ], @@ -540,7 +518,7 @@ mod tests { names: vec!["location".to_string()], fields: vec![Arc::new(MappedField { field_id: Some(12), - names: vec!["element".to_string()], + names: vec![LIST_FIELD_NAME.to_string()], fields: vec![ Arc::new(MappedField { field_id: Some(13), diff --git a/crates/iceberg/src/expr/visitors/name_mapping_visitor.rs b/crates/iceberg/src/spec/name_mapping/visitor.rs similarity index 85% rename from crates/iceberg/src/expr/visitors/name_mapping_visitor.rs rename to crates/iceberg/src/spec/name_mapping/visitor.rs index dfa6e2971d..f6d00787f2 100644 --- a/crates/iceberg/src/expr/visitors/name_mapping_visitor.rs +++ b/crates/iceberg/src/spec/name_mapping/visitor.rs @@ -21,15 +21,10 @@ use crate::spec::MappedField; pub trait NameMappingVisitor { /// Aggregated result of `MappedField`s type S; - /// Result type for processing one `MappedField` - type T; /// Handles entire `NameMapping` field fn mapping(&self, field_result: Self::S) -> Self::S; - /// Takes all visited child maps and merges into one map - fn fields(&self, field_results: Vec) -> Self::S; - /// Handles a single `MappedField` - fn field(&self, field: &MappedField, field_result: Self::S) -> Self::T; + fn field(&self, field: &MappedField, field_result: Self::S) -> Self::S; } From 71202c8dfd5d97dcd67c924c8528994720387d89 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sat, 17 May 2025 00:08:29 -0400 Subject: [PATCH 5/6] fix --- crates/iceberg/src/spec/name_mapping/mod.rs | 71 +++++++++------------ 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/crates/iceberg/src/spec/name_mapping/mod.rs b/crates/iceberg/src/spec/name_mapping/mod.rs index bcfa0711aa..b4f037c1ec 100644 --- a/crates/iceberg/src/spec/name_mapping/mod.rs +++ b/crates/iceberg/src/spec/name_mapping/mod.rs @@ -24,12 +24,12 @@ use std::str::FromStr; use std::sync::Arc; use serde::{Deserialize, Serialize}; -use serde_with::{serde_as, DefaultOnNull}; +use serde_with::{DefaultOnNull, serde_as}; use visitor::NameMappingVisitor; use crate::spec::{ - visit_schema, ListType, MapType, NestedFieldRef, PrimitiveType, Schema, SchemaVisitor, - StructType, LIST_FIELD_NAME, MAP_KEY_FIELD_NAME, MAP_VALUE_FIELD_NAME, + LIST_FIELD_NAME, ListType, MAP_KEY_FIELD_NAME, MAP_VALUE_FIELD_NAME, MapType, NestedFieldRef, + PrimitiveType, Schema, SchemaVisitor, StructType, visit_schema, }; use crate::{Error, Result}; @@ -279,7 +279,7 @@ impl SchemaVisitor for CreateMapping { mod tests { use super::*; - use crate::spec::{NestedField, Type, MAP_VALUE_FIELD_NAME}; + use crate::spec::{MAP_VALUE_FIELD_NAME, NestedField, Type}; fn make_field( field_id: Option, @@ -487,42 +487,33 @@ mod tests { })], }), Arc::new(MappedField { - fields: vec![ - MappedField { - field_id: Some(5), - names: vec!["element".to_string()], - fields: vec![], - } - .into(), - ], - }, - MappedField { - field_id: Some(6), - names: vec!["quux".to_string()], - fields: vec![ - Arc::new(MappedField { - field_id: Some(7), - names: vec![MAP_KEY_FIELD_NAME.to_string()], - fields: vec![], - }), - Arc::new(MappedField { - field_id: Some(8), - names: vec![MAP_VALUE_FIELD_NAME.to_string()], - fields: vec![ - Arc::new(MappedField { - field_id: Some(9), - names: vec![MAP_KEY_FIELD_NAME.to_string()], - fields: vec![], - }), - Arc::new(MappedField { - field_id: Some(10), - names: vec![MAP_VALUE_FIELD_NAME.to_string()], - fields: vec![], - }), - ], - }), - ], - }), + field_id: Some(6), + names: vec!["quux".to_string()], + fields: vec![ + Arc::new(MappedField { + field_id: Some(7), + names: vec![MAP_KEY_FIELD_NAME.to_string()], + fields: vec![], + }), + Arc::new(MappedField { + field_id: Some(8), + names: vec![MAP_VALUE_FIELD_NAME.to_string()], + fields: vec![ + Arc::new(MappedField { + field_id: Some(9), + names: vec![MAP_KEY_FIELD_NAME.to_string()], + fields: vec![], + }), + Arc::new(MappedField { + field_id: Some(10), + names: vec![MAP_VALUE_FIELD_NAME.to_string()], + fields: vec![], + }), + ], + }), + ], + }, + ), Arc::new(MappedField { field_id: Some(11), names: vec!["location".to_string()], From b771c4415ad85077a82b35a5690637260074b8ee Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sat, 17 May 2025 00:09:14 -0400 Subject: [PATCH 6/6] fmt --- crates/iceberg/src/spec/name_mapping/mod.rs | 53 ++++++++++----------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/crates/iceberg/src/spec/name_mapping/mod.rs b/crates/iceberg/src/spec/name_mapping/mod.rs index b4f037c1ec..99548624f3 100644 --- a/crates/iceberg/src/spec/name_mapping/mod.rs +++ b/crates/iceberg/src/spec/name_mapping/mod.rs @@ -487,33 +487,32 @@ mod tests { })], }), Arc::new(MappedField { - field_id: Some(6), - names: vec!["quux".to_string()], - fields: vec![ - Arc::new(MappedField { - field_id: Some(7), - names: vec![MAP_KEY_FIELD_NAME.to_string()], - fields: vec![], - }), - Arc::new(MappedField { - field_id: Some(8), - names: vec![MAP_VALUE_FIELD_NAME.to_string()], - fields: vec![ - Arc::new(MappedField { - field_id: Some(9), - names: vec![MAP_KEY_FIELD_NAME.to_string()], - fields: vec![], - }), - Arc::new(MappedField { - field_id: Some(10), - names: vec![MAP_VALUE_FIELD_NAME.to_string()], - fields: vec![], - }), - ], - }), - ], - }, - ), + field_id: Some(6), + names: vec!["quux".to_string()], + fields: vec![ + Arc::new(MappedField { + field_id: Some(7), + names: vec![MAP_KEY_FIELD_NAME.to_string()], + fields: vec![], + }), + Arc::new(MappedField { + field_id: Some(8), + names: vec![MAP_VALUE_FIELD_NAME.to_string()], + fields: vec![ + Arc::new(MappedField { + field_id: Some(9), + names: vec![MAP_KEY_FIELD_NAME.to_string()], + fields: vec![], + }), + Arc::new(MappedField { + field_id: Some(10), + names: vec![MAP_VALUE_FIELD_NAME.to_string()], + fields: vec![], + }), + ], + }), + ], + }), Arc::new(MappedField { field_id: Some(11), names: vec!["location".to_string()],