use super::{
extract_pk_value, ExecError, FetcherPool, FkPkAccess, HasPkValue, LoadRelated, MaybeMyFromRow,
MaybeMyLoadRelated, MaybePgFromRow, MaybeSqliteFromRow, MaybeSqliteLoadRelated,
};
use crate::core::Model;
use crate::sql::Pool;
pub async fn fetch_with_prefetch_pool<P, C>(
parent_qs: crate::query::QuerySet<P>,
child_fk_column: &'static str,
pool: &Pool,
) -> Result<Vec<(P, Vec<C>)>, ExecError>
where
P: Model
+ MaybePgFromRow
+ MaybeMyFromRow
+ MaybeSqliteFromRow
+ LoadRelated
+ MaybeMyLoadRelated
+ MaybeSqliteLoadRelated
+ HasPkValue
+ Send
+ Unpin,
C: Model
+ MaybePgFromRow
+ MaybeMyFromRow
+ MaybeSqliteFromRow
+ LoadRelated
+ MaybeMyLoadRelated
+ MaybeSqliteLoadRelated
+ FkPkAccess
+ Send
+ Unpin,
{
let parents: Vec<P> = parent_qs.fetch(pool).await?;
if parents.is_empty() {
return Ok(Vec::new());
}
let pk_field = P::SCHEMA
.primary_key()
.ok_or(ExecError::MissingPrimaryKey {
table: P::SCHEMA.table,
})?;
let mut parent_pks: Vec<crate::core::SqlValue> = Vec::with_capacity(parents.len());
for parent in &parents {
let pk = extract_pk_value(parent);
if !matches!(pk, crate::core::SqlValue::Null) {
parent_pks.push(pk);
}
}
{
let mut seen = std::collections::HashSet::new();
parent_pks.retain(|v| seen.insert(v.to_display_string()));
}
if parent_pks.is_empty() {
return Ok(parents.into_iter().map(|p| (p, Vec::new())).collect());
}
let children: Vec<C> = crate::query::QuerySet::<C>::new()
.filter_op(
child_fk_column,
crate::core::Op::In,
crate::core::SqlValue::List(parent_pks),
)
.fetch(pool)
.await?;
let mut grouped: std::collections::HashMap<String, Vec<C>> = std::collections::HashMap::new();
for child in children {
let Some(fk_pk) = child.__rustango_fk_pk_value(child_fk_column) else {
continue;
};
grouped
.entry(fk_pk.to_display_string())
.or_default()
.push(child);
}
let mut out = Vec::with_capacity(parents.len());
for parent in parents {
let pk = extract_pk_value(&parent).to_display_string();
let kids = grouped.remove(&pk).unwrap_or_default();
out.push((parent, kids));
}
let _ = pk_field;
Ok(out)
}
pub async fn fetch_with_prefetch_filtered<P, C>(
parent_qs: crate::query::QuerySet<P>,
child_fk_column: &'static str,
child_qs: crate::query::QuerySet<C>,
pool: &Pool,
) -> Result<Vec<(P, Vec<C>)>, ExecError>
where
P: Model
+ MaybePgFromRow
+ MaybeMyFromRow
+ MaybeSqliteFromRow
+ LoadRelated
+ MaybeMyLoadRelated
+ MaybeSqliteLoadRelated
+ HasPkValue
+ Send
+ Unpin,
C: Model
+ MaybePgFromRow
+ MaybeMyFromRow
+ MaybeSqliteFromRow
+ LoadRelated
+ MaybeMyLoadRelated
+ MaybeSqliteLoadRelated
+ FkPkAccess
+ Send
+ Unpin,
{
let parents: Vec<P> = parent_qs.fetch(pool).await?;
if parents.is_empty() {
return Ok(Vec::new());
}
let pk_field = P::SCHEMA
.primary_key()
.ok_or(ExecError::MissingPrimaryKey {
table: P::SCHEMA.table,
})?;
let mut parent_pks: Vec<crate::core::SqlValue> = Vec::with_capacity(parents.len());
for parent in &parents {
let pk = extract_pk_value(parent);
if !matches!(pk, crate::core::SqlValue::Null) {
parent_pks.push(pk);
}
}
{
let mut seen = std::collections::HashSet::new();
parent_pks.retain(|v| seen.insert(v.to_display_string()));
}
if parent_pks.is_empty() {
return Ok(parents.into_iter().map(|p| (p, Vec::new())).collect());
}
let children: Vec<C> = child_qs
.filter_op(
child_fk_column,
crate::core::Op::In,
crate::core::SqlValue::List(parent_pks),
)
.fetch(pool)
.await?;
let mut grouped: std::collections::HashMap<String, Vec<C>> = std::collections::HashMap::new();
for child in children {
let Some(fk_pk) = child.__rustango_fk_pk_value(child_fk_column) else {
continue;
};
grouped
.entry(fk_pk.to_display_string())
.or_default()
.push(child);
}
let mut out = Vec::with_capacity(parents.len());
for parent in parents {
let pk = extract_pk_value(&parent).to_display_string();
let kids = grouped.remove(&pk).unwrap_or_default();
out.push((parent, kids));
}
let _ = pk_field;
Ok(out)
}