prax_query/relations/
executor.rs1use std::collections::HashMap;
14
15use crate::error::{QueryError, QueryResult};
16use crate::filter::{Filter, FilterValue};
17use crate::relations::RelationMeta;
18use crate::row::FromRow;
19use crate::traits::{Model, ModelWithPk, QueryEngine};
20
21pub async fn load_has_many<E, P, C, R>(
27 engine: &E,
28 parents: &[P],
29) -> QueryResult<HashMap<String, Vec<C>>>
30where
31 E: QueryEngine,
32 P: Model + ModelWithPk,
33 C: Model + ModelWithPk + FromRow + Send + 'static,
34 R: RelationMeta<Owner = P, Target = C>,
35{
36 let pk_values: Vec<FilterValue> = parents.iter().map(|p| p.pk_value()).collect();
37 if pk_values.is_empty() {
38 return Ok(HashMap::new());
39 }
40
41 let filter = Filter::In(R::FOREIGN_KEY.into(), pk_values);
42 let dialect = engine.dialect();
43 let (where_sql, params) = filter.to_sql(0, dialect);
44 let sql = format!("SELECT * FROM {} WHERE {}", C::TABLE_NAME, where_sql);
45
46 let children: Vec<C> = engine.query_many::<C>(&sql, params).await?;
47
48 let mut out: HashMap<String, Vec<C>> = HashMap::with_capacity(parents.len());
53 for child in children {
54 let fk = child.get_column_value(R::FOREIGN_KEY).ok_or_else(|| {
55 QueryError::internal(format!(
56 "relation {}: child model missing FK column {}",
57 R::NAME,
58 R::FOREIGN_KEY
59 ))
60 })?;
61 let key = filter_value_key(&fk);
62 out.entry(key).or_default().push(child);
63 }
64 Ok(out)
65}
66
67#[doc(hidden)]
75pub fn filter_value_key_public(v: &FilterValue) -> String {
76 filter_value_key(v)
77}
78
79pub(crate) fn filter_value_key(v: &FilterValue) -> String {
88 match v {
89 FilterValue::Int(i) => i.to_string(),
90 FilterValue::String(s) => s.clone(),
91 FilterValue::Bool(b) => b.to_string(),
92 FilterValue::Float(f) => f.to_string(),
93 FilterValue::Null => "<null>".into(),
94 FilterValue::Json(v) => v.to_string(),
95 FilterValue::List(_) => {
96 panic!("relation executor does not support composite keys yet (FilterValue::List)")
97 }
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn filter_value_key_int() {
107 assert_eq!(filter_value_key(&FilterValue::Int(42)), "42");
108 }
109
110 #[test]
111 fn filter_value_key_string() {
112 assert_eq!(filter_value_key(&FilterValue::String("abc".into())), "abc");
113 }
114
115 #[test]
116 fn filter_value_key_bool() {
117 assert_eq!(filter_value_key(&FilterValue::Bool(true)), "true");
118 }
119
120 #[test]
121 fn filter_value_key_null() {
122 assert_eq!(filter_value_key(&FilterValue::Null), "<null>");
123 }
124
125 #[test]
126 #[should_panic(expected = "composite keys")]
127 fn filter_value_key_list_panics() {
128 let _ = filter_value_key(&FilterValue::List(vec![FilterValue::Int(1)]));
129 }
130}