use super::{ArrowPlan, NormalisePlan};
use arrow::array::{Array, AsArray, PrimitiveArray};
use arrow::datatypes::{DataType, Field, FieldRef, Fields, Float64Type};
use arrow::error::ArrowError;
use arrow::json::writer::{make_encoder, Encoder, EncoderFactory, EncoderOptions, NullableEncoder};
use rayon::prelude::*;
use std::sync::Arc;
pub fn normalise_to_json(
plan: &NormalisePlan<'_>,
fields: Option<&Fields>,
rows: &[Option<&str>],
) -> Vec<String> {
let by_value = |row: &Option<&str>| {
let value = row
.and_then(|row| serde_json::from_str(row).ok())
.unwrap_or(serde_json::Value::Null);
serde_json::to_string(&plan.normalise(value).0).expect("a Value serialises")
};
let fields = fields.filter(|fields| !fields.iter().any(|f| holds_empty_struct(f.data_type())));
let columns: usize = fields.map_or(0, |f| f.iter().map(|f| leaf_columns(f.data_type())).sum());
let least = columns.div_ceil(8);
let typed = fields
.filter(|_| rows.len() >= least)
.and_then(|fields| plan.to_arrow(fields));
let Some(typed) = typed else {
return rows.par_iter().map(by_value).collect();
};
let missing = by_value(&None);
let batch = rows
.len()
.div_ceil(rayon::current_num_threads() * 4)
.max(least)
.max(1);
rows.par_chunks(batch)
.enumerate()
.flat_map_iter(|(b, chunk)| {
written(&typed, chunk, b * batch, &missing)
.unwrap_or_else(|| chunk.iter().map(by_value).collect())
})
.collect()
}
fn leaf_columns(data_type: &DataType) -> usize {
match data_type {
DataType::Struct(fields) => fields
.iter()
.map(|f| leaf_columns(f.data_type()))
.sum::<usize>()
.max(1),
DataType::List(item) | DataType::Map(item, _) => leaf_columns(item.data_type()),
_ => 1,
}
}
fn holds_empty_struct(data_type: &DataType) -> bool {
match data_type {
DataType::Struct(fields) => {
fields.is_empty() || fields.iter().any(|f| holds_empty_struct(f.data_type()))
}
DataType::List(item) | DataType::Map(item, _) => holds_empty_struct(item.data_type()),
_ => false,
}
}
fn written(
typed: &ArrowPlan<'_>,
chunk: &[Option<&str>],
first_row: usize,
missing: &str,
) -> Option<Vec<String>> {
let (array, _) = typed.normalise(chunk, first_row)?;
let field: FieldRef = Arc::new(Field::new("", array.data_type().clone(), true));
let options = EncoderOptions::default()
.with_explicit_nulls(true)
.with_encoder_factory(Arc::new(SerdeFloats));
let mut encoder = make_encoder(&field, &array, &options).ok()?;
let mut buf = Vec::new();
(0..array.len())
.map(|i| {
if chunk[i].is_none() {
return Some(missing.to_string());
}
buf.clear();
encoder.encode(i, &mut buf);
String::from_utf8(buf.clone()).ok()
})
.collect()
}
#[derive(Debug)]
struct SerdeFloats;
impl EncoderFactory for SerdeFloats {
fn make_default_encoder<'a>(
&self,
_field: &'a FieldRef,
array: &'a dyn Array,
_options: &'a EncoderOptions,
) -> Result<Option<NullableEncoder<'a>>, ArrowError> {
if array.data_type() != &DataType::Float64 {
return Ok(None);
}
let floats = array.as_primitive::<Float64Type>();
let nulls = floats.nulls().cloned();
Ok(Some(NullableEncoder::new(Box::new(Floats(floats)), nulls)))
}
}
struct Floats<'a>(&'a PrimitiveArray<Float64Type>);
impl Encoder for Floats<'_> {
fn encode(&mut self, idx: usize, out: &mut Vec<u8>) {
serde_json::to_writer(&mut *out, &self.0.value(idx)).expect("a float serialises");
}
}