use std::collections::{HashMap, HashSet};
use sea_orm::{
ConnectionTrait,
sea_query::{Alias, Expr, ExprTrait, Query},
};
use serde_json::Value;
#[must_use]
pub fn fk_key(value: &Value) -> Option<String> {
value
.as_i64()
.map(|n| n.to_string())
.or_else(|| value.as_str().map(str::to_string))
}
pub async fn fetch_fk_label_map<C: ConnectionTrait>(
db: &C,
fk_table: &str,
fk_col: &str,
ids: &[String],
) -> HashMap<String, String> {
if ids.is_empty() {
return HashMap::new();
}
let stmt = Query::select()
.expr(Expr::cust("CAST(id AS TEXT)"))
.expr(Expr::col(Alias::new(fk_col)))
.from(Alias::new(fk_table))
.and_where(Expr::cust("CAST(id AS TEXT)").is_in(ids.to_vec()))
.to_owned();
db.query_all(&stmt)
.await
.unwrap_or_default()
.iter()
.filter_map(|row| {
let id = row.try_get_by_index::<String>(0).ok()?;
let label = row.try_get_by_index::<String>(1).ok()?;
Some((id, label))
})
.collect()
}
pub async fn resolve_fk_labels<C: ConnectionTrait>(
db: &C,
rows: &mut [Value],
fk_display: &[(String, String, String)],
) {
if fk_display.is_empty() {
return;
}
let specs: Vec<(&str, &str, &str)> = fk_display
.iter()
.map(|(col, table, label_col)| (col.as_str(), table.as_str(), label_col.as_str()))
.collect();
resolve_fk_labels_in_rows(db, rows, &specs).await;
}
pub async fn resolve_fk_labels_in_rows<C: ConnectionTrait>(
db: &C,
rows: &mut [Value],
fk_cols: &[(&str, &str, &str)],
) {
for (col, fk_table, fk_col) in fk_cols {
let ids: Vec<String> = rows
.iter()
.filter_map(|r| r.get(*col).and_then(fk_key))
.collect::<HashSet<String>>()
.into_iter()
.collect();
let label_map = fetch_fk_label_map(db, fk_table, fk_col, &ids).await;
if label_map.is_empty() {
continue;
}
for row in rows.iter_mut() {
if let Some(key) = row.get(*col).and_then(fk_key)
&& let Some(label) = label_map.get(&key)
{
row[*col] = Value::String(label.clone());
}
}
}
}