1use arrow::datatypes::{DataType, Field, Schema as ArrowSchema};
10use lance_core::{Error, Result};
11use lance_namespace_reqwest_client::models::{JsonArrowDataType, JsonArrowField, JsonArrowSchema};
12
13pub fn arrow_schema_to_json(arrow_schema: &ArrowSchema) -> Result<JsonArrowSchema> {
15 let fields: Result<Vec<JsonArrowField>> = arrow_schema
16 .fields()
17 .iter()
18 .map(|f| arrow_field_to_json(f.as_ref()))
19 .collect();
20
21 let metadata = if arrow_schema.metadata().is_empty() {
22 None
23 } else {
24 Some(arrow_schema.metadata().clone())
25 };
26
27 Ok(JsonArrowSchema {
28 fields: fields?,
29 metadata,
30 })
31}
32
33fn arrow_field_to_json(arrow_field: &Field) -> Result<JsonArrowField> {
35 let data_type = arrow_type_to_json(arrow_field.data_type())?;
36
37 Ok(JsonArrowField {
38 name: arrow_field.name().clone(),
39 nullable: arrow_field.is_nullable(),
40 r#type: Box::new(data_type),
41 metadata: if arrow_field.metadata().is_empty() {
42 None
43 } else {
44 Some(arrow_field.metadata().clone())
45 },
46 })
47}
48
49fn arrow_type_to_json(data_type: &DataType) -> Result<JsonArrowDataType> {
51 match data_type {
52 DataType::Null => Ok(JsonArrowDataType::new("null".to_string())),
54 DataType::Boolean => Ok(JsonArrowDataType::new("bool".to_string())),
55 DataType::Int8 => Ok(JsonArrowDataType::new("int8".to_string())),
56 DataType::UInt8 => Ok(JsonArrowDataType::new("uint8".to_string())),
57 DataType::Int16 => Ok(JsonArrowDataType::new("int16".to_string())),
58 DataType::UInt16 => Ok(JsonArrowDataType::new("uint16".to_string())),
59 DataType::Int32 => Ok(JsonArrowDataType::new("int32".to_string())),
60 DataType::UInt32 => Ok(JsonArrowDataType::new("uint32".to_string())),
61 DataType::Int64 => Ok(JsonArrowDataType::new("int64".to_string())),
62 DataType::UInt64 => Ok(JsonArrowDataType::new("uint64".to_string())),
63 DataType::Float16 => Ok(JsonArrowDataType::new("float16".to_string())),
64 DataType::Float32 => Ok(JsonArrowDataType::new("float32".to_string())),
65 DataType::Float64 => Ok(JsonArrowDataType::new("float64".to_string())),
66 DataType::Decimal32(precision, scale) => {
67 let mut dt = JsonArrowDataType::new("decimal32".to_string());
68 dt.length = Some(*precision as i64 * 1000 + *scale as i64); Ok(dt)
70 }
71 DataType::Decimal64(precision, scale) => {
72 let mut dt = JsonArrowDataType::new("decimal64".to_string());
73 dt.length = Some(*precision as i64 * 1000 + *scale as i64); Ok(dt)
75 }
76 DataType::Decimal128(precision, scale) => {
77 let mut dt = JsonArrowDataType::new("decimal128".to_string());
78 dt.length = Some(*precision as i64 * 1000 + *scale as i64); Ok(dt)
80 }
81 DataType::Decimal256(precision, scale) => {
82 let mut dt = JsonArrowDataType::new("decimal256".to_string());
83 dt.length = Some(*precision as i64 * 1000 + *scale as i64); Ok(dt)
85 }
86 DataType::Date32 => Ok(JsonArrowDataType::new("date32".to_string())),
87 DataType::Date64 => Ok(JsonArrowDataType::new("date64".to_string())),
88 DataType::Time32(_) => Ok(JsonArrowDataType::new("time32".to_string())),
89 DataType::Time64(_) => Ok(JsonArrowDataType::new("time64".to_string())),
90 DataType::Timestamp(_, _tz) => {
91 Ok(JsonArrowDataType::new("timestamp".to_string()))
93 }
94 DataType::Duration(_) => Ok(JsonArrowDataType::new("duration".to_string())),
95 DataType::Interval(_) => Ok(JsonArrowDataType::new("interval".to_string())),
96
97 DataType::Utf8 => Ok(JsonArrowDataType::new("utf8".to_string())),
99 DataType::LargeUtf8 => Ok(JsonArrowDataType::new("large_utf8".to_string())),
100 DataType::Binary => Ok(JsonArrowDataType::new("binary".to_string())),
101 DataType::LargeBinary => Ok(JsonArrowDataType::new("large_binary".to_string())),
102 DataType::FixedSizeBinary(size) => {
103 let mut dt = JsonArrowDataType::new("fixed_size_binary".to_string());
104 dt.length = Some(*size as i64);
105 Ok(dt)
106 }
107
108 DataType::List(field) => {
110 let inner_type = arrow_type_to_json(field.data_type())?;
111 let inner_field = JsonArrowField {
112 name: field.name().clone(),
113 nullable: field.is_nullable(),
114 r#type: Box::new(inner_type),
115 metadata: if field.metadata().is_empty() {
116 None
117 } else {
118 Some(field.metadata().clone())
119 },
120 };
121 Ok(JsonArrowDataType {
122 r#type: "list".to_string(),
123 fields: Some(vec![inner_field]),
124 length: None,
125 })
126 }
127 DataType::LargeList(field) => {
128 let inner_type = arrow_type_to_json(field.data_type())?;
129 let inner_field = JsonArrowField {
130 name: field.name().clone(),
131 nullable: field.is_nullable(),
132 r#type: Box::new(inner_type),
133 metadata: if field.metadata().is_empty() {
134 None
135 } else {
136 Some(field.metadata().clone())
137 },
138 };
139 Ok(JsonArrowDataType {
140 r#type: "large_list".to_string(),
141 fields: Some(vec![inner_field]),
142 length: None,
143 })
144 }
145 DataType::FixedSizeList(field, size) => {
146 let inner_type = arrow_type_to_json(field.data_type())?;
147 let inner_field = JsonArrowField {
148 name: field.name().clone(),
149 nullable: field.is_nullable(),
150 r#type: Box::new(inner_type),
151 metadata: if field.metadata().is_empty() {
152 None
153 } else {
154 Some(field.metadata().clone())
155 },
156 };
157 Ok(JsonArrowDataType {
158 r#type: "fixed_size_list".to_string(),
159 fields: Some(vec![inner_field]),
160 length: Some(*size as i64),
161 })
162 }
163 DataType::Struct(fields) => {
164 let json_fields: Result<Vec<JsonArrowField>> = fields
165 .iter()
166 .map(|f| arrow_field_to_json(f.as_ref()))
167 .collect();
168 Ok(JsonArrowDataType {
169 r#type: "struct".to_string(),
170 fields: Some(json_fields?),
171 length: None,
172 })
173 }
174 DataType::Union(_, _) => {
175 Ok(JsonArrowDataType::new("union".to_string()))
177 }
178 DataType::Dictionary(_, value_type) => {
179 arrow_type_to_json(value_type)
181 }
182
183 DataType::Map(entries_field, keys_sorted) => {
184 if *keys_sorted {
185 return Err(Error::namespace(format!(
186 "Map types with keys_sorted=true are not yet supported for JSON conversion: {:?}",
187 data_type
188 )));
189 }
190 let inner_type = arrow_type_to_json(entries_field.data_type())?;
191 let inner_field = JsonArrowField {
192 name: entries_field.name().clone(),
193 nullable: entries_field.is_nullable(),
194 r#type: Box::new(inner_type),
195 metadata: if entries_field.metadata().is_empty() {
196 None
197 } else {
198 Some(entries_field.metadata().clone())
199 },
200 };
201 Ok(JsonArrowDataType {
202 r#type: "map".to_string(),
203 fields: Some(vec![inner_field]),
204 length: None,
205 })
206 }
207
208 DataType::RunEndEncoded(_, _) => Err(Error::namespace(format!(
210 "RunEndEncoded type is not yet supported for JSON conversion: {:?}",
211 data_type
212 ))),
213 DataType::ListView(_) | DataType::LargeListView(_) => Err(Error::namespace(format!(
214 "ListView types are not yet supported for JSON conversion: {:?}",
215 data_type
216 ))),
217 DataType::Utf8View | DataType::BinaryView => Err(Error::namespace(format!(
218 "View types are not yet supported for JSON conversion: {:?}",
219 data_type
220 ))),
221 }
222}
223
224pub fn convert_json_arrow_schema(json_schema: &JsonArrowSchema) -> Result<ArrowSchema> {
226 let fields: Result<Vec<Field>> = json_schema
227 .fields
228 .iter()
229 .map(convert_json_arrow_field)
230 .collect();
231
232 let metadata = json_schema.metadata.as_ref().cloned().unwrap_or_default();
233
234 Ok(ArrowSchema::new_with_metadata(fields?, metadata))
235}
236
237pub fn convert_json_arrow_field(json_field: &JsonArrowField) -> Result<Field> {
239 let data_type = convert_json_arrow_type(&json_field.r#type)?;
240 let nullable = json_field.nullable;
241
242 let field = Field::new(&json_field.name, data_type, nullable);
243 Ok(match json_field.metadata.as_ref() {
244 Some(metadata) => field.with_metadata(metadata.clone()),
245 None => field,
246 })
247}
248
249pub fn convert_json_arrow_type(json_type: &JsonArrowDataType) -> Result<DataType> {
251 let type_name = json_type.r#type.to_lowercase();
252
253 match type_name.as_str() {
254 "null" => Ok(DataType::Null),
255 "bool" | "boolean" => Ok(DataType::Boolean),
256 "int8" => Ok(DataType::Int8),
257 "uint8" => Ok(DataType::UInt8),
258 "int16" => Ok(DataType::Int16),
259 "uint16" => Ok(DataType::UInt16),
260 "int32" => Ok(DataType::Int32),
261 "uint32" => Ok(DataType::UInt32),
262 "int64" => Ok(DataType::Int64),
263 "uint64" => Ok(DataType::UInt64),
264 "float32" => Ok(DataType::Float32),
265 "float64" => Ok(DataType::Float64),
266 "utf8" => Ok(DataType::Utf8),
267 "binary" => Ok(DataType::Binary),
268 _ => Err(Error::namespace(format!(
269 "Unsupported Arrow type: {}",
270 type_name
271 ))),
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278 use std::collections::HashMap;
279 use std::sync::Arc;
280
281 #[test]
282 fn test_extension_metadata_preserved_in_json_roundtrip() {
283 const ARROW_EXT_NAME_KEY: &str = "ARROW:extension:name";
284 const LANCE_JSON_EXT_NAME: &str = "lance.json";
285
286 let meta_field =
287 Field::new("meta", DataType::Binary, true).with_metadata(HashMap::from([(
288 ARROW_EXT_NAME_KEY.to_string(),
289 LANCE_JSON_EXT_NAME.to_string(),
290 )]));
291 let arrow_schema =
292 ArrowSchema::new(vec![Field::new("id", DataType::Int32, false), meta_field]);
293
294 let json_schema = arrow_schema_to_json(&arrow_schema).unwrap();
295 let meta_json_field = json_schema
296 .fields
297 .iter()
298 .find(|f| f.name == "meta")
299 .unwrap();
300 assert!(
301 meta_json_field
302 .metadata
303 .as_ref()
304 .unwrap()
305 .contains_key(ARROW_EXT_NAME_KEY)
306 );
307
308 let roundtrip = convert_json_arrow_schema(&json_schema).unwrap();
309 let meta_field = roundtrip.field_with_name("meta").unwrap();
310 assert_eq!(
311 meta_field.metadata().get(ARROW_EXT_NAME_KEY),
312 Some(&LANCE_JSON_EXT_NAME.to_string())
313 );
314 }
315
316 #[test]
317 fn test_convert_basic_types() {
318 let int_type = JsonArrowDataType::new("int32".to_string());
320 let result = convert_json_arrow_type(&int_type).unwrap();
321 assert_eq!(result, DataType::Int32);
322
323 let string_type = JsonArrowDataType::new("utf8".to_string());
325 let result = convert_json_arrow_type(&string_type).unwrap();
326 assert_eq!(result, DataType::Utf8);
327
328 let float_type = JsonArrowDataType::new("float64".to_string());
330 let result = convert_json_arrow_type(&float_type).unwrap();
331 assert_eq!(result, DataType::Float64);
332
333 let binary_type = JsonArrowDataType::new("binary".to_string());
335 let result = convert_json_arrow_type(&binary_type).unwrap();
336 assert_eq!(result, DataType::Binary);
337 }
338
339 #[test]
340 fn test_convert_field() {
341 let int_type = JsonArrowDataType::new("int32".to_string());
342 let field = JsonArrowField {
343 name: "test_field".to_string(),
344 r#type: Box::new(int_type),
345 nullable: false,
346 metadata: None,
347 };
348
349 let result = convert_json_arrow_field(&field).unwrap();
350 assert_eq!(result.name(), "test_field");
351 assert_eq!(result.data_type(), &DataType::Int32);
352 assert!(!result.is_nullable());
353 }
354
355 #[test]
356 fn test_convert_schema() {
357 let int_type = JsonArrowDataType::new("int32".to_string());
358 let string_type = JsonArrowDataType::new("utf8".to_string());
359
360 let id_field = JsonArrowField {
361 name: "id".to_string(),
362 r#type: Box::new(int_type),
363 nullable: false,
364 metadata: None,
365 };
366
367 let name_field = JsonArrowField {
368 name: "name".to_string(),
369 r#type: Box::new(string_type),
370 nullable: true,
371 metadata: None,
372 };
373
374 let mut metadata = HashMap::new();
375 metadata.insert("key".to_string(), "value".to_string());
376
377 let schema = JsonArrowSchema {
378 fields: vec![id_field, name_field],
379 metadata: Some(metadata.clone()),
380 };
381
382 let result = convert_json_arrow_schema(&schema).unwrap();
383 assert_eq!(result.fields().len(), 2);
384 assert_eq!(result.field(0).name(), "id");
385 assert_eq!(result.field(1).name(), "name");
386 assert_eq!(result.metadata(), &metadata);
387 }
388
389 #[test]
390 fn test_unsupported_type() {
391 let unsupported_type = JsonArrowDataType::new("unsupported".to_string());
392 let result = convert_json_arrow_type(&unsupported_type);
393 assert!(result.is_err());
394 assert!(
395 result
396 .unwrap_err()
397 .to_string()
398 .contains("Unsupported Arrow type")
399 );
400 }
401
402 #[test]
403 fn test_list_type() {
404 use arrow::datatypes::Field;
405
406 let inner_field = Field::new("item", DataType::Int32, true);
407 let list_type = DataType::List(Arc::new(inner_field));
408
409 let result = arrow_type_to_json(&list_type).unwrap();
410 assert_eq!(result.r#type, "list");
411 assert!(result.fields.is_some());
412 let fields = result.fields.unwrap();
413 assert_eq!(fields.len(), 1);
414 assert_eq!(fields[0].name, "item");
415 assert_eq!(fields[0].r#type.r#type, "int32");
416 }
417
418 #[test]
419 fn test_struct_type() {
420 use arrow::datatypes::Field;
421
422 let fields = vec![
423 Field::new("id", DataType::Int64, false),
424 Field::new("name", DataType::Utf8, true),
425 ];
426 let struct_type = DataType::Struct(fields.into());
427
428 let result = arrow_type_to_json(&struct_type).unwrap();
429 assert_eq!(result.r#type, "struct");
430 assert!(result.fields.is_some());
431 let json_fields = result.fields.unwrap();
432 assert_eq!(json_fields.len(), 2);
433 assert_eq!(json_fields[0].name, "id");
434 assert_eq!(json_fields[0].r#type.r#type, "int64");
435 assert_eq!(json_fields[1].name, "name");
436 assert_eq!(json_fields[1].r#type.r#type, "utf8");
437 }
438
439 #[test]
440 fn test_fixed_size_list_type() {
441 use arrow::datatypes::Field;
442
443 let inner_field = Field::new("item", DataType::Float32, false);
444 let fixed_list_type = DataType::FixedSizeList(Arc::new(inner_field), 3);
445
446 let result = arrow_type_to_json(&fixed_list_type).unwrap();
447 assert_eq!(result.r#type, "fixed_size_list");
448 assert_eq!(result.length, Some(3));
449 assert!(result.fields.is_some());
450 let fields = result.fields.unwrap();
451 assert_eq!(fields.len(), 1);
452 assert_eq!(fields[0].r#type.r#type, "float32");
453 }
454
455 #[test]
456 fn test_nested_struct_with_list() {
457 use arrow::datatypes::Field;
458
459 let inner_list_field = Field::new("item", DataType::Utf8, true);
460 let list_type = DataType::List(Arc::new(inner_list_field));
461
462 let struct_fields = vec![
463 Field::new("id", DataType::Int32, false),
464 Field::new("tags", list_type, true),
465 ];
466 let struct_type = DataType::Struct(struct_fields.into());
467
468 let result = arrow_type_to_json(&struct_type).unwrap();
469 assert_eq!(result.r#type, "struct");
470 let json_fields = result.fields.unwrap();
471 assert_eq!(json_fields.len(), 2);
472 assert_eq!(json_fields[0].name, "id");
473 assert_eq!(json_fields[1].name, "tags");
474 assert_eq!(json_fields[1].r#type.r#type, "list");
475
476 let list_fields = json_fields[1].r#type.fields.as_ref().unwrap();
478 assert_eq!(list_fields.len(), 1);
479 assert_eq!(list_fields[0].r#type.r#type, "utf8");
480 }
481
482 #[test]
483 fn test_map_type_supported() {
484 use arrow::datatypes::Field;
485
486 let key_field = Field::new("keys", DataType::Utf8, false);
487 let value_field = Field::new("values", DataType::Int32, true);
488 let map_type = DataType::Map(
489 Arc::new(Field::new(
490 "entries",
491 DataType::Struct(vec![key_field, value_field].into()),
492 false,
493 )),
494 false,
495 );
496
497 let result = arrow_type_to_json(&map_type);
498 assert!(result.is_ok());
499 let json_type = result.unwrap();
500 assert_eq!(json_type.r#type, "map");
501 assert!(json_type.fields.is_some());
502
503 let fields = json_type.fields.unwrap();
504 assert_eq!(fields.len(), 1);
505 assert_eq!(fields[0].name, "entries");
506 assert_eq!(fields[0].r#type.r#type, "struct");
507 }
508
509 #[test]
510 fn test_additional_types() {
511 let date32 = arrow_type_to_json(&DataType::Date32).unwrap();
513 assert_eq!(date32.r#type, "date32");
514
515 let date64 = arrow_type_to_json(&DataType::Date64).unwrap();
516 assert_eq!(date64.r#type, "date64");
517
518 let fixed_binary = arrow_type_to_json(&DataType::FixedSizeBinary(16)).unwrap();
520 assert_eq!(fixed_binary.r#type, "fixed_size_binary");
521 assert_eq!(fixed_binary.length, Some(16));
522
523 let float16 = arrow_type_to_json(&DataType::Float16).unwrap();
525 assert_eq!(float16.r#type, "float16");
526 }
527}