1use std::collections::{BTreeSet, HashMap, HashSet, VecDeque};
5
6use serde_json::{Map, Value as Json};
7
8use crate::{DataError, DataType, Field, Row, RowSource, Schema, Value};
9
10pub const MAX_DEPTH: usize = 32;
13
14pub const DEFAULT_SAMPLE: usize = 1000;
17
18pub const MAX_UNKNOWN_KEYS: usize = 100;
21
22#[derive(Debug)]
40pub struct RecordSource<I> {
41 records: I,
42 buffer: VecDeque<Map<String, Json>>,
43 columns: Vec<String>,
44 names: HashSet<String>,
46 schema: Schema,
47 unknown: BTreeSet<String>,
48 more_unknown: bool,
49}
50
51impl<I> RecordSource<I>
52where
53 I: Iterator<Item = Result<Json, DataError>>,
54{
55 pub(crate) fn new(mut records: I, sample: usize) -> Result<Self, DataError> {
57 let mut buffer = VecDeque::new();
58 let mut fields: Vec<Field> = Vec::new();
59 let mut index: HashMap<String, usize> = HashMap::new();
63 let mut first: Vec<usize> = Vec::new();
64 let mut held: Vec<usize> = Vec::new();
65 for number in 0..sample.max(1) {
66 let Some(record) = records.next() else {
67 break;
68 };
69 let record = object(record?);
70 for (key, value) in &record {
71 let seen = field_of(key, value, 0);
72 match index.get(key) {
73 Some(&at) => {
74 fields[at] = merge(fields[at].clone(), seen);
75 held[at] += 1;
76 }
77 None => {
79 index.insert(key.clone(), fields.len());
80 fields.push(seen.nullable(number > 0 || value.is_null()));
81 first.push(number);
82 held.push(1);
83 }
84 }
85 }
86 buffer.push_back(record);
87 }
88 let read = buffer.len();
90 for ((field, first), held) in fields.iter_mut().zip(first).zip(held) {
91 if held < read - first {
92 *field = field.clone().nullable(true);
93 }
94 }
95 Ok(RecordSource {
96 records,
97 buffer,
98 names: index.into_keys().collect(),
99 columns: fields.iter().map(|f| f.name().to_string()).collect(),
100 schema: Schema::new(fields),
101 unknown: BTreeSet::new(),
102 more_unknown: false,
103 })
104 }
105
106 pub fn unknown_keys(&self) -> impl Iterator<Item = &str> {
109 self.unknown.iter().map(String::as_str)
110 }
111
112 pub fn more_unknown_keys(&self) -> bool {
115 self.more_unknown
116 }
117
118 fn row(&mut self, record: Map<String, Json>) -> Row {
119 for key in record.keys() {
120 if !self.names.contains(key) && !self.unknown.contains(key) {
121 if self.unknown.len() < MAX_UNKNOWN_KEYS {
122 self.unknown.insert(key.clone());
123 } else {
124 self.more_unknown = true;
125 }
126 }
127 }
128 self.columns
129 .iter()
130 .zip(self.schema.fields())
131 .map(
132 |(column, field)| match record.get(column).map_or(Value::Null, cell) {
133 Value::Int(n) if field.data_type() == &DataType::Float => {
135 Value::Float(n as f64)
136 }
137 other => other,
138 },
139 )
140 .collect()
141 }
142}
143
144impl<I> RowSource for RecordSource<I>
145where
146 I: Iterator<Item = Result<Json, DataError>>,
147{
148 fn columns(&self) -> &[String] {
149 &self.columns
150 }
151
152 fn schema(&self) -> Option<&Schema> {
153 Some(&self.schema)
154 }
155
156 fn next_row(&mut self) -> Option<Result<Row, DataError>> {
157 let record = match self.buffer.pop_front() {
158 Some(record) => record,
159 None => match self.records.next()? {
160 Ok(record) => object(record),
161 Err(error) => return Some(Err(error)),
162 },
163 };
164 Some(Ok(self.row(record)))
165 }
166}
167
168fn object(record: Json) -> Map<String, Json> {
170 match record {
171 Json::Object(map) => map,
172 other => Map::from_iter([("value".to_string(), other)]),
173 }
174}
175
176pub(crate) fn cell(value: &Json) -> Value {
178 match value {
179 Json::Null => Value::Null,
180 Json::Bool(b) => Value::Str(b.to_string()),
181 Json::Number(n) => match n.as_i64() {
182 Some(i) => Value::Int(i),
183 None => n.as_f64().map_or(Value::Null, Value::Float),
184 },
185 Json::String(s) => Value::Str(s.clone()),
186 other => Value::Str(other.to_string()),
187 }
188}
189
190fn field_of(name: &str, value: &Json, depth: usize) -> Field {
192 let data_type = match value {
193 Json::Null => return Field::new(name, DataType::Unknown),
194 _ if depth >= MAX_DEPTH => DataType::Any,
195 Json::Bool(_) => DataType::Boolean,
196 Json::Number(n) if n.is_f64() => DataType::Float,
197 Json::Number(_) => DataType::Integer,
198 Json::String(_) => DataType::String,
199 Json::Array(items) => {
200 let item = items
201 .iter()
202 .map(|item| field_of("item", item, depth + 1))
203 .reduce(merge)
204 .unwrap_or_else(|| Field::new("item", DataType::Unknown));
205 DataType::List(Box::new(item))
206 }
207 Json::Object(map) => {
208 let fields = map.iter().map(|(k, v)| field_of(k, v, depth + 1)).collect();
209 DataType::Struct(fields)
210 }
211 };
212 Field::new(name, data_type).nullable(false)
213}
214
215fn merge(a: Field, b: Field) -> Field {
217 let nullable = a.is_nullable() || b.is_nullable();
218 let data_type = match (a.data_type().clone(), b.data_type().clone()) {
219 (DataType::Unknown, other) | (other, DataType::Unknown) => other,
220 (x, y) if x == y => x,
221 (DataType::Integer, DataType::Float) | (DataType::Float, DataType::Integer) => {
222 DataType::Float
223 }
224 (DataType::List(x), DataType::List(y)) => DataType::List(Box::new(merge(*x, *y))),
225 (DataType::Struct(xs), DataType::Struct(ys)) => {
226 let at: HashMap<String, usize> = ys
228 .iter()
229 .enumerate()
230 .map(|(i, y)| (y.name().to_string(), i))
231 .collect();
232 let mut ys: Vec<Option<Field>> = ys.into_iter().map(Some).collect();
233 let mut fields: Vec<Field> = xs
234 .into_iter()
235 .map(|x| match at.get(x.name()).and_then(|&i| ys[i].take()) {
236 Some(y) => merge(x, y),
237 None => x.nullable(true),
238 })
239 .collect();
240 fields.extend(ys.into_iter().flatten().map(|y| y.nullable(true)));
241 DataType::Struct(fields)
242 }
243 _ => DataType::Any,
244 };
245 a.with_type(data_type).nullable(nullable)
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251 use serde_json::json;
252
253 fn source(
254 records: Vec<Json>,
255 sample: usize,
256 ) -> RecordSource<impl Iterator<Item = Result<Json, DataError>>> {
257 RecordSource::new(records.into_iter().map(Ok), sample).unwrap()
258 }
259
260 #[test]
261 fn the_sample_decides_columns_and_types() {
262 let source = source(
263 vec![
264 json!({"id": 1, "score": 2, "tags": ["a"], "meta": {"x": 1}}),
265 json!({"id": 2, "score": 2.5, "tags": [], "meta": {"y": "z"}, "extra": null}),
266 ],
267 10,
268 );
269 assert_eq!(source.columns(), ["id", "score", "tags", "meta", "extra"]);
270 let schema = source.schema().unwrap();
271 let types: Vec<String> = schema
272 .fields()
273 .iter()
274 .map(|f| f.data_type().to_string())
275 .collect();
276 assert_eq!(
277 types,
278 ["integer", "float", "list<string>", "struct", "unknown"]
279 );
280 assert!(!schema.fields()[0].is_nullable());
281 assert!(schema.fields()[4].is_nullable());
282 let meta: Vec<(&str, bool)> = schema.fields()[3]
283 .children()
284 .map(|f| (f.name(), f.is_nullable()))
285 .collect();
286 assert_eq!(meta, [("x", true), ("y", true)]);
287 }
288
289 #[test]
290 fn keys_after_the_sample_are_reported_not_invented() {
291 let mut source = source(
292 vec![json!({"a": 1}), json!({"a": "x", "b": true}), json!(7)],
293 1,
294 );
295 assert_eq!(source.columns(), ["a"]);
296 assert_eq!(source.next_row().unwrap().unwrap(), [Value::Int(1)]);
297 assert_eq!(source.next_row().unwrap().unwrap(), [Value::from("x")]);
298 assert_eq!(source.next_row().unwrap().unwrap(), [Value::Null]);
300 assert!(source.next_row().is_none());
301 assert_eq!(source.unknown_keys().collect::<Vec<_>>(), ["b", "value"]);
302 }
303
304 #[test]
305 fn cells_keep_scalars_and_write_nested_values_as_json() {
306 assert_eq!(cell(&json!(true)), Value::from("true"));
307 assert_eq!(cell(&json!(1.5)), Value::Float(1.5));
308 assert_eq!(cell(&json!(u64::MAX)), Value::Float(u64::MAX as f64));
309 assert_eq!(cell(&json!({"a": [1, 2]})), Value::from(r#"{"a":[1,2]}"#));
310 }
311}