1use std::collections::BTreeMap;
2
3use teaql_core::{
4 DeleteCommand, Entity, EntityDescriptor, Expr, InsertCommand, PropertyDescriptor, Record,
5 SelectQuery, UpdateCommand, Value,
6};
7use teaql_sql::SqlDialect;
8
9use crate::{
10 GraphMutationKind, GraphMutationPlan, GraphNode, GraphOperation, RepositoryError, RuntimeError,
11 sorted_update_fields,
12};
13
14use super::{GraphTransactionBoundary, QueryExecutor, ResolvedRepository, helpers::*};
15
16impl<'a, D, E> ResolvedRepository<'a, D, E>
17where
18 D: SqlDialect,
19 E: QueryExecutor,
20{
21 pub fn save_graph(&self, node: GraphNode) -> Result<GraphNode, RepositoryError<E::Error>> {
22 if node.entity != self.entity {
23 return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
24 "resolved repository {} cannot save graph root {}",
25 self.entity, node.entity
26 ))));
27 }
28 let boundary = self
29 .repository
30 .executor
31 .begin_transaction()
32 .map_err(RepositoryError::Executor)?;
33 if matches!(boundary, GraphTransactionBoundary::Unsupported) {
34 return Err(RepositoryError::Runtime(RuntimeError::Graph(
35 "save_graph requires a transactional executor".to_owned(),
36 )));
37 }
38 let result = self
39 .plan_graph(node)
40 .and_then(|plan| self.execute_graph_plan(plan));
41 match result {
42 Ok(saved) => {
43 if matches!(boundary, GraphTransactionBoundary::Started) {
44 self.repository
45 .executor
46 .commit_transaction()
47 .map_err(RepositoryError::Executor)?;
48 }
49 Ok(saved)
50 }
51 Err(err) => {
52 if !matches!(boundary, GraphTransactionBoundary::Unsupported) {
53 self.repository
54 .executor
55 .rollback_transaction()
56 .map_err(RepositoryError::Executor)?;
57 }
58 Err(err)
59 }
60 }
61 }
62
63 pub fn save_entity_graph<T>(&self, entity: T) -> Result<GraphNode, RepositoryError<E::Error>>
64 where
65 T: Entity,
66 {
67 let node = self
68 .graph_node_from_entity(entity)
69 .map_err(RepositoryError::Runtime)?;
70 self.save_graph(node)
71 }
72
73 pub fn plan_graph(
74 &self,
75 node: GraphNode,
76 ) -> Result<GraphMutationPlan, RepositoryError<E::Error>> {
77 if node.entity != self.entity {
78 return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
79 "resolved repository {} cannot plan graph root {}",
80 self.entity, node.entity
81 ))));
82 }
83 let mut node = node;
84 let mut plan = GraphMutationPlan::default();
85 self.collect_graph_plan(&mut node, &mut plan)?;
86 plan.planned_root = Some(node);
87 plan.rebuild_batches();
88 Ok(plan)
89 }
90
91 pub fn execute_graph_plan(
92 &self,
93 plan: GraphMutationPlan,
94 ) -> Result<GraphNode, RepositoryError<E::Error>> {
95 let Some(root) = plan.planned_root else {
96 return Err(RepositoryError::Runtime(RuntimeError::Graph(
97 "graph mutation plan has no planned root".to_owned(),
98 )));
99 };
100 self.upsert_graph_node(root)
101 }
102
103 pub fn graph_node_from_entity<T>(&self, entity: T) -> Result<GraphNode, RuntimeError>
104 where
105 T: Entity,
106 {
107 let descriptor = T::entity_descriptor();
108 if descriptor.name != self.entity {
109 return Err(RuntimeError::Graph(format!(
110 "resolved repository {} cannot extract graph root {}",
111 self.entity, descriptor.name
112 )));
113 }
114 self.graph_node_from_record(&descriptor.name, entity.into_record())
115 }
116
117 fn collect_graph_plan(
118 &self,
119 node: &mut GraphNode,
120 plan: &mut GraphMutationPlan,
121 ) -> Result<(), RepositoryError<E::Error>> {
122 match node.operation {
123 GraphOperation::Reference => {
124 plan.push(
125 node.entity.clone(),
126 GraphMutationKind::Reference,
127 node.values.clone(),
128 Vec::new(),
129 );
130 return Ok(());
131 }
132 GraphOperation::Remove => {
133 plan.push(
134 node.entity.clone(),
135 GraphMutationKind::Delete,
136 node.values.clone(),
137 Vec::new(),
138 );
139 return Ok(());
140 }
141 GraphOperation::Upsert => {}
142 }
143
144 let descriptor = self
145 .repository
146 .metadata
147 .context
148 .require_entity(&node.entity)
149 .map_err(RepositoryError::Runtime)?;
150 let id_property = descriptor.id_property().cloned();
151 let id = id_property.as_ref().and_then(|property| {
152 node.values
153 .get(&property.name)
154 .filter(|value| !is_unassigned_id_value(value))
155 .cloned()
156 });
157 let is_update = match (id_property.as_ref(), id.as_ref()) {
158 (Some(id_property), Some(id)) => self
159 .fetch_graph_current_row(&node.entity, &id_property.name, id)?
160 .is_some(),
161 _ => false,
162 };
163 if !is_update {
164 if let Some(id_property) = id_property.as_ref() {
165 let needs_id = !node.values.contains_key(&id_property.name)
166 || node
167 .values
168 .get(&id_property.name)
169 .is_some_and(is_unassigned_id_value);
170 if needs_id {
171 let id = self
172 .repository
173 .metadata
174 .context
175 .next_id(&node.entity)
176 .map_err(RepositoryError::Runtime)?;
177 node.values.insert(id_property.name.clone(), Value::U64(id));
178 }
179 }
180 ensure_initial_version(&mut node.values, descriptor);
181 }
182 let update_fields = if is_update {
183 let mut excluded = Vec::new();
184 if let Some(id_property) = id_property.as_ref() {
185 excluded.push(id_property.name.clone());
186 }
187 if let Some(version_property) = descriptor.version_property() {
188 excluded.push(version_property.name.clone());
189 }
190 sorted_update_fields(&node.values, excluded)
191 } else {
192 Vec::new()
193 };
194 plan.push(
195 node.entity.clone(),
196 if is_update {
197 GraphMutationKind::Update
198 } else {
199 GraphMutationKind::Create
200 },
201 node.values.clone(),
202 update_fields,
203 );
204
205 for (name, children) in &mut node.relations {
206 let relation = descriptor.relation_by_name(name).ok_or_else(|| {
207 RepositoryError::Runtime(RuntimeError::MissingRelation {
208 entity: node.entity.clone(),
209 relation: name.clone(),
210 })
211 })?;
212 let child_repo = self.scoped_repository(relation.target_entity.clone());
213 for child in children {
214 ensure_relation_target(&node.entity, name, &relation.target_entity, child)?;
215 child_repo.collect_graph_plan(child, plan)?;
216 }
217 }
218 Ok(())
219 }
220
221 fn insert_graph_node(
222 &self,
223 mut node: GraphNode,
224 ) -> Result<GraphNode, RepositoryError<E::Error>> {
225 match node.operation {
226 GraphOperation::Upsert => {}
227 GraphOperation::Reference => return self.validate_reference_node(node),
228 GraphOperation::Remove => {
229 return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
230 "create graph cannot remove node {}",
231 node.entity
232 ))));
233 }
234 }
235
236 let descriptor = self
237 .repository
238 .metadata
239 .context
240 .require_entity(&node.entity)
241 .map_err(RepositoryError::Runtime)?;
242
243 let mut one_relations = Vec::new();
244 let mut many_relations = Vec::new();
245 for (name, children) in std::mem::take(&mut node.relations) {
246 let relation = descriptor.relation_by_name(&name).ok_or_else(|| {
247 RepositoryError::Runtime(RuntimeError::MissingRelation {
248 entity: node.entity.clone(),
249 relation: name.clone(),
250 })
251 })?;
252 if relation.many {
253 many_relations.push((name, relation.clone(), children));
254 } else {
255 one_relations.push((name, relation.clone(), children));
256 }
257 }
258
259 for (name, relation, children) in one_relations {
260 if children.len() > 1 {
261 return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
262 "relation {}.{} expects one child, got {}",
263 node.entity,
264 name,
265 children.len()
266 ))));
267 }
268 let mut saved_children = Vec::new();
269 for child in children {
270 ensure_relation_target(&node.entity, &name, &relation.target_entity, &child)?;
271 let child_repo = self.scoped_repository(child.entity.clone());
272 let saved_child = child_repo.insert_graph_node(child)?;
273 if relation.attach {
274 let foreign_value = saved_child
275 .values
276 .get(&relation.foreign_key)
277 .cloned()
278 .ok_or_else(|| {
279 RepositoryError::Runtime(RuntimeError::Graph(format!(
280 "saved child {} missing foreign key {} for relation {}.{}",
281 relation.target_entity, relation.foreign_key, node.entity, name
282 )))
283 })?;
284 node.values
285 .insert(relation.local_key.clone(), foreign_value);
286 }
287 saved_children.push(saved_child);
288 }
289 node.relations.insert(name, saved_children);
290 }
291
292 let command = self
293 .prepare_insert_command(&InsertCommand {
294 entity: node.entity.clone(),
295 values: node.values.clone(),
296 })
297 .map_err(RepositoryError::Runtime)?;
298 self.execute_prepared_insert(command.clone())?;
299 node.values = command.values;
300
301 for (name, relation, children) in many_relations {
302 let local_value = node
303 .values
304 .get(&relation.local_key)
305 .cloned()
306 .ok_or_else(|| {
307 RepositoryError::Runtime(RuntimeError::Graph(format!(
308 "parent {} missing local key {} for relation {}",
309 node.entity, relation.local_key, name
310 )))
311 })?;
312 let mut saved_children = Vec::new();
313 for mut child in children {
314 ensure_relation_target(&node.entity, &name, &relation.target_entity, &child)?;
315 if relation.attach {
316 child
317 .values
318 .insert(relation.foreign_key.clone(), local_value.clone());
319 }
320 let child_repo = self.scoped_repository(child.entity.clone());
321 saved_children.push(child_repo.insert_graph_node(child)?);
322 }
323 node.relations.insert(name, saved_children);
324 }
325
326 Ok(node)
327 }
328
329 fn upsert_graph_node(
330 &self,
331 mut node: GraphNode,
332 ) -> Result<GraphNode, RepositoryError<E::Error>> {
333 match node.operation {
334 GraphOperation::Upsert => {}
335 GraphOperation::Reference => return self.validate_reference_node(node),
336 GraphOperation::Remove => {
337 self.validate_remove_node(&node)?;
338 self.delete_graph_node(&node)?;
339 return Ok(node);
340 }
341 }
342
343 let descriptor = self
344 .repository
345 .metadata
346 .context
347 .require_entity(&node.entity)
348 .map_err(RepositoryError::Runtime)?;
349 let Some(id_property) = descriptor.id_property() else {
350 return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
351 "entity {} has no id property for graph upsert",
352 node.entity
353 ))));
354 };
355 let Some(id) = node
356 .values
357 .get(&id_property.name)
358 .filter(|value| !is_unassigned_id_value(value))
359 .cloned()
360 else {
361 return self.insert_graph_node(node);
362 };
363
364 if self
365 .fetch_graph_current_row(&node.entity, &id_property.name, &id)?
366 .is_none()
367 {
368 return self.insert_graph_node(node);
369 }
370
371 let mut one_relations = Vec::new();
372 let mut many_relations = Vec::new();
373 for (name, children) in std::mem::take(&mut node.relations) {
374 let relation = descriptor.relation_by_name(&name).ok_or_else(|| {
375 RepositoryError::Runtime(RuntimeError::MissingRelation {
376 entity: node.entity.clone(),
377 relation: name.clone(),
378 })
379 })?;
380 if relation.many {
381 many_relations.push((name, relation.clone(), children));
382 } else {
383 one_relations.push((name, relation.clone(), children));
384 }
385 }
386
387 for (name, relation, children) in one_relations {
388 if children.len() > 1 {
389 return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
390 "relation {}.{} expects one child, got {}",
391 node.entity,
392 name,
393 children.len()
394 ))));
395 }
396 let mut saved_children = Vec::new();
397 for child in children {
398 ensure_relation_target(&node.entity, &name, &relation.target_entity, &child)?;
399 let child_repo = self.scoped_repository(child.entity.clone());
400 let saved_child = child_repo.upsert_graph_node(child)?;
401 if relation.attach {
402 let foreign_value = saved_child
403 .values
404 .get(&relation.foreign_key)
405 .cloned()
406 .ok_or_else(|| {
407 RepositoryError::Runtime(RuntimeError::Graph(format!(
408 "saved child {} missing foreign key {} for relation {}.{}",
409 relation.target_entity, relation.foreign_key, node.entity, name
410 )))
411 })?;
412 node.values
413 .insert(relation.local_key.clone(), foreign_value);
414 }
415 saved_children.push(saved_child);
416 }
417 node.relations.insert(name, saved_children);
418 }
419
420 let update = self.graph_update_command(&node, descriptor, id_property, &id);
421 if !update.values.is_empty() || update.expected_version.is_some() {
422 let prepared_update = self
423 .prepare_update_command(&update)
424 .map_err(RepositoryError::Runtime)?;
425 self.execute_prepared_update(prepared_update.clone())?;
426 for (field, value) in &prepared_update.values {
427 node.values.insert(field.clone(), value.clone());
428 }
429 if let Some(version_property) = descriptor.version_property() {
430 if let Some(expected_version) = prepared_update.expected_version {
431 node.values.insert(
432 version_property.name.clone(),
433 Value::I64(expected_version + 1),
434 );
435 }
436 }
437 }
438
439 for (name, relation, children) in many_relations {
440 let local_value = node
441 .values
442 .get(&relation.local_key)
443 .cloned()
444 .ok_or_else(|| {
445 RepositoryError::Runtime(RuntimeError::Graph(format!(
446 "parent {} missing local key {} for relation {}",
447 node.entity, relation.local_key, name
448 )))
449 })?;
450 let child_repo = self.scoped_repository(relation.target_entity.clone());
451 let existing_children = child_repo.fetch_graph_children(
452 &relation.target_entity,
453 &relation.foreign_key,
454 &local_value,
455 )?;
456 let child_descriptor = self
457 .repository
458 .metadata
459 .context
460 .require_entity(&relation.target_entity)
461 .map_err(RepositoryError::Runtime)?;
462 let child_id_property = child_descriptor.id_property().ok_or_else(|| {
463 RepositoryError::Runtime(RuntimeError::Graph(format!(
464 "entity {} has no id property for graph diff",
465 relation.target_entity
466 )))
467 })?;
468 let mut existing_by_id = BTreeMap::new();
469 for child in existing_children {
470 if let Some(id) = child.get(&child_id_property.name) {
471 existing_by_id.insert(graph_identity_key(id), child);
472 }
473 }
474
475 let mut seen = std::collections::BTreeSet::new();
476 let mut saved_children = Vec::new();
477 for mut child in children {
478 ensure_relation_target(&node.entity, &name, &relation.target_entity, &child)?;
479 if relation.attach && child.operation != GraphOperation::Reference {
480 child
481 .values
482 .insert(relation.foreign_key.clone(), local_value.clone());
483 }
484 if let Some(child_id) = child
485 .values
486 .get(&child_id_property.name)
487 .filter(|value| !is_unassigned_id_value(value))
488 {
489 let key = graph_identity_key(child_id);
490 if !seen.insert(key.clone()) {
491 return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
492 "duplicate child id {key} in relation {}.{}",
493 node.entity, name
494 ))));
495 }
496 }
497 saved_children.push(child_repo.upsert_graph_node(child)?);
498 }
499
500 if relation.delete_missing {
501 for (id_key, existing) in existing_by_id {
502 if seen.contains(&id_key) {
503 continue;
504 }
505 let Some(existing_id) = existing.get(&child_id_property.name).cloned() else {
506 continue;
507 };
508 let mut delete =
509 DeleteCommand::new(relation.target_entity.clone(), existing_id);
510 if let Some(version) = graph_record_version(&existing, child_descriptor) {
511 delete = delete.expected_version(version);
512 }
513 child_repo.delete(&delete)?;
514 }
515 }
516
517 node.relations.insert(name, saved_children);
518 }
519
520 Ok(node)
521 }
522
523 fn validate_reference_node(
524 &self,
525 node: GraphNode,
526 ) -> Result<GraphNode, RepositoryError<E::Error>> {
527 if !node.relations.is_empty() {
528 return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
529 "reference node {} cannot contain child relations",
530 node.entity
531 ))));
532 }
533 let descriptor = self
534 .repository
535 .metadata
536 .context
537 .require_entity(&node.entity)
538 .map_err(RepositoryError::Runtime)?;
539 let id_property = descriptor.id_property().ok_or_else(|| {
540 RepositoryError::Runtime(RuntimeError::Graph(format!(
541 "entity {} has no id property for graph reference",
542 node.entity
543 )))
544 })?;
545 let id = node
546 .values
547 .get(&id_property.name)
548 .filter(|value| !is_unassigned_id_value(value))
549 .cloned()
550 .ok_or_else(|| {
551 RepositoryError::Runtime(RuntimeError::Graph(format!(
552 "reference node {} missing id property {}",
553 node.entity, id_property.name
554 )))
555 })?;
556
557 for field in node.values.keys() {
558 if field == &id_property.name {
559 continue;
560 }
561 if descriptor
562 .version_property()
563 .map(|property| field == &property.name)
564 .unwrap_or(false)
565 {
566 continue;
567 }
568 return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
569 "reference node {} cannot carry mutable field {}",
570 node.entity, field
571 ))));
572 }
573
574 let current = self
575 .fetch_graph_current_row(&node.entity, &id_property.name, &id)?
576 .ok_or_else(|| {
577 RepositoryError::Runtime(RuntimeError::Graph(format!(
578 "reference node {}({}) does not exist",
579 node.entity,
580 graph_identity_key(&id)
581 )))
582 })?;
583
584 if let Some(version_property) = descriptor.version_property() {
585 if let Some(Value::I64(existing_version)) = current.get(&version_property.name) {
586 if *existing_version < 0 {
587 return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
588 "reference node {}({}) is deleted",
589 node.entity,
590 graph_identity_key(&id)
591 ))));
592 }
593 if let Some(Value::I64(expected_version)) = node.values.get(&version_property.name)
594 {
595 if expected_version != existing_version {
596 return Err(RepositoryError::Runtime(
597 RuntimeError::OptimisticLockConflict {
598 entity: node.entity,
599 id: graph_identity_key(&id),
600 },
601 ));
602 }
603 }
604 }
605 }
606
607 Ok(GraphNode {
608 entity: node.entity,
609 values: current,
610 relations: BTreeMap::new(),
611 operation: GraphOperation::Reference,
612 })
613 }
614
615 fn validate_remove_node(&self, node: &GraphNode) -> Result<(), RepositoryError<E::Error>> {
616 if !node.relations.is_empty() {
617 return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
618 "remove node {} cannot contain child relations",
619 node.entity
620 ))));
621 }
622 let descriptor = self
623 .repository
624 .metadata
625 .context
626 .require_entity(&node.entity)
627 .map_err(RepositoryError::Runtime)?;
628 let id_property = descriptor.id_property().ok_or_else(|| {
629 RepositoryError::Runtime(RuntimeError::Graph(format!(
630 "entity {} has no id property for graph remove",
631 node.entity
632 )))
633 })?;
634 let id = node
635 .values
636 .get(&id_property.name)
637 .filter(|value| !is_unassigned_id_value(value))
638 .cloned()
639 .ok_or_else(|| {
640 RepositoryError::Runtime(RuntimeError::Graph(format!(
641 "remove node {} missing id property {}",
642 node.entity, id_property.name
643 )))
644 })?;
645 let current = self
646 .fetch_graph_current_row(&node.entity, &id_property.name, &id)?
647 .ok_or_else(|| {
648 RepositoryError::Runtime(RuntimeError::Graph(format!(
649 "remove node {}({}) does not exist",
650 node.entity,
651 graph_identity_key(&id)
652 )))
653 })?;
654 if let Some(version_property) = descriptor.version_property() {
655 if let Some(Value::I64(existing_version)) = current.get(&version_property.name) {
656 if *existing_version < 0 {
657 return Err(RepositoryError::Runtime(RuntimeError::Graph(format!(
658 "remove node {}({}) is already deleted",
659 node.entity,
660 graph_identity_key(&id)
661 ))));
662 }
663 }
664 }
665 Ok(())
666 }
667
668 fn graph_node_from_record(
669 &self,
670 entity: &str,
671 record: Record,
672 ) -> Result<GraphNode, RuntimeError> {
673 let descriptor = self.repository.metadata.context.require_entity(entity)?;
674 let mut node = GraphNode::new(entity);
675
676 for (field, value) in record {
677 let Some(relation) = descriptor.relation_by_name(&field) else {
678 node.values.insert(field, value);
679 continue;
680 };
681
682 match value {
683 Value::Null => {
684 node.relations.entry(field).or_default();
685 }
686 Value::Object(record) => {
687 let child = self.graph_node_from_record(&relation.target_entity, record)?;
688 node.relations.entry(field).or_default().push(child);
689 }
690 Value::List(values) => {
691 let children = node.relations.entry(field.clone()).or_default();
692 for value in values {
693 let Value::Object(record) = value else {
694 return Err(RuntimeError::Graph(format!(
695 "relation {}.{} expects object children, got {:?}",
696 entity, field, value
697 )));
698 };
699 children
700 .push(self.graph_node_from_record(&relation.target_entity, record)?);
701 }
702 }
703 other => {
704 return Err(RuntimeError::Graph(format!(
705 "relation {}.{} expects object/list/null, got {:?}",
706 entity, field, other
707 )));
708 }
709 }
710 }
711
712 Ok(node)
713 }
714
715 fn graph_update_command(
716 &self,
717 node: &GraphNode,
718 descriptor: &EntityDescriptor,
719 id_property: &PropertyDescriptor,
720 id: &Value,
721 ) -> UpdateCommand {
722 let mut command = UpdateCommand::new(node.entity.clone(), id.clone());
723 if let Some(version_property) = descriptor.version_property() {
724 if let Some(Value::I64(version)) = node.values.get(&version_property.name) {
725 command = command.expected_version(*version);
726 }
727 }
728 for property in descriptor.properties.iter().filter(|property| {
729 !property.is_id && !property.is_version && node.values.contains_key(&property.name)
730 }) {
731 if property.name == id_property.name {
732 continue;
733 }
734 if let Some(value) = node.values.get(&property.name) {
735 command.values.insert(property.name.clone(), value.clone());
736 }
737 }
738 command
739 }
740
741 fn delete_graph_node(&self, node: &GraphNode) -> Result<u64, RepositoryError<E::Error>> {
742 let descriptor = self
743 .repository
744 .metadata
745 .context
746 .require_entity(&node.entity)
747 .map_err(RepositoryError::Runtime)?;
748 let id_property = descriptor.id_property().ok_or_else(|| {
749 RepositoryError::Runtime(RuntimeError::Graph(format!(
750 "entity {} has no id property for graph remove",
751 node.entity
752 )))
753 })?;
754 let id = node
755 .values
756 .get(&id_property.name)
757 .filter(|value| !is_unassigned_id_value(value))
758 .cloned()
759 .ok_or_else(|| {
760 RepositoryError::Runtime(RuntimeError::Graph(format!(
761 "remove node {} missing id property {}",
762 node.entity, id_property.name
763 )))
764 })?;
765 let mut delete = DeleteCommand::new(node.entity.clone(), id);
766 if let Some(version_property) = descriptor.version_property() {
767 if let Some(Value::I64(version)) = node.values.get(&version_property.name) {
768 delete = delete.expected_version(*version);
769 }
770 }
771 self.delete(&delete)
772 }
773
774 fn fetch_graph_current_row(
775 &self,
776 entity: &str,
777 id_property: &str,
778 id: &Value,
779 ) -> Result<Option<Record>, RepositoryError<E::Error>> {
780 let mut rows = self
781 .scoped_repository(entity.to_owned())
782 .fetch_all(&SelectQuery::new(entity).filter(Expr::eq(id_property, id.clone())))?;
783 Ok(rows.pop())
784 }
785
786 fn fetch_graph_children(
787 &self,
788 entity: &str,
789 foreign_key: &str,
790 parent_value: &Value,
791 ) -> Result<Vec<Record>, RepositoryError<E::Error>> {
792 self.scoped_repository(entity.to_owned()).fetch_all(
793 &SelectQuery::new(entity).filter(Expr::eq(foreign_key, parent_value.clone())),
794 )
795 }
796}