use std::collections::HashSet;
use crate::utils::aliases::StrMap;
use sea_orm::{
ConnectionTrait,
sea_query::{Alias, Expr, ExprTrait, Func, 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],
) -> StrMap {
if ids.is_empty() {
return StrMap::new();
}
let id_text = || {
Expr::col(Alias::new("id")).cast_as(Alias::new(super::text_cast_type_of(
db.get_database_backend(),
)))
};
let stmt = Query::select()
.expr(id_text())
.expr(Expr::col(Alias::new(fk_col)))
.from(Alias::new(fk_table))
.and_where(id_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 fetch_fk_matching_ids<C: ConnectionTrait>(
db: &C,
fk_table: &str,
fk_col: &str,
pattern: &str,
) -> Vec<String> {
let needle = format!("%{}%", pattern.to_lowercase());
let stmt = Query::select()
.expr(
Expr::col(Alias::new("id")).cast_as(Alias::new(super::text_cast_type_of(
db.get_database_backend(),
))),
)
.from(Alias::new(fk_table))
.and_where(Expr::expr(Func::lower(Expr::col(Alias::new(fk_col)))).like(needle))
.to_owned();
db.query_all(&stmt)
.await
.unwrap_or_default()
.iter()
.filter_map(|row| row.try_get_by_index::<String>(0).ok())
.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());
}
}
}
}