1use std::collections::HashMap;
4
5use serde_json::{Map, Value};
6
7use super::ir::{RowCol, RowShape};
8
9pub fn restore_rows(shape: &RowShape, rows: &[Value]) -> Value {
16 let mut index: HashMap<Value, usize> = HashMap::new();
20 let mut buckets: Vec<Vec<&Value>> = Vec::new();
21 for (i, row) in rows.iter().enumerate() {
22 let key = root_key(shape, row).unwrap_or_else(|| Value::from(i as u64));
23 match index.get(&key) {
24 Some(&idx) => buckets[idx].push(row),
25 None => {
26 index.insert(key, buckets.len());
27 buckets.push(vec![row]);
28 }
29 }
30 }
31
32 let cols: Vec<&RowCol> = shape.columns.iter().collect();
33 let docs: Vec<Value> = buckets
34 .into_iter()
35 .map(|group| {
36 let present = shape
39 .present_alias
40 .as_deref()
41 .and_then(|a| row_val(group[0], a).as_str())
42 .map(|s| s.to_owned());
43 Value::Object(build_object(&cols, &group, 0, present.as_deref()))
44 })
45 .collect();
46 Value::Array(docs)
47}
48
49fn build_object(
55 cols: &[&RowCol],
56 rows: &[&Value],
57 path_len: usize,
58 present: Option<&str>,
59) -> Map<String, Value> {
60 let mut obj = Map::new();
61 for c in cols {
62 if c.json_path.len() == path_len + 1 || (!c.is_array && c.json_path.len() > path_len + 1) {
63 let mut val = row_val(rows[0], &c.alias).clone();
64 if c.is_json {
66 val = parse_json_col(val);
67 }
68 merge_path(
69 &mut obj,
70 &c.json_path[path_len..],
71 val,
72 present,
73 c.always,
74 c.is_bool,
75 );
76 }
77 }
78 let mut rels: Vec<String> = Vec::new();
80 for c in cols {
81 if c.is_array && c.json_path.len() > path_len + 1 {
82 let r = &c.json_path[path_len];
83 if !rels.iter().any(|x| x == r) {
84 rels.push(r.clone());
85 }
86 }
87 }
88 for r in rels {
89 let sub: Vec<&RowCol> = cols
90 .iter()
91 .copied()
92 .filter(|c| {
93 c.is_array && c.json_path.len() > path_len + 1 && c.json_path[path_len] == r
94 })
95 .collect();
96 obj.insert(r.clone(), relation_value(&sub, rows, path_len));
97 }
98 obj
99}
100
101fn relation_value(cols: &[&RowCol], rows: &[&Value], path_len: usize) -> Value {
104 let one = cols
105 .first()
106 .and_then(|c| c.ones.get(path_len).copied())
107 .unwrap_or_else(|| cols.first().map(|c| c.one).unwrap_or(false));
108 let id_col = cols
109 .iter()
110 .find(|c| c.json_path.len() == path_len + 2 && c.json_path[path_len + 1] == "_id");
111 let mut order: Vec<String> = Vec::new();
114 let mut groups: HashMap<String, Vec<&Value>> = HashMap::new();
115 for row in rows {
116 if cols.iter().all(|c| row_val(row, &c.alias).is_null()) {
118 continue;
119 }
120 let key = match id_col.map(|c| row_val(row, &c.alias)) {
121 Some(v) if !v.is_null() => v.to_string(),
122 _ => format!("\u{0}{}", order.len()),
123 };
124 if !groups.contains_key(&key) {
125 order.push(key.clone());
126 }
127 groups.entry(key).or_default().push(row);
128 }
129 let docs: Vec<Value> = order
130 .into_iter()
131 .map(|k| Value::Object(build_object(cols, &groups[&k], path_len + 1, None)))
132 .collect();
133 if one {
134 docs.into_iter().next().unwrap_or(Value::Null)
135 } else {
136 Value::Array(docs)
137 }
138}
139
140fn row_val<'a>(row: &'a Value, alias: &str) -> &'a Value {
142 match row {
143 Value::Object(map) => map.get(alias).unwrap_or(&Value::Null),
144 _ => &Value::Null,
145 }
146}
147
148fn root_key(shape: &RowShape, row: &Value) -> Option<Value> {
151 let id_alias = shape
152 .columns
153 .iter()
154 .find(|c| !c.is_array && c.json_path.len() == 1 && c.json_path[0] == "_id")?
155 .alias
156 .clone();
157 row.get(&id_alias).cloned()
158}
159
160fn merge_path(
174 obj: &mut Map<String, Value>,
175 path: &[String],
176 val: Value,
177 present: Option<&str>,
178 always: bool,
179 is_bool: bool,
180) {
181 let val = if is_bool { to_json_bool(val) } else { val };
182 if path.len() <= 1 {
183 if let Some(k) = path.first() {
184 if always || !val.is_null() || present_contains(present, k) {
185 obj.insert(k.clone(), val);
186 }
187 }
188 return;
189 }
190 let head = path[0].clone();
191 let entry = obj
192 .entry(head.clone())
193 .or_insert_with(|| Value::Object(Map::new()));
194 if let Value::Object(m) = entry {
195 merge_path(m, &path[1..], val, present, always, false);
196 }
197}
198
199fn parse_json_col(v: Value) -> Value {
202 match v {
203 Value::String(s) => serde_json::from_str::<Value>(&s).unwrap_or(Value::String(s)),
204 other => other,
205 }
206}
207
208fn to_json_bool(v: Value) -> Value {
211 let as_num = match &v {
212 Value::Number(n) => n.as_f64(),
213 Value::String(s) => s.parse::<f64>().ok(),
214 _ => None,
215 };
216 match as_num {
217 Some(1.0) => Value::Bool(true),
218 Some(0.0) => Value::Bool(false),
219 _ => v,
220 }
221}
222
223fn present_contains(present: Option<&str>, key: &str) -> bool {
225 match present {
226 Some(s) => s.contains(&format!(",{},", key)),
227 None => false,
228 }
229}
230
231pub fn restore_rows_json(shape: &Value, rows: &Value) -> Result<Value, String> {
233 let shape = RowShape::from_value(shape)?;
234 let rows = match rows {
235 Value::Array(a) => a.clone(),
236 _ => Vec::new(),
237 };
238 Ok(restore_rows(&shape, &rows))
239}
240
241impl RowShape {
242 pub fn from_value(v: &Value) -> Result<RowShape, String> {
244 let obj = v
245 .get("columns")
246 .and_then(|c| c.as_array())
247 .ok_or("rowShape 缺少 columns")?;
248 let mut columns = Vec::new();
249 for c in obj {
250 let alias = c
251 .get("alias")
252 .and_then(|a| a.as_str())
253 .ok_or("列缺 alias")?
254 .to_string();
255 let path: Vec<String> = c
256 .get("path")
257 .and_then(|p| p.as_array())
258 .map(|arr| {
259 arr.iter()
260 .filter_map(|s| s.as_str().map(String::from))
261 .collect()
262 })
263 .unwrap_or_default();
264 let is_array = c.get("isArray").and_then(|b| b.as_bool()).unwrap_or(false);
265 let one = c.get("one").and_then(|b| b.as_bool()).unwrap_or(false);
266 let ones: Vec<bool> = match c.get("ones").and_then(|o| o.as_array()) {
268 Some(a) => a.iter().map(|b| b.as_bool().unwrap_or(false)).collect(),
269 None if is_array => vec![one],
270 None => Vec::new(),
271 };
272 let sub_shape = c
273 .get("subShape")
274 .and_then(|s| {
275 if s.is_null() {
276 None
277 } else {
278 Some(RowShape::from_value(s))
279 }
280 })
281 .transpose()?;
282 let always = c.get("always").and_then(|b| b.as_bool()).unwrap_or(false);
283 let is_bool = c.get("bool").and_then(|b| b.as_bool()).unwrap_or(false);
284 let is_json = c.get("json").and_then(|b| b.as_bool()).unwrap_or(false);
285 columns.push(RowCol {
286 alias,
287 json_path: path,
288 is_array,
289 one,
290 ones,
291 sub_shape,
292 always,
293 is_bool,
294 is_json,
295 });
296 }
297 let present_alias = v
298 .get("present")
299 .and_then(|p| p.as_str())
300 .filter(|s| !s.is_empty())
301 .map(|s| s.to_string());
302 Ok(RowShape {
303 columns,
304 present_alias,
305 })
306 }
307}