1use std::collections::BTreeMap;
2use std::slice;
3
4use teaql_core::{
5 Aggregate, CompactRow, Expr, ObjectGroupBy, RelationAggregate, RelationLoad,
6 SelectQuery, Value,
7};
8
9use crate::{DataServiceError, RuntimeError};
10
11use super::{helpers::*, EntityDataService, RelationLoadPlan};
12
13#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
14enum FlatIdentityKey {
15 U64(u64),
16 Other(String),
17}
18
19impl FlatIdentityKey {
20 fn from_value(value: &Value) -> Self {
21 match value {
22 Value::U64(value) => Self::U64(*value),
23 Value::I64(value) if *value >= 0 => Self::U64(*value as u64),
24 _ => Self::Other(graph_identity_key(value)),
25 }
26 }
27}
28
29impl<'a, E> EntityDataService<'a, E>
30where
31 E: teaql_data_service::QueryExecutor
32 + teaql_data_service::MutationExecutor
33 + Send
34 + Sync
35 + 'static,
36{
37 pub fn relation_loads(&self) -> Vec<String> {
38 self.behavior()
39 .map(|behavior| behavior.relation_loads(self.data_service.metadata.context))
40 .unwrap_or_default()
41 }
42
43 pub fn relation_plans(&self) -> Result<Vec<RelationLoadPlan>, RuntimeError> {
44 self.build_relation_plans(&self.entity, &self.relation_loads())
45 }
46
47 pub fn relation_query(
48 &self,
49 relation_name: &str,
50 parent_rows: &[CompactRow],
51 ) -> Result<SelectQuery, RuntimeError> {
52 let plan = self
53 .relation_plans()?
54 .into_iter()
55 .find(|plan| plan.relation_name == relation_name)
56 .ok_or_else(|| RuntimeError::MissingRelation {
57 entity: self.entity.clone(),
58 relation: relation_name.to_owned(),
59 })?;
60 Ok(self.query_for_plan(&plan, parent_rows))
61 }
62
63 pub(crate) async fn enhance_relations_internal(
64 &self,
65 parent_rows: &mut [CompactRow],
66 ) -> Result<(), DataServiceError<E::Error>> {
67 let plans = self.relation_plans().map_err(DataServiceError::Runtime)?;
68 for plan in plans {
69 self.enhance_plan(parent_rows, &plan).await?;
70 }
71 Ok(())
72 }
73
74 pub(crate) async fn enhance_query_relations_internal(
75 &self,
76 parent_rows: &mut [CompactRow],
77 query: &SelectQuery,
78 ) -> Result<(), DataServiceError<E::Error>> {
79 let plans = self
80 .build_relation_plans_from_loads(&query.entity, &query.relations)
81 .map_err(DataServiceError::Runtime)?;
82 for plan in plans {
83 self.enhance_plan(parent_rows, &plan).await?;
84 }
85 Ok(())
86 }
87
88 pub(crate) async fn hydrate_flat_plans_internal(
89 &self,
90 parent_rows: &mut [CompactRow],
91 plans: &[RelationLoadPlan],
92 root: &crate::EntityRoot,
93 graph: &mut crate::EntityGraphBuilder,
94 ) -> Result<(), DataServiceError<E::Error>> {
95 for plan in plans {
96 self.hydrate_flat_plan(parent_rows, plan, root, graph)
97 .await?;
98 }
99 Ok(())
100 }
101
102 pub(crate) async fn hydrate_compact_flat_plans_internal(
103 &self,
104 parent_rows: &[CompactRow],
105 plans: &[RelationLoadPlan],
106 root: &crate::EntityRoot,
107 graph: &mut crate::EntityGraphBuilder,
108 ) -> Result<(), DataServiceError<E::Error>> {
109 for plan in plans {
110 self.hydrate_compact_flat_plan(parent_rows, plan, root, graph)
111 .await?;
112 }
113 Ok(())
114 }
115
116 pub(crate) fn flat_relation_plans(
117 &self,
118 query: &SelectQuery,
119 ) -> Result<Option<(Vec<RelationLoadPlan>, Vec<RelationLoadPlan>)>, RuntimeError> {
120 let context = self.data_service.metadata.context;
121 let query_plans = self.build_relation_plans_from_loads(&query.entity, &query.relations)?;
122 let behavior_plans = self.relation_plans()?;
123
124 fn supported(context: &crate::UserContext, plan: &RelationLoadPlan) -> bool {
125 context.has_entity_graph_decoder(&plan.target_entity)
126 && plan.children.iter().all(|child| supported(context, child))
127 }
128
129 Ok(query_plans
130 .iter()
131 .chain(behavior_plans.iter())
132 .all(|plan| supported(context, plan))
133 .then_some((query_plans, behavior_plans)))
134 }
135
136 pub(crate) fn enhance_relation_aggregates_internal<'b>(
137 &'b self,
138 parent_rows: &'b mut [CompactRow],
139 relation_aggregates: &'b [RelationAggregate],
140 parent_cache_options: Option<teaql_core::AggregationCacheOptions>,
141 parent_trace_chain: &'b [teaql_core::TraceNode],
142 ) -> std::pin::Pin<
143 Box<dyn std::future::Future<Output = Result<(), DataServiceError<E::Error>>> + Send + 'b>,
144 > {
145 Box::pin(async move {
146 for aggregate in relation_aggregates {
147 self.enhance_relation_aggregate(
148 parent_rows,
149 aggregate,
150 parent_cache_options,
151 parent_trace_chain,
152 )
153 .await?;
154 }
155 Ok(())
156 })
157 }
158
159 pub(crate) fn enhance_object_group_bys_internal<'b>(
160 &'b self,
161 rows: &'b mut [CompactRow],
162 object_group_bys: &'b [ObjectGroupBy],
163 parent_trace_chain: &'b [teaql_core::TraceNode],
164 ) -> std::pin::Pin<
165 Box<dyn std::future::Future<Output = Result<(), DataServiceError<E::Error>>> + Send + 'b>,
166 > {
167 Box::pin(async move {
168 for group_by in object_group_bys {
169 let ids = rows
170 .iter()
171 .filter_map(|row| row.get(&group_by.storage_field).cloned())
172 .collect::<Vec<_>>();
173 if ids.is_empty() {
174 continue;
175 }
176 let mut query = group_by.query.clone();
177 ensure_projection(&mut query, "id");
178 query = query.and_filter(Expr::in_list("id", ids));
179 let object_rows = self
180 .scoped_data_service_internal(query.entity.clone())
181 .with_trace_context(parent_trace_chain.to_vec())
182 .fetch_compact_all_internal(query)
183 .await?
184 .into_iter()
185 .filter_map(|row| {
186 row.get("id")
187 .cloned()
188 .map(|id| (graph_identity_key(&id), row))
189 })
190 .collect::<BTreeMap<_, _>>();
191 for row in rows.iter_mut() {
192 if let Some(key) = row.get(&group_by.storage_field).map(graph_identity_key) {
193 let value = object_rows
194 .get(&key)
195 .cloned()
196 .map(|row| Value::object(row.into_map()))
197 .unwrap_or(Value::Null);
198 row.insert(group_by.property_name.clone(), value);
199 }
200 }
201 }
202 Ok(())
203 })
204 }
205
206 pub(crate) fn enhance_child_queries_internal<'b>(
207 &'b self,
208 rows: &'b mut [CompactRow],
209 child_queries: &'b [SelectQuery],
210 parent_trace_chain: &'b [teaql_core::TraceNode],
211 ) -> std::pin::Pin<
212 Box<dyn std::future::Future<Output = Result<(), DataServiceError<E::Error>>> + Send + 'b>,
213 > {
214 Box::pin(async move {
215 for child_query in child_queries {
216 let ids = rows
217 .iter()
218 .filter_map(|row| row.get("id").cloned())
219 .collect::<Vec<_>>();
220 if ids.is_empty() {
221 continue;
222 }
223 let mut query = child_query.clone();
224 ensure_projection(&mut query, "id");
225 query = query.and_filter(Expr::in_list("id", ids));
226 let child_rows = self
227 .scoped_data_service_internal(query.entity.clone())
228 .with_trace_context(parent_trace_chain.to_vec())
229 .fetch_compact_all_internal(query)
230 .await?
231 .into_iter()
232 .filter_map(|row| {
233 row.get("id")
234 .cloned()
235 .map(|id| (graph_identity_key(&id), row))
236 })
237 .collect::<BTreeMap<_, _>>();
238 for row in rows.iter_mut() {
239 if let Some(key) = row.get("id").map(graph_identity_key) {
240 if let Some(child) = child_rows.get(&key) {
241 row.extend(child.clone());
242 }
243 }
244 }
245 }
246 Ok(())
247 })
248 }
249
250 async fn enhance_relation_aggregate(
251 &self,
252 parent_rows: &mut [CompactRow],
253 aggregate: &RelationAggregate,
254 parent_cache_options: Option<teaql_core::AggregationCacheOptions>,
255 parent_trace_chain: &[teaql_core::TraceNode],
256 ) -> Result<(), DataServiceError<E::Error>> {
257 let plan = self
258 .build_relation_plans_from_loads(
259 &self.entity,
260 &[RelationLoad::with_query(
261 aggregate.relation_name.clone(),
262 aggregate.query.clone(),
263 )],
264 )
265 .map_err(DataServiceError::Runtime)?
266 .into_iter()
267 .next()
268 .ok_or_else(|| {
269 DataServiceError::Runtime(RuntimeError::MissingRelation {
270 entity: self.entity.clone(),
271 relation: aggregate.relation_name.clone(),
272 })
273 })?;
274
275 let ids = parent_rows
276 .iter()
277 .filter_map(|row| row.get(&plan.local_key).cloned())
278 .collect::<Vec<_>>();
279 if ids.is_empty() {
280 attach_empty_relation_aggregate(parent_rows, &aggregate.alias, aggregate.single_result);
281 return Ok(());
282 }
283
284 let child_repo = self.scoped_data_service_internal(plan.target_entity.clone());
285 let mut query = aggregate.query.clone();
286 query.entity = plan.target_entity.clone();
287 if query.aggregation_cache.is_none() {
288 if let Some(options) = parent_cache_options.filter(|options| options.propagate) {
289 query.aggregation_cache = Some(teaql_core::AggregationCacheOptions::enabled(
290 options.propagate_cache_expired_millis,
291 ));
292 }
293 }
294 query.projection.clear();
295 query.expr_projection.clear();
296 query.order_by.clear();
297 query.slice = None;
298 query.relations.clear();
299 if query.aggregates.is_empty() {
300 let alias = aggregate_alias(aggregate.single_result, &aggregate.alias);
301 query = query.aggregate(Aggregate::count(alias));
302 }
303 if !query
304 .group_by
305 .iter()
306 .any(|field| field == &plan.foreign_key)
307 {
308 query = query.group_by(plan.foreign_key.clone());
309 }
310 query = query.and_filter(Expr::in_list(plan.foreign_key.clone(), ids));
311
312 let mut chain = parent_trace_chain.to_vec();
313 chain.push(teaql_core::TraceNode {
314 entity_type: query.entity.clone(),
315 entity_id: None,
316 comment: aggregate.alias.clone(),
317 });
318
319 let mut aggregate_rows = child_repo
320 .with_trace_context(chain)
321 .fetch_compact_all_internal(query)
322 .await?;
323 let foreign_key_column = self
324 .data_service
325 .metadata
326 .context
327 .entity(&plan.target_entity)
328 .and_then(|descriptor| {
329 descriptor
330 .properties
331 .iter()
332 .find(|property| property.name == plan.foreign_key)
333 .map(|property| property.column_name.clone())
334 });
335 if let Some(foreign_key_column) =
336 foreign_key_column.filter(|column| column != &plan.foreign_key)
337 {
338 for row in &mut aggregate_rows {
339 if !row.contains_key(&plan.foreign_key) {
340 if let Some(value) = row.remove(&foreign_key_column) {
341 row.insert(plan.foreign_key.clone(), value);
342 }
343 }
344 }
345 }
346 attach_relation_aggregate_rows(parent_rows, &plan, aggregate, aggregate_rows);
347 Ok(())
348 }
349
350 fn build_relation_plans(
351 &self,
352 entity: &str,
353 loads: &[String],
354 ) -> Result<Vec<RelationLoadPlan>, RuntimeError> {
355 let descriptor = self.data_service.metadata.context.require_entity(entity)?;
356 let mut grouped: BTreeMap<String, Vec<String>> = BTreeMap::new();
357 for load in loads {
358 match load.split_once('.') {
359 Some((head, tail)) => {
360 grouped
361 .entry(head.to_owned())
362 .or_default()
363 .push(tail.to_owned());
364 }
365 None => {
366 grouped.entry(load.clone()).or_default();
367 }
368 }
369 }
370
371 grouped
372 .into_iter()
373 .map(|(name, child_loads)| {
374 let relation = descriptor.relation_by_name(&name).ok_or_else(|| {
375 RuntimeError::MissingRelation {
376 entity: entity.to_owned(),
377 relation: name.clone(),
378 }
379 })?;
380 let child_repo = self.scoped_data_service_internal(relation.target_entity.clone());
381 let children =
382 child_repo.build_relation_plans(&relation.target_entity, &child_loads)?;
383 Ok(RelationLoadPlan {
384 parent_entity: entity.to_owned(),
385 relation_name: relation.name.clone(),
386 path: relation.name.clone(),
387 target_entity: relation.target_entity.clone(),
388 local_key: relation.local_key.clone(),
389 foreign_key: relation.foreign_key.clone(),
390 many: relation.many,
391 query: None,
392 children,
393 })
394 })
395 .collect()
396 }
397
398 fn build_relation_plans_from_loads(
399 &self,
400 entity: &str,
401 loads: &[RelationLoad],
402 ) -> Result<Vec<RelationLoadPlan>, RuntimeError> {
403 let descriptor = self.data_service.metadata.context.require_entity(entity)?;
404 loads
405 .iter()
406 .map(|load| {
407 let relation = descriptor.relation_by_name(&load.name).ok_or_else(|| {
408 RuntimeError::MissingRelation {
409 entity: entity.to_owned(),
410 relation: load.name.clone(),
411 }
412 })?;
413 let relation_query = load.query.as_deref().cloned();
414 let child_loads = relation_query
415 .as_ref()
416 .map(|query| query.relations.as_slice())
417 .unwrap_or_default();
418 let child_repo = self.scoped_data_service_internal(relation.target_entity.clone());
419 let children = child_repo
420 .build_relation_plans_from_loads(&relation.target_entity, child_loads)?;
421 Ok(RelationLoadPlan {
422 parent_entity: entity.to_owned(),
423 relation_name: relation.name.clone(),
424 path: relation.name.clone(),
425 target_entity: relation.target_entity.clone(),
426 local_key: relation.local_key.clone(),
427 foreign_key: relation.foreign_key.clone(),
428 many: relation.many,
429 query: relation_query,
430 children,
431 })
432 })
433 .collect()
434 }
435 fn enhance_plan<'b>(
436 &'b self,
437 parent_rows: &'b mut [CompactRow],
438 plan: &'b RelationLoadPlan,
439 ) -> std::pin::Pin<
440 Box<dyn std::future::Future<Output = Result<(), DataServiceError<E::Error>>> + Send + 'b>,
441 > {
442 Box::pin(async move {
443 let scope = self.data_service.metadata.context.start_runtime_operation(
444 crate::RuntimeOperation::new(
445 "relation_load",
446 format!("{}.{}", plan.parent_entity, plan.path),
447 )
448 .attribute("teaql.entity.type", plan.parent_entity.clone())
449 .attribute("teaql.relation.name", plan.path.clone()),
450 );
451 let result = scope
452 .run(async {
453 let child_repo = self.scoped_data_service_internal(plan.target_entity.clone());
454 let query = self.query_for_plan(plan, parent_rows);
455 let mut child_rows = child_repo.fetch_compact_all_internal(query).await?;
456 for child in &mut child_rows {
457 child.remove(teaql_core::PARTITION_RANK_PROPERTY);
458 }
459 self.attach_relation_rows(parent_rows, plan, child_rows);
460
461 if !plan.children.is_empty() {
462 for parent in parent_rows.iter_mut() {
463 match parent.get_mut(&plan.relation_name) {
464 Some(Value::Object(child)) => {
465 child_repo
466 .enhance_child_record(child, &plan.children)
467 .await?;
468 }
469 Some(Value::List(values)) => {
470 for value in values.iter_mut() {
471 if let Value::Object(child) = value {
472 child_repo
473 .enhance_child_record(child, &plan.children)
474 .await?;
475 }
476 }
477 }
478 _ => {}
479 }
480 }
481 }
482 Ok(())
483 })
484 .await;
485 match &result {
486 Ok(_) => scope.success(BTreeMap::from([(
487 "teaql.result.cardinality".to_owned(),
488 crate::RuntimeAttributeValue::Integer(parent_rows.len() as i64),
489 )])),
490 Err(_) => scope.failure("relation_load_error"),
491 }
492 result
493 })
494 }
495
496 fn hydrate_flat_plan<'b>(
497 &'b self,
498 parent_rows: &'b mut [CompactRow],
499 plan: &'b RelationLoadPlan,
500 root: &'b crate::EntityRoot,
501 graph: &'b mut crate::EntityGraphBuilder,
502 ) -> std::pin::Pin<
503 Box<dyn std::future::Future<Output = Result<(), DataServiceError<E::Error>>> + Send + 'b>,
504 > {
505 Box::pin(async move {
506 let child_repo = self.scoped_data_service_internal(plan.target_entity.clone());
507 let query = self.query_for_plan(plan, parent_rows);
508 let mut child_rows = child_repo.fetch_compact_all_internal(query).await?;
509 for child in &mut child_rows {
510 child.remove(teaql_core::PARTITION_RANK_PROPERTY);
511 }
512
513 for child_plan in &plan.children {
517 child_repo
518 .hydrate_flat_plan(&mut child_rows, child_plan, root, graph)
519 .await?;
520 }
521
522 let inverse_relation = self
523 .data_service
524 .metadata
525 .context
526 .entity(&plan.target_entity)
527 .and_then(|descriptor| {
528 descriptor.relations.iter().find(|relation| {
529 relation.target_entity == plan.parent_entity
530 && relation.local_key == plan.foreign_key
531 && relation.foreign_key == plan.local_key
532 })
533 })
534 .map(|relation| (relation.name.clone(), relation.many));
535
536 let mut buckets: BTreeMap<FlatIdentityKey, Vec<CompactRow>> = BTreeMap::new();
537 for child in child_rows {
538 if let Some(key) = child.get(&plan.foreign_key) {
539 buckets
540 .entry(FlatIdentityKey::from_value(key))
541 .or_default()
542 .push(child);
543 }
544 }
545
546 let context = self.data_service.metadata.context;
547 for parent in parent_rows {
548 let Some(local_value) = parent.get(&plan.local_key) else {
549 continue;
550 };
551 let related = buckets
552 .remove(&FlatIdentityKey::from_value(local_value))
553 .unwrap_or_default();
554
555 if let Some((inverse_name, inverse_many)) = &inverse_relation {
556 let parent_record = parent.clone();
557 for child in &related {
558 let Some(child_id) = child.get("id").and_then(Value::try_u64) else {
559 continue;
560 };
561 if *inverse_many {
562 context
563 .decode_compact_entity_list_into_graph(
564 &plan.parent_entity,
565 vec![parent_record.clone()],
566 root,
567 graph,
568 &plan.target_entity,
569 child_id,
570 inverse_name,
571 )
572 .map_err(DataServiceError::Entity)?;
573 } else {
574 context
575 .decode_compact_entity_option_into_graph(
576 &plan.parent_entity,
577 vec![parent_record.clone()],
578 root,
579 graph,
580 &plan.target_entity,
581 child_id,
582 inverse_name,
583 )
584 .map_err(DataServiceError::Entity)?;
585 }
586 }
587 }
588
589 if plan.many || plan.local_key == "id" {
590 let owner_id = parent.get("id").and_then(Value::try_u64).ok_or_else(|| {
591 DataServiceError::Entity(teaql_core::EntityError::new(
592 &plan.parent_entity,
593 "loaded reverse relation owner is missing its u64 id",
594 ))
595 })?;
596 if plan.many {
597 context
598 .decode_compact_entity_list_into_graph(
599 &plan.target_entity,
600 related,
601 root,
602 graph,
603 &plan.parent_entity,
604 owner_id,
605 &plan.relation_name,
606 )
607 .map_err(DataServiceError::Entity)?;
608 } else {
609 context
610 .decode_compact_entity_option_into_graph(
611 &plan.target_entity,
612 related,
613 root,
614 graph,
615 &plan.parent_entity,
616 owner_id,
617 &plan.relation_name,
618 )
619 .map_err(DataServiceError::Entity)?;
620 }
621 } else if related.is_empty() {
622 parent.insert(plan.relation_name.clone(), Value::Null);
625 } else {
626 for child in related {
627 context
628 .decode_compact_entity_into_graph(&plan.target_entity, child, root, graph)
629 .map_err(DataServiceError::Entity)?;
630 }
631 }
632 }
633 Ok(())
634 })
635 }
636
637 fn hydrate_compact_flat_plan<'b>(
638 &'b self,
639 parent_rows: &'b [CompactRow],
640 plan: &'b RelationLoadPlan,
641 root: &'b crate::EntityRoot,
642 graph: &'b mut crate::EntityGraphBuilder,
643 ) -> std::pin::Pin<
644 Box<dyn std::future::Future<Output = Result<(), DataServiceError<E::Error>>> + Send + 'b>,
645 > {
646 Box::pin(async move {
647 let child_repo = self.scoped_data_service_internal(plan.target_entity.clone());
648 let query = self.query_for_compact_plan(plan, parent_rows);
649 let child_rows = child_repo.fetch_compact_all_internal(query).await?;
650
651 for child_plan in &plan.children {
652 child_repo
653 .hydrate_compact_flat_plan(&child_rows, child_plan, root, graph)
654 .await?;
655 }
656
657 let mut buckets: BTreeMap<FlatIdentityKey, Vec<CompactRow>> = BTreeMap::new();
658 for child in child_rows {
659 if let Some(key) = child.get(&plan.foreign_key) {
660 buckets
661 .entry(FlatIdentityKey::from_value(key))
662 .or_default()
663 .push(child);
664 }
665 }
666
667 let context = self.data_service.metadata.context;
668 for parent in parent_rows {
669 let Some(local_value) = parent.get(&plan.local_key) else {
670 continue;
671 };
672 let related = buckets
673 .remove(&FlatIdentityKey::from_value(local_value))
674 .unwrap_or_default();
675
676 if plan.many || plan.local_key == "id" {
677 let owner_id = parent.get("id").and_then(Value::try_u64).ok_or_else(|| {
678 DataServiceError::Entity(teaql_core::EntityError::new(
679 &plan.parent_entity,
680 "loaded reverse relation owner is missing its u64 id",
681 ))
682 })?;
683 if plan.many {
684 context
685 .decode_compact_entity_list_into_graph(
686 &plan.target_entity,
687 related,
688 root,
689 graph,
690 &plan.parent_entity,
691 owner_id,
692 &plan.relation_name,
693 )
694 .map_err(DataServiceError::Entity)?;
695 } else {
696 context
697 .decode_compact_entity_option_into_graph(
698 &plan.target_entity,
699 related,
700 root,
701 graph,
702 &plan.parent_entity,
703 owner_id,
704 &plan.relation_name,
705 )
706 .map_err(DataServiceError::Entity)?;
707 }
708 } else {
709 for child in related {
710 context
711 .decode_compact_entity_into_graph(
712 &plan.target_entity,
713 child,
714 root,
715 graph,
716 )
717 .map_err(DataServiceError::Entity)?;
718 }
719 }
720 }
721 Ok(())
722 })
723 }
724
725 fn enhance_child_record<'b>(
726 &'b self,
727 child: &'b mut std::collections::BTreeMap<String, Value>,
728 plans: &'b [RelationLoadPlan],
729 ) -> std::pin::Pin<
730 Box<dyn std::future::Future<Output = Result<(), DataServiceError<E::Error>>> + Send + 'b>,
731 > {
732 Box::pin(async move {
733 for plan in plans {
734 let mut row = CompactRow::from_map(std::mem::take(child));
735 self.enhance_plan(slice::from_mut(&mut row), plan).await?;
736 *child = row.into_map();
737 }
738 Ok(())
739 })
740 }
741
742 fn query_for_plan(&self, plan: &RelationLoadPlan, parent_rows: &[CompactRow]) -> SelectQuery {
743 let ids = parent_rows
747 .iter()
748 .filter_map(|row| row.get(&plan.local_key).cloned())
749 .fold(BTreeMap::new(), |mut unique, value| {
750 unique
751 .entry(FlatIdentityKey::from_value(&value))
752 .or_insert(value);
753 unique
754 })
755 .into_values()
756 .collect::<Vec<_>>();
757
758 let mut query = plan
759 .query
760 .clone()
761 .unwrap_or_else(|| SelectQuery::new(plan.target_entity.clone()));
762 query.entity = plan.target_entity.clone();
763 ensure_projection(&mut query, &plan.foreign_key);
764 for child in &plan.children {
765 ensure_projection(&mut query, &child.local_key);
766 }
767 if !ids.is_empty() {
768 query = query.and_filter(Expr::in_list(plan.foreign_key.clone(), ids));
769 }
770 if query.slice.is_some() {
771 query.partition_by = Some(plan.foreign_key.clone());
772 }
773 query
774 }
775
776 fn query_for_compact_plan(
777 &self,
778 plan: &RelationLoadPlan,
779 parent_rows: &[CompactRow],
780 ) -> SelectQuery {
781 let ids = parent_rows
782 .iter()
783 .filter_map(|row| row.get(&plan.local_key).cloned())
784 .fold(BTreeMap::new(), |mut unique, value| {
785 unique
786 .entry(FlatIdentityKey::from_value(&value))
787 .or_insert(value);
788 unique
789 })
790 .into_values()
791 .collect::<Vec<_>>();
792 let mut query = plan
793 .query
794 .clone()
795 .unwrap_or_else(|| SelectQuery::new(plan.target_entity.clone()));
796 query.entity = plan.target_entity.clone();
797 ensure_projection(&mut query, &plan.foreign_key);
798 for child in &plan.children {
799 ensure_projection(&mut query, &child.local_key);
800 }
801 if !ids.is_empty() {
802 query = query.and_filter(Expr::in_list(plan.foreign_key.clone(), ids));
803 }
804 if query.slice.is_some() {
805 query.partition_by = Some(plan.foreign_key.clone());
806 }
807 query
808 }
809
810 fn attach_relation_rows(
811 &self,
812 parent_rows: &mut [CompactRow],
813 plan: &RelationLoadPlan,
814 child_rows: Vec<CompactRow>,
815 ) {
816 let inverse_relation = self
817 .data_service
818 .metadata
819 .context
820 .entity(&plan.target_entity)
821 .and_then(|descriptor| {
822 descriptor.relations.iter().find(|relation| {
823 relation.target_entity == plan.parent_entity
824 && relation.local_key == plan.foreign_key
825 && relation.foreign_key == plan.local_key
826 })
827 })
828 .map(|relation| (relation.name.clone(), relation.many));
829
830 let mut buckets: BTreeMap<String, Vec<CompactRow>> = BTreeMap::new();
831 for child in child_rows.clone() {
832 if let Some(key) = child.get(&plan.foreign_key) {
833 buckets
834 .entry(graph_identity_key(key))
835 .or_default()
836 .push(child);
837 }
838 }
839
840 for parent in parent_rows.iter_mut() {
841 let Some(local_value) = parent.get(&plan.local_key) else {
842 continue;
843 };
844 let bucket_key = graph_identity_key(local_value);
845 let related = buckets.get(&bucket_key).cloned().unwrap_or_default();
846 let related = match &inverse_relation {
847 Some((inverse_relation, inverse_many)) => {
848 let mut parent_object = parent.clone();
849 parent_object.remove(&plan.relation_name);
850 related
851 .into_iter()
852 .map(|mut child| {
853 match *inverse_many {
854 true => {
855 if !child.contains_key(inverse_relation) {
856 child.insert(
857 inverse_relation.clone(),
858 Value::List(Vec::new()),
859 );
860 }
861 let entry = child.get_mut(inverse_relation).expect(
862 "inverse relation was inserted immediately above",
863 );
864 if let Value::List(list) = entry {
865 list.push(Value::object(
866 parent_object.clone().into_map(),
867 ));
868 }
869 }
870 false => {
871 child.insert(
872 inverse_relation.clone(),
873 Value::object(parent_object.clone().into_map()),
874 );
875 }
876 }
877 child
878 })
879 .collect::<Vec<_>>()
880 }
881 None => related,
882 };
883 match plan.many {
884 true => {
885 parent.insert(
886 plan.relation_name.clone(),
887 Value::List(
888 related
889 .into_iter()
890 .map(|row| Value::object(row.into_map()))
891 .collect(),
892 ),
893 );
894 }
895 false => {
896 let value = related
897 .into_iter()
898 .next()
899 .map(|row| Value::object(row.into_map()))
900 .unwrap_or(Value::Null);
901 parent.insert(plan.relation_name.clone(), value);
902 }
903 }
904 }
905 }
906}