use std::collections::HashMap;
use serde_json::Value;
use crate::bson::id_key;
use super::plan::unit_index;
pub const MAX_FEDERATION_ROWS: usize = 100_000;
fn collect_at_path<'a>(docs: &'a mut [Value], path: &[String], out: &mut Vec<&'a mut Value>) {
let Some((head, tail)) = path.split_first() else {
for d in docs.iter_mut() {
out.push(d);
}
return;
};
for d in docs.iter_mut() {
let Some(obj) = d.as_object_mut() else {
continue;
};
let Some(child) = obj.get_mut(head.as_str()) else {
continue;
};
match child {
Value::Array(arr) => collect_at_path(arr.as_mut_slice(), tail, out),
other => collect_at_path(std::slice::from_mut(other), tail, out),
}
}
}
fn as_str_field(v: &Value, key: &str) -> String {
v.get(key)
.and_then(|x| x.as_str())
.unwrap_or("")
.to_string()
}
pub fn merge_federated(plan: &Value, results: &[Value]) -> Result<Value, String> {
let sources = plan
.get("sources")
.and_then(|v| v.as_array())
.ok_or_else(|| "联邦计划缺少 sources".to_string())?;
if results.len() != sources.len() {
return Err(format!(
"联邦结果数与取数单元数不一致: results = {}, sources = {}",
results.len(),
sources.len()
));
}
let cap = plan
.get("maxRowsPerSource")
.and_then(|v| v.as_u64())
.map(|n| n as usize)
.unwrap_or(MAX_FEDERATION_ROWS);
let mut rows_by_unit: Vec<Vec<Value>> = Vec::with_capacity(results.len());
for (i, r) in results.iter().enumerate() {
let rows = r
.as_array()
.ok_or_else(|| format!("第 {} 个取数单元的结果必须是数组", i))?;
if rows.len() > cap {
return Err(format!(
"第 {} 个取数单元返回 {} 行,超过联邦内存 join 上限 {}(拒绝静默全表拉取)",
i,
rows.len(),
cap
));
}
rows_by_unit.push(rows.clone());
}
let mut root_docs = rows_by_unit.first().cloned().unwrap_or_default();
let index = unit_index(sources);
let edges: Vec<Value> = plan
.get("join")
.and_then(|j| j.get("edges"))
.and_then(|e| e.as_array())
.cloned()
.unwrap_or_default();
for edge in &edges {
let rel = as_str_field(edge, "rel");
let local = as_str_field(edge, "local");
let foreign = as_str_field(edge, "foreign");
let cardinality = as_str_field(edge, "cardinality");
let key = as_str_field(edge, "key");
if rel.is_empty() || cardinality.is_empty() {
return Err("非法 join 边:缺少 rel / cardinality".to_string());
}
let idx = *index
.get(&key)
.ok_or_else(|| format!("join 边引用了不存在的取数单元 key: {}", key))?;
let children = &rows_by_unit[idx];
let path: Vec<String> = edge
.get("path")
.and_then(|p| p.as_array())
.map(|a| {
a.iter()
.map(|x| x.as_str().unwrap_or("").to_string())
.collect()
})
.unwrap_or_default();
let mut by_key: HashMap<String, Vec<&Value>> = HashMap::new();
for c in children {
let k = id_key(c.get(&foreign).unwrap_or(&Value::Null));
by_key.entry(k).or_default().push(c);
}
let mut parents: Vec<&mut Value> = Vec::new();
collect_at_path(root_docs.as_mut_slice(), &path, &mut parents);
for p in parents {
let Some(obj) = p.as_object_mut() else {
continue;
};
let lk = id_key(obj.get(&local).unwrap_or(&Value::Null));
let matched = by_key.get(&lk).cloned().unwrap_or_default();
let value = if cardinality == "one" {
matched.first().map(|v| (*v).clone()).unwrap_or(Value::Null)
} else {
Value::Array(matched.into_iter().cloned().collect())
};
obj.insert(rel.clone(), value);
}
}
Ok(Value::Array(root_docs))
}