Skip to main content

common/direct_access/entity/
entity_repository.rs

1// Generated by Qleany v1.11.0 from common_entity_repository.tera
2
3use std::fmt::Display;
4
5use crate::{
6    database::hashmap_store::HashMapStoreSnapshot,
7    // `*_or_recover` rather than a plain `.read()/.write().unwrap()`: a panic
8    // anywhere under a table's guard poisons that lock for the rest of the
9    // process, and the release profile unwinds, so the poison is reachable in a
10    // shipped build. Recovering (and clearing) it keeps one unrelated failure
11    // from escalating into a second panic on every later access to the store.
12    database::hashmap_store::{read_or_recover, write_or_recover},
13    database::transactions::Transaction,
14    direct_access::repository_factory,
15    entities::Entity,
16    event::{DirectAccessEntity, EntityEvent, Event, EventBuffer, Origin},
17    snapshot::EntityTreeSnapshot,
18    types::EntityId,
19};
20
21use crate::direct_access::workspace::WorkspaceRelationshipField;
22use crate::error::RepositoryError;
23use serde::{Deserialize, Serialize};
24
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26pub enum EntityRelationshipField {
27    Fields,
28    InheritsFrom,
29    Relationships,
30}
31
32impl Display for EntityRelationshipField {
33    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
34        write!(f, "{:?}", self)
35    }
36}
37
38pub trait EntityTable {
39    fn create(&mut self, entity: &Entity) -> Result<Entity, RepositoryError>;
40    fn create_multi(&mut self, entities: &[Entity]) -> Result<Vec<Entity>, RepositoryError>;
41    fn get(&self, id: &EntityId) -> Result<Option<Entity>, RepositoryError>;
42    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Entity>>, RepositoryError>;
43    fn get_all(&self) -> Result<Vec<Entity>, RepositoryError>;
44    fn update(&mut self, entity: &Entity) -> Result<Entity, RepositoryError>;
45    fn update_multi(&mut self, entities: &[Entity]) -> Result<Vec<Entity>, RepositoryError>;
46    fn update_with_relationships(&mut self, entity: &Entity) -> Result<Entity, RepositoryError>;
47    fn update_with_relationships_multi(
48        &mut self,
49        entities: &[Entity],
50    ) -> Result<Vec<Entity>, RepositoryError>;
51    fn remove(&mut self, id: &EntityId) -> Result<(), RepositoryError>;
52    fn remove_multi(&mut self, ids: &[EntityId]) -> Result<(), RepositoryError>;
53    fn get_relationship(
54        &self,
55        id: &EntityId,
56        field: &EntityRelationshipField,
57    ) -> Result<Vec<EntityId>, RepositoryError>;
58    fn get_relationship_many(
59        &self,
60        ids: &[EntityId],
61        field: &EntityRelationshipField,
62    ) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError>;
63    fn get_relationship_count(
64        &self,
65        id: &EntityId,
66        field: &EntityRelationshipField,
67    ) -> Result<usize, RepositoryError>;
68    fn get_relationship_in_range(
69        &self,
70        id: &EntityId,
71        field: &EntityRelationshipField,
72        offset: usize,
73        limit: usize,
74    ) -> Result<Vec<EntityId>, RepositoryError>;
75    fn get_relationships_from_right_ids(
76        &self,
77        field: &EntityRelationshipField,
78        right_ids: &[EntityId],
79    ) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError>;
80    fn set_relationship_multi(
81        &mut self,
82        field: &EntityRelationshipField,
83        relationships: Vec<(EntityId, Vec<EntityId>)>,
84    ) -> Result<(), RepositoryError>;
85    fn set_relationship(
86        &mut self,
87        id: &EntityId,
88        field: &EntityRelationshipField,
89        right_ids: &[EntityId],
90    ) -> Result<(), RepositoryError>;
91    fn move_relationship_ids(
92        &mut self,
93        id: &EntityId,
94        field: &EntityRelationshipField,
95        ids_to_move: &[EntityId],
96        new_index: i32,
97    ) -> Result<Vec<EntityId>, RepositoryError>;
98}
99
100pub trait EntityTableRO {
101    fn get(&self, id: &EntityId) -> Result<Option<Entity>, RepositoryError>;
102    fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Entity>>, RepositoryError>;
103    fn get_all(&self) -> Result<Vec<Entity>, RepositoryError>;
104    fn get_relationship(
105        &self,
106        id: &EntityId,
107        field: &EntityRelationshipField,
108    ) -> Result<Vec<EntityId>, RepositoryError>;
109    fn get_relationship_many(
110        &self,
111        ids: &[EntityId],
112        field: &EntityRelationshipField,
113    ) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError>;
114    fn get_relationship_count(
115        &self,
116        id: &EntityId,
117        field: &EntityRelationshipField,
118    ) -> Result<usize, RepositoryError>;
119    fn get_relationship_in_range(
120        &self,
121        id: &EntityId,
122        field: &EntityRelationshipField,
123        offset: usize,
124        limit: usize,
125    ) -> Result<Vec<EntityId>, RepositoryError>;
126    fn get_relationships_from_right_ids(
127        &self,
128        field: &EntityRelationshipField,
129        right_ids: &[EntityId],
130    ) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError>;
131}
132
133pub struct EntityRepository<'a> {
134    table: Box<dyn EntityTable + 'a>,
135    transaction: &'a Transaction,
136}
137
138impl<'a> EntityRepository<'a> {
139    pub fn new(table: Box<dyn EntityTable + 'a>, transaction: &'a Transaction) -> Self {
140        EntityRepository { table, transaction }
141    }
142
143    pub fn create_orphan(
144        &mut self,
145        event_buffer: &mut EventBuffer,
146        entity: &Entity,
147    ) -> Result<Entity, RepositoryError> {
148        let new = self.table.create(entity)?;
149        event_buffer.push(Event {
150            origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Created)),
151            ids: vec![new.id],
152            data: None,
153        });
154        Ok(new)
155    }
156
157    pub fn create_orphan_multi(
158        &mut self,
159        event_buffer: &mut EventBuffer,
160        entities: &[Entity],
161    ) -> Result<Vec<Entity>, RepositoryError> {
162        let new_entities = self.table.create_multi(entities)?;
163        event_buffer.push(Event {
164            origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Created)),
165            ids: new_entities.iter().map(|e| e.id).collect(),
166            data: None,
167        });
168        Ok(new_entities)
169    }
170    pub fn create(
171        &mut self,
172        event_buffer: &mut EventBuffer,
173        entity: &Entity,
174        owner_id: EntityId,
175        index: i32,
176    ) -> Result<Entity, RepositoryError> {
177        let new = self.table.create(entity)?;
178        let created_id = new.id;
179
180        let mut relationship_ids = self.get_relationships_from_owner(&owner_id)?;
181        // Insert at index
182        if index >= 0 && (index as usize) < relationship_ids.len() {
183            relationship_ids.insert(index as usize, created_id);
184        } else {
185            relationship_ids.push(created_id);
186        }
187
188        self.set_relationships_in_owner(event_buffer, &owner_id, &relationship_ids)?;
189        event_buffer.push(Event {
190            origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Created)),
191            ids: vec![created_id],
192            data: None,
193        });
194        Ok(new)
195    }
196
197    pub fn create_multi(
198        &mut self,
199        event_buffer: &mut EventBuffer,
200        entities: &[Entity],
201        owner_id: EntityId,
202        index: i32,
203    ) -> Result<Vec<Entity>, RepositoryError> {
204        let new_entities = self.table.create_multi(entities)?;
205        let created_ids: Vec<EntityId> = new_entities.iter().map(|e| e.id).collect();
206
207        let mut relationship_ids = self.get_relationships_from_owner(&owner_id)?;
208        if index >= 0 && (index as usize) < relationship_ids.len() {
209            for (i, id) in created_ids.iter().enumerate() {
210                relationship_ids.insert(index as usize + i, *id);
211            }
212        } else {
213            relationship_ids.extend(created_ids.iter());
214        }
215
216        self.set_relationships_in_owner(event_buffer, &owner_id, &relationship_ids)?;
217        event_buffer.push(Event {
218            origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Created)),
219            ids: created_ids,
220            data: None,
221        });
222        Ok(new_entities)
223    }
224
225    pub fn get(&self, id: &EntityId) -> Result<Option<Entity>, RepositoryError> {
226        self.table.get(id)
227    }
228    pub fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Entity>>, RepositoryError> {
229        self.table.get_multi(ids)
230    }
231    pub fn get_all(&self) -> Result<Vec<Entity>, RepositoryError> {
232        self.table.get_all()
233    }
234
235    pub fn update(
236        &mut self,
237        event_buffer: &mut EventBuffer,
238        entity: &Entity,
239    ) -> Result<Entity, RepositoryError> {
240        let updated = self.table.update(entity)?;
241        event_buffer.push(Event {
242            origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Updated)),
243            ids: vec![updated.id],
244            data: None,
245        });
246        Ok(updated)
247    }
248
249    pub fn update_multi(
250        &mut self,
251        event_buffer: &mut EventBuffer,
252        entities: &[Entity],
253    ) -> Result<Vec<Entity>, RepositoryError> {
254        let updated = self.table.update_multi(entities)?;
255        event_buffer.push(Event {
256            origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Updated)),
257            ids: updated.iter().map(|e| e.id).collect(),
258            data: None,
259        });
260        Ok(updated)
261    }
262
263    pub fn update_with_relationships(
264        &mut self,
265        event_buffer: &mut EventBuffer,
266        entity: &Entity,
267    ) -> Result<Entity, RepositoryError> {
268        let updated = self.table.update_with_relationships(entity)?;
269        event_buffer.push(Event {
270            origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Updated)),
271            ids: vec![updated.id],
272            data: None,
273        });
274        Ok(updated)
275    }
276
277    pub fn update_with_relationships_multi(
278        &mut self,
279        event_buffer: &mut EventBuffer,
280        entities: &[Entity],
281    ) -> Result<Vec<Entity>, RepositoryError> {
282        let updated = self.table.update_with_relationships_multi(entities)?;
283        event_buffer.push(Event {
284            origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Updated)),
285            ids: updated.iter().map(|e| e.id).collect(),
286            data: None,
287        });
288        Ok(updated)
289    }
290
291    pub fn remove(
292        &mut self,
293        event_buffer: &mut EventBuffer,
294        id: &EntityId,
295    ) -> Result<(), RepositoryError> {
296        let entity = match self.table.get(id)? {
297            Some(e) => e,
298            None => return Ok(()),
299        };
300        // get all strong forward relationship fields
301
302        let fields = entity.fields.clone();
303        let relationships = entity.relationships.clone();
304
305        // remove all strong relationships, initiating a cascade remove
306
307        repository_factory::write::create_field_repository(self.transaction)?
308            .remove_multi(event_buffer, &fields)?;
309        repository_factory::write::create_relationship_repository(self.transaction)?
310            .remove_multi(event_buffer, &relationships)?;
311        // Before removal, find which owner(s) reference this entity
312        let affected_owner_ids: Vec<EntityId> = {
313            let owner_repo =
314                repository_factory::write::create_workspace_repository(self.transaction)?;
315            owner_repo
316                .get_relationships_from_right_ids(&WorkspaceRelationshipField::Entities, &[*id])?
317                .into_iter()
318                .map(|(owner_id, _)| owner_id)
319                .collect()
320        };
321        // Save each owner's current relationship IDs (properly ordered via get_relationships_from_owner)
322        let mut owner_rel_before: std::collections::HashMap<EntityId, Vec<EntityId>> =
323            std::collections::HashMap::new();
324        for owner_id in &affected_owner_ids {
325            owner_rel_before.insert(*owner_id, self.get_relationships_from_owner(owner_id)?);
326        }
327
328        // remove entity
329        self.table.remove(id)?;
330        event_buffer.push(Event {
331            origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Removed)),
332            ids: vec![*id],
333            data: None,
334        });
335        // Update each affected owner's relationship to exclude removed ID (emits Updated event)
336        for owner_id in &affected_owner_ids {
337            if let Some(rel_ids) = owner_rel_before.get(owner_id) {
338                let updated: Vec<EntityId> =
339                    rel_ids.iter().copied().filter(|rid| *rid != *id).collect();
340                self.set_relationships_in_owner(event_buffer, owner_id, &updated)?;
341            }
342        }
343
344        Ok(())
345    }
346
347    pub fn remove_multi(
348        &mut self,
349        event_buffer: &mut EventBuffer,
350        ids: &[EntityId],
351    ) -> Result<(), RepositoryError> {
352        let entities = self.table.get_multi(ids)?;
353        if entities.is_empty() || entities.iter().all(|e| e.is_none()) {
354            return Ok(());
355        }
356
357        // get all strong forward relationship fields
358
359        let mut fields_ids: Vec<EntityId> = entities
360            .iter()
361            .flat_map(|entity| entity.as_ref().map(|entity| entity.fields.clone()))
362            .flatten()
363            .collect();
364        // remove duplicates
365        fields_ids.sort();
366        fields_ids.dedup();
367        let mut relationships_ids: Vec<EntityId> = entities
368            .iter()
369            .flat_map(|entity| entity.as_ref().map(|entity| entity.relationships.clone()))
370            .flatten()
371            .collect();
372        // remove duplicates
373        relationships_ids.sort();
374        relationships_ids.dedup();
375
376        // remove all strong relationships, initiating a cascade remove
377
378        repository_factory::write::create_field_repository(self.transaction)?
379            .remove_multi(event_buffer, &fields_ids)?;
380        repository_factory::write::create_relationship_repository(self.transaction)?
381            .remove_multi(event_buffer, &relationships_ids)?;
382        // Before removal, find which owner(s) reference these entities
383        let affected_owner_ids: Vec<EntityId> = {
384            let owner_repo =
385                repository_factory::write::create_workspace_repository(self.transaction)?;
386            owner_repo
387                .get_relationships_from_right_ids(&WorkspaceRelationshipField::Entities, ids)?
388                .into_iter()
389                .map(|(owner_id, _)| owner_id)
390                .collect()
391        };
392        // Save each owner's current relationship IDs (properly ordered via get_relationships_from_owner)
393        let mut owner_rel_before: std::collections::HashMap<EntityId, Vec<EntityId>> =
394            std::collections::HashMap::new();
395        for owner_id in &affected_owner_ids {
396            owner_rel_before.insert(*owner_id, self.get_relationships_from_owner(owner_id)?);
397        }
398
399        self.table.remove_multi(ids)?;
400        event_buffer.push(Event {
401            origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Removed)),
402            ids: ids.into(),
403            data: None,
404        });
405        // Update each affected owner's relationship to exclude removed IDs (emits Updated event)
406        {
407            let removed_set: std::collections::HashSet<EntityId> = ids.iter().copied().collect();
408            for owner_id in &affected_owner_ids {
409                if let Some(rel_ids) = owner_rel_before.get(owner_id) {
410                    let updated: Vec<EntityId> = rel_ids
411                        .iter()
412                        .copied()
413                        .filter(|rid| !removed_set.contains(rid))
414                        .collect();
415                    self.set_relationships_in_owner(event_buffer, owner_id, &updated)?;
416                }
417            }
418        }
419
420        Ok(())
421    }
422    pub fn get_relationship(
423        &self,
424        id: &EntityId,
425        field: &EntityRelationshipField,
426    ) -> Result<Vec<EntityId>, RepositoryError> {
427        self.table.get_relationship(id, field)
428    }
429    pub fn get_relationship_many(
430        &self,
431        ids: &[EntityId],
432        field: &EntityRelationshipField,
433    ) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError> {
434        self.table.get_relationship_many(ids, field)
435    }
436    pub fn get_relationship_count(
437        &self,
438        id: &EntityId,
439        field: &EntityRelationshipField,
440    ) -> Result<usize, RepositoryError> {
441        self.table.get_relationship_count(id, field)
442    }
443    pub fn get_relationship_in_range(
444        &self,
445        id: &EntityId,
446        field: &EntityRelationshipField,
447        offset: usize,
448        limit: usize,
449    ) -> Result<Vec<EntityId>, RepositoryError> {
450        self.table
451            .get_relationship_in_range(id, field, offset, limit)
452    }
453    pub fn get_relationships_from_right_ids(
454        &self,
455        field: &EntityRelationshipField,
456        right_ids: &[EntityId],
457    ) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError> {
458        self.table
459            .get_relationships_from_right_ids(field, right_ids)
460    }
461
462    pub fn set_relationship_multi(
463        &mut self,
464        event_buffer: &mut EventBuffer,
465        field: &EntityRelationshipField,
466        relationships: Vec<(EntityId, Vec<EntityId>)>,
467    ) -> Result<(), RepositoryError> {
468        // Validate that all right_ids exist
469        let all_right_ids: Vec<EntityId> = relationships
470            .iter()
471            .flat_map(|(_, ids)| ids.iter().copied())
472            .collect();
473        if !all_right_ids.is_empty() {
474            match field {
475                EntityRelationshipField::Fields => {
476                    let child_repo =
477                        repository_factory::write::create_field_repository(self.transaction)?;
478                    let found = child_repo.get_multi(&all_right_ids)?;
479                    let missing: Vec<_> = all_right_ids
480                        .iter()
481                        .zip(found.iter())
482                        .filter(|(_, entity)| entity.is_none())
483                        .map(|(id, _)| *id)
484                        .collect();
485                    if !missing.is_empty() {
486                        return Err(RepositoryError::MissingRelationshipTarget {
487                            operation: "set_relationship_multi",
488                            ids: missing,
489                        });
490                    }
491                }
492                EntityRelationshipField::InheritsFrom => {
493                    let child_repo =
494                        repository_factory::write::create_entity_repository(self.transaction)?;
495                    let found = child_repo.get_multi(&all_right_ids)?;
496                    let missing: Vec<_> = all_right_ids
497                        .iter()
498                        .zip(found.iter())
499                        .filter(|(_, entity)| entity.is_none())
500                        .map(|(id, _)| *id)
501                        .collect();
502                    if !missing.is_empty() {
503                        return Err(RepositoryError::MissingRelationshipTarget {
504                            operation: "set_relationship_multi",
505                            ids: missing,
506                        });
507                    }
508                }
509                EntityRelationshipField::Relationships => {
510                    let child_repo = repository_factory::write::create_relationship_repository(
511                        self.transaction,
512                    )?;
513                    let found = child_repo.get_multi(&all_right_ids)?;
514                    let missing: Vec<_> = all_right_ids
515                        .iter()
516                        .zip(found.iter())
517                        .filter(|(_, entity)| entity.is_none())
518                        .map(|(id, _)| *id)
519                        .collect();
520                    if !missing.is_empty() {
521                        return Err(RepositoryError::MissingRelationshipTarget {
522                            operation: "set_relationship_multi",
523                            ids: missing,
524                        });
525                    }
526                }
527            }
528        }
529        self.table
530            .set_relationship_multi(field, relationships.clone())?;
531        for (left_id, right_ids) in relationships {
532            event_buffer.push(Event {
533                origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Updated)),
534                ids: vec![left_id],
535                data: Some(format!(
536                    "{}:{}",
537                    field,
538                    right_ids
539                        .iter()
540                        .map(|id| id.to_string())
541                        .collect::<Vec<_>>()
542                        .join(",")
543                )),
544            });
545        }
546        Ok(())
547    }
548
549    pub fn set_relationship(
550        &mut self,
551        event_buffer: &mut EventBuffer,
552        id: &EntityId,
553        field: &EntityRelationshipField,
554        right_ids: &[EntityId],
555    ) -> Result<(), RepositoryError> {
556        // Validate that all right_ids exist
557        if !right_ids.is_empty() {
558            match field {
559                EntityRelationshipField::Fields => {
560                    let child_repo =
561                        repository_factory::write::create_field_repository(self.transaction)?;
562                    let found = child_repo.get_multi(right_ids)?;
563                    let missing: Vec<_> = right_ids
564                        .iter()
565                        .zip(found.iter())
566                        .filter(|(_, entity)| entity.is_none())
567                        .map(|(id, _)| *id)
568                        .collect();
569                    if !missing.is_empty() {
570                        return Err(RepositoryError::MissingRelationshipTarget {
571                            operation: "set_relationship",
572                            ids: missing,
573                        });
574                    }
575                }
576                EntityRelationshipField::InheritsFrom => {
577                    let child_repo =
578                        repository_factory::write::create_entity_repository(self.transaction)?;
579                    let found = child_repo.get_multi(right_ids)?;
580                    let missing: Vec<_> = right_ids
581                        .iter()
582                        .zip(found.iter())
583                        .filter(|(_, entity)| entity.is_none())
584                        .map(|(id, _)| *id)
585                        .collect();
586                    if !missing.is_empty() {
587                        return Err(RepositoryError::MissingRelationshipTarget {
588                            operation: "set_relationship",
589                            ids: missing,
590                        });
591                    }
592                }
593                EntityRelationshipField::Relationships => {
594                    let child_repo = repository_factory::write::create_relationship_repository(
595                        self.transaction,
596                    )?;
597                    let found = child_repo.get_multi(right_ids)?;
598                    let missing: Vec<_> = right_ids
599                        .iter()
600                        .zip(found.iter())
601                        .filter(|(_, entity)| entity.is_none())
602                        .map(|(id, _)| *id)
603                        .collect();
604                    if !missing.is_empty() {
605                        return Err(RepositoryError::MissingRelationshipTarget {
606                            operation: "set_relationship",
607                            ids: missing,
608                        });
609                    }
610                }
611            }
612        }
613        self.table.set_relationship(id, field, right_ids)?;
614        event_buffer.push(Event {
615            origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Updated)),
616            ids: vec![*id],
617            data: Some(format!(
618                "{}:{}",
619                field,
620                right_ids
621                    .iter()
622                    .map(|id| id.to_string())
623                    .collect::<Vec<_>>()
624                    .join(",")
625            )),
626        });
627        Ok(())
628    }
629
630    pub fn move_relationship_ids(
631        &mut self,
632        event_buffer: &mut EventBuffer,
633        id: &EntityId,
634        field: &EntityRelationshipField,
635        ids_to_move: &[EntityId],
636        new_index: i32,
637    ) -> Result<Vec<EntityId>, RepositoryError> {
638        let reordered = self
639            .table
640            .move_relationship_ids(id, field, ids_to_move, new_index)?;
641        event_buffer.push(Event {
642            origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Updated)),
643            ids: vec![*id],
644            data: Some(format!(
645                "{}:{}",
646                field,
647                reordered
648                    .iter()
649                    .map(|id| id.to_string())
650                    .collect::<Vec<_>>()
651                    .join(",")
652            )),
653        });
654        Ok(reordered)
655    }
656    pub fn get_relationships_from_owner(
657        &self,
658        owner_id: &EntityId,
659    ) -> Result<Vec<EntityId>, RepositoryError> {
660        let repo = repository_factory::write::create_workspace_repository(self.transaction)?;
661        repo.get_relationship(owner_id, &WorkspaceRelationshipField::Entities)
662    }
663
664    pub fn set_relationships_in_owner(
665        &mut self,
666        event_buffer: &mut EventBuffer,
667        owner_id: &EntityId,
668        ids: &[EntityId],
669    ) -> Result<(), RepositoryError> {
670        let mut repo = repository_factory::write::create_workspace_repository(self.transaction)?;
671        repo.set_relationship(
672            event_buffer,
673            owner_id,
674            &WorkspaceRelationshipField::Entities,
675            ids,
676        )
677    }
678
679    pub fn snapshot(&self, ids: &[EntityId]) -> Result<EntityTreeSnapshot, RepositoryError> {
680        // O(1) whole-store capture (im::HashMap structural sharing). Restore is scoped to `ids`.
681        let store_snap = self.transaction.snapshot_store();
682        Ok(EntityTreeSnapshot {
683            store_snapshot: Some(store_snap),
684            root_ids: ids.to_vec(),
685        })
686    }
687
688    pub fn restore(
689        &mut self,
690        event_buffer: &mut EventBuffer,
691        snap: &EntityTreeSnapshot,
692    ) -> Result<(), RepositoryError> {
693        let store_snap = snap
694            .store_snapshot
695            .as_ref()
696            .ok_or_else(|| RepositoryError::Serialization("missing store snapshot".into()))?;
697        let snap_store = store_snap
698            .downcast_ref::<HashMapStoreSnapshot>()
699            .ok_or_else(|| RepositoryError::Serialization("snapshot type mismatch".into()))?;
700
701        // Scoped restore: walk the subtree rooted at the captured ids (strong relationships only),
702        // in both the snapshot and the live store, and reconcile only it.
703        let mut visited = std::collections::HashSet::new();
704        self.restore_subtree(event_buffer, snap_store, &snap.root_ids, &mut visited)?;
705
706        // Surgically reconcile the root ids' placement in their (external) strong owner,
707        // preserving sibling edits made on other undo stacks.
708        self.reconcile_backref_workspace_entities(event_buffer, snap_store, &snap.root_ids)?;
709
710        Ok(())
711    }
712
713    /// Restore the subtree rooted at `ids` (of this entity type) to the snapshot state, recursing
714    /// into strong children. Touches only the subtree: in-scope rows and the forward junctions they
715    /// own are restored wholesale; external referrers are reconciled surgically; everything else
716    /// (other trunks, non-undoable data) is left untouched. `visited` guards against cycles.
717    pub(crate) fn restore_subtree(
718        &self,
719        event_buffer: &mut EventBuffer,
720        snap: &HashMapStoreSnapshot,
721        ids: &[EntityId],
722        // Keyed by (entity type, id): EntityIds are per-type, not globally unique, so a bare
723        // id set would let one entity type's id falsely mask another's.
724        visited: &mut std::collections::HashSet<(&'static str, EntityId)>,
725    ) -> Result<(), RepositoryError> {
726        let ids: Vec<EntityId> = ids
727            .iter()
728            .copied()
729            .filter(|id| visited.insert(("entity", *id)))
730            .collect();
731        if ids.is_empty() {
732            return Ok(());
733        }
734
735        let store = self.transaction.get_store();
736
737        // Three-way partition by presence in snapshot vs live store (read before mutating).
738        let mut to_create: Vec<EntityId> = Vec::new(); // in snapshot, gone from live -> re-add
739        let mut to_update: Vec<EntityId> = Vec::new(); // in both                    -> revert
740        let mut to_delete: Vec<EntityId> = Vec::new(); // live only (created after)  -> delete
741        {
742            let live = read_or_recover(&store.entities);
743            for id in &ids {
744                match (snap.entities.contains_key(id), live.contains_key(id)) {
745                    (true, false) => to_create.push(*id),
746                    (true, true) => to_update.push(*id),
747                    (false, true) => to_delete.push(*id),
748                    (false, false) => {}
749                }
750            }
751        }
752
753        // 1. Recurse into strong children first (collect child ids from snapshot + live, then drop
754        //    all locks before recursing so self-referential ownership cannot deadlock).
755        {
756            let mut child_ids: Vec<EntityId> = Vec::new();
757            for id in to_create.iter().chain(to_update.iter()) {
758                if let Some(list) = snap.jn_field_from_entity_fields.get(id) {
759                    child_ids.extend(list.iter().copied());
760                }
761            }
762            {
763                let live_jn = read_or_recover(&store.jn_field_from_entity_fields);
764                for id in &ids {
765                    if let Some(list) = live_jn.get(id) {
766                        child_ids.extend(list.iter().copied());
767                    }
768                }
769            }
770            child_ids.sort();
771            child_ids.dedup();
772            if !child_ids.is_empty() {
773                repository_factory::write::create_field_repository(self.transaction)?
774                    .restore_subtree(event_buffer, snap, &child_ids, visited)?;
775            }
776        }
777        {
778            let mut child_ids: Vec<EntityId> = Vec::new();
779            for id in to_create.iter().chain(to_update.iter()) {
780                if let Some(list) = snap.jn_relationship_from_entity_relationships.get(id) {
781                    child_ids.extend(list.iter().copied());
782                }
783            }
784            {
785                let live_jn = read_or_recover(&store.jn_relationship_from_entity_relationships);
786                for id in &ids {
787                    if let Some(list) = live_jn.get(id) {
788                        child_ids.extend(list.iter().copied());
789                    }
790                }
791            }
792            child_ids.sort();
793            child_ids.dedup();
794            if !child_ids.is_empty() {
795                repository_factory::write::create_relationship_repository(self.transaction)?
796                    .restore_subtree(event_buffer, snap, &child_ids, visited)?;
797            }
798        }
799
800        // 2. Entity rows: revert/re-add from snapshot, delete the ones created after it.
801        {
802            let mut live = write_or_recover(&store.entities);
803            for id in to_create.iter().chain(to_update.iter()) {
804                if let Some(row) = snap.entities.get(id) {
805                    live.insert(*id, row.clone());
806                }
807            }
808            for id in &to_delete {
809                live.remove(id);
810            }
811        }
812
813        // 3. This entity's own forward junctions (strong + weak): restored wholesale because the
814        //    junction key is in-scope (owned exclusively by this trunk).
815        {
816            let mut live_jn = write_or_recover(&store.jn_field_from_entity_fields);
817            for id in to_create.iter().chain(to_update.iter()) {
818                match snap.jn_field_from_entity_fields.get(id) {
819                    Some(v) => {
820                        live_jn.insert(*id, v.clone());
821                    }
822                    None => {
823                        live_jn.remove(id);
824                    }
825                }
826            }
827            for id in &to_delete {
828                live_jn.remove(id);
829            }
830        }
831        {
832            let mut live_jn = write_or_recover(&store.jn_entity_from_entity_inherits_from);
833            for id in to_create.iter().chain(to_update.iter()) {
834                match snap.jn_entity_from_entity_inherits_from.get(id) {
835                    Some(v) => {
836                        live_jn.insert(*id, v.clone());
837                    }
838                    None => {
839                        live_jn.remove(id);
840                    }
841                }
842            }
843            for id in &to_delete {
844                live_jn.remove(id);
845            }
846        }
847        {
848            let mut live_jn = write_or_recover(&store.jn_relationship_from_entity_relationships);
849            for id in to_create.iter().chain(to_update.iter()) {
850                match snap.jn_relationship_from_entity_relationships.get(id) {
851                    Some(v) => {
852                        live_jn.insert(*id, v.clone());
853                    }
854                    None => {
855                        live_jn.remove(id);
856                    }
857                }
858            }
859            for id in &to_delete {
860                live_jn.remove(id);
861            }
862        }
863
864        // 4. External weak referrers of these ids: reconcile surgically (membership only).
865        self.reconcile_backref_use_case_entities(event_buffer, snap, &ids)?;
866        self.reconcile_backref_field_entity(event_buffer, snap, &ids)?;
867        self.reconcile_backref_file_entity(event_buffer, snap, &ids)?;
868        self.reconcile_backref_entity_inherits_from(event_buffer, snap, &ids)?;
869        self.reconcile_backref_relationship_left_entity(event_buffer, snap, &ids)?;
870        self.reconcile_backref_relationship_right_entity(event_buffer, snap, &ids)?;
871
872        // 5. Events: precise per-id (Created/Updated/Removed), not a whole-store storm.
873        if !to_create.is_empty() {
874            event_buffer.push(Event {
875                origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Created)),
876                ids: to_create.clone(),
877                data: None,
878            });
879            event_buffer.push(Event {
880                origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Updated)),
881                ids: to_create.clone(),
882                data: None,
883            });
884        }
885        if !to_update.is_empty() {
886            event_buffer.push(Event {
887                origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Updated)),
888                ids: to_update.clone(),
889                data: None,
890            });
891        }
892        if !to_delete.is_empty() {
893            event_buffer.push(Event {
894                origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Removed)),
895                ids: to_delete.clone(),
896                data: None,
897            });
898        }
899
900        Ok(())
901    }
902
903    /// Surgically reconcile one external referrer junction (out-of-scope key -> in-scope values):
904    /// `use_case.entities`.
905    fn reconcile_backref_use_case_entities(
906        &self,
907        event_buffer: &mut EventBuffer,
908        snap: &HashMapStoreSnapshot,
909        scope_ids: &[EntityId],
910    ) -> Result<(), RepositoryError> {
911        let scope: std::collections::HashSet<EntityId> = scope_ids.iter().copied().collect();
912        if scope.is_empty() {
913            return Ok(());
914        }
915        let store = self.transaction.get_store();
916
917        // External left keys whose ordered list references any scope id, in snapshot or live.
918        let mut left_keys: std::collections::HashSet<EntityId> = std::collections::HashSet::new();
919        for (left, rights) in snap.jn_entity_from_use_case_entities.iter() {
920            if rights.iter().any(|rid| scope.contains(rid)) {
921                left_keys.insert(*left);
922            }
923        }
924        {
925            let live_jn = read_or_recover(&store.jn_entity_from_use_case_entities);
926            for (left, rights) in live_jn.iter() {
927                if rights.iter().any(|rid| scope.contains(rid)) {
928                    left_keys.insert(*left);
929                }
930            }
931        }
932
933        for left in left_keys {
934            let snap_list: Vec<EntityId> = snap
935                .jn_entity_from_use_case_entities
936                .get(&left)
937                .cloned()
938                .unwrap_or_default();
939            let new_list = {
940                let live_jn = read_or_recover(&store.jn_entity_from_use_case_entities);
941                let live_list: Vec<EntityId> = live_jn.get(&left).cloned().unwrap_or_default();
942                let reconciled = crate::database::hashmap_store::reconcile_backref_list(
943                    &live_list, &snap_list, &scope,
944                );
945                if reconciled == live_list {
946                    None
947                } else {
948                    Some(reconciled)
949                }
950            };
951            if let Some(reconciled) = new_list {
952                write_or_recover(&store.jn_entity_from_use_case_entities).insert(left, reconciled);
953                event_buffer.push(Event {
954                    origin: Origin::DirectAccess(DirectAccessEntity::UseCase(EntityEvent::Updated)),
955                    ids: vec![left],
956                    data: None,
957                });
958            }
959        }
960        Ok(())
961    }
962
963    /// Surgically reconcile one external referrer junction (out-of-scope key -> in-scope values):
964    /// `workspace.entities`.
965    fn reconcile_backref_workspace_entities(
966        &self,
967        event_buffer: &mut EventBuffer,
968        snap: &HashMapStoreSnapshot,
969        scope_ids: &[EntityId],
970    ) -> Result<(), RepositoryError> {
971        let scope: std::collections::HashSet<EntityId> = scope_ids.iter().copied().collect();
972        if scope.is_empty() {
973            return Ok(());
974        }
975        let store = self.transaction.get_store();
976
977        // External left keys whose ordered list references any scope id, in snapshot or live.
978        let mut left_keys: std::collections::HashSet<EntityId> = std::collections::HashSet::new();
979        for (left, rights) in snap.jn_entity_from_workspace_entities.iter() {
980            if rights.iter().any(|rid| scope.contains(rid)) {
981                left_keys.insert(*left);
982            }
983        }
984        {
985            let live_jn = read_or_recover(&store.jn_entity_from_workspace_entities);
986            for (left, rights) in live_jn.iter() {
987                if rights.iter().any(|rid| scope.contains(rid)) {
988                    left_keys.insert(*left);
989                }
990            }
991        }
992
993        for left in left_keys {
994            let snap_list: Vec<EntityId> = snap
995                .jn_entity_from_workspace_entities
996                .get(&left)
997                .cloned()
998                .unwrap_or_default();
999            let new_list = {
1000                let live_jn = read_or_recover(&store.jn_entity_from_workspace_entities);
1001                let live_list: Vec<EntityId> = live_jn.get(&left).cloned().unwrap_or_default();
1002                let reconciled = crate::database::hashmap_store::reconcile_backref_list(
1003                    &live_list, &snap_list, &scope,
1004                );
1005                if reconciled == live_list {
1006                    None
1007                } else {
1008                    Some(reconciled)
1009                }
1010            };
1011            if let Some(reconciled) = new_list {
1012                write_or_recover(&store.jn_entity_from_workspace_entities).insert(left, reconciled);
1013                event_buffer.push(Event {
1014                    origin: Origin::DirectAccess(DirectAccessEntity::Workspace(
1015                        EntityEvent::Updated,
1016                    )),
1017                    ids: vec![left],
1018                    data: None,
1019                });
1020            }
1021        }
1022        Ok(())
1023    }
1024
1025    /// Surgically reconcile one external referrer junction (out-of-scope key -> in-scope values):
1026    /// `field.entity`.
1027    fn reconcile_backref_field_entity(
1028        &self,
1029        event_buffer: &mut EventBuffer,
1030        snap: &HashMapStoreSnapshot,
1031        scope_ids: &[EntityId],
1032    ) -> Result<(), RepositoryError> {
1033        let scope: std::collections::HashSet<EntityId> = scope_ids.iter().copied().collect();
1034        if scope.is_empty() {
1035            return Ok(());
1036        }
1037        let store = self.transaction.get_store();
1038
1039        // External left keys whose ordered list references any scope id, in snapshot or live.
1040        let mut left_keys: std::collections::HashSet<EntityId> = std::collections::HashSet::new();
1041        for (left, rights) in snap.jn_entity_from_field_entity.iter() {
1042            if rights.iter().any(|rid| scope.contains(rid)) {
1043                left_keys.insert(*left);
1044            }
1045        }
1046        {
1047            let live_jn = read_or_recover(&store.jn_entity_from_field_entity);
1048            for (left, rights) in live_jn.iter() {
1049                if rights.iter().any(|rid| scope.contains(rid)) {
1050                    left_keys.insert(*left);
1051                }
1052            }
1053        }
1054
1055        for left in left_keys {
1056            let snap_list: Vec<EntityId> = snap
1057                .jn_entity_from_field_entity
1058                .get(&left)
1059                .cloned()
1060                .unwrap_or_default();
1061            let new_list = {
1062                let live_jn = read_or_recover(&store.jn_entity_from_field_entity);
1063                let live_list: Vec<EntityId> = live_jn.get(&left).cloned().unwrap_or_default();
1064                let reconciled = crate::database::hashmap_store::reconcile_backref_list(
1065                    &live_list, &snap_list, &scope,
1066                );
1067                if reconciled == live_list {
1068                    None
1069                } else {
1070                    Some(reconciled)
1071                }
1072            };
1073            if let Some(reconciled) = new_list {
1074                write_or_recover(&store.jn_entity_from_field_entity).insert(left, reconciled);
1075                event_buffer.push(Event {
1076                    origin: Origin::DirectAccess(DirectAccessEntity::Field(EntityEvent::Updated)),
1077                    ids: vec![left],
1078                    data: None,
1079                });
1080            }
1081        }
1082        Ok(())
1083    }
1084
1085    /// Surgically reconcile one external referrer junction (out-of-scope key -> in-scope values):
1086    /// `file.entity`.
1087    fn reconcile_backref_file_entity(
1088        &self,
1089        event_buffer: &mut EventBuffer,
1090        snap: &HashMapStoreSnapshot,
1091        scope_ids: &[EntityId],
1092    ) -> Result<(), RepositoryError> {
1093        let scope: std::collections::HashSet<EntityId> = scope_ids.iter().copied().collect();
1094        if scope.is_empty() {
1095            return Ok(());
1096        }
1097        let store = self.transaction.get_store();
1098
1099        // External left keys whose ordered list references any scope id, in snapshot or live.
1100        let mut left_keys: std::collections::HashSet<EntityId> = std::collections::HashSet::new();
1101        for (left, rights) in snap.jn_entity_from_file_entity.iter() {
1102            if rights.iter().any(|rid| scope.contains(rid)) {
1103                left_keys.insert(*left);
1104            }
1105        }
1106        {
1107            let live_jn = read_or_recover(&store.jn_entity_from_file_entity);
1108            for (left, rights) in live_jn.iter() {
1109                if rights.iter().any(|rid| scope.contains(rid)) {
1110                    left_keys.insert(*left);
1111                }
1112            }
1113        }
1114
1115        for left in left_keys {
1116            let snap_list: Vec<EntityId> = snap
1117                .jn_entity_from_file_entity
1118                .get(&left)
1119                .cloned()
1120                .unwrap_or_default();
1121            let new_list = {
1122                let live_jn = read_or_recover(&store.jn_entity_from_file_entity);
1123                let live_list: Vec<EntityId> = live_jn.get(&left).cloned().unwrap_or_default();
1124                let reconciled = crate::database::hashmap_store::reconcile_backref_list(
1125                    &live_list, &snap_list, &scope,
1126                );
1127                if reconciled == live_list {
1128                    None
1129                } else {
1130                    Some(reconciled)
1131                }
1132            };
1133            if let Some(reconciled) = new_list {
1134                write_or_recover(&store.jn_entity_from_file_entity).insert(left, reconciled);
1135                event_buffer.push(Event {
1136                    origin: Origin::DirectAccess(DirectAccessEntity::File(EntityEvent::Updated)),
1137                    ids: vec![left],
1138                    data: None,
1139                });
1140            }
1141        }
1142        Ok(())
1143    }
1144
1145    /// Surgically reconcile one external referrer junction (out-of-scope key -> in-scope values):
1146    /// `entity.inherits_from`.
1147    fn reconcile_backref_entity_inherits_from(
1148        &self,
1149        event_buffer: &mut EventBuffer,
1150        snap: &HashMapStoreSnapshot,
1151        scope_ids: &[EntityId],
1152    ) -> Result<(), RepositoryError> {
1153        let scope: std::collections::HashSet<EntityId> = scope_ids.iter().copied().collect();
1154        if scope.is_empty() {
1155            return Ok(());
1156        }
1157        let store = self.transaction.get_store();
1158
1159        // External left keys whose ordered list references any scope id, in snapshot or live.
1160        let mut left_keys: std::collections::HashSet<EntityId> = std::collections::HashSet::new();
1161        for (left, rights) in snap.jn_entity_from_entity_inherits_from.iter() {
1162            if rights.iter().any(|rid| scope.contains(rid)) {
1163                left_keys.insert(*left);
1164            }
1165        }
1166        {
1167            let live_jn = read_or_recover(&store.jn_entity_from_entity_inherits_from);
1168            for (left, rights) in live_jn.iter() {
1169                if rights.iter().any(|rid| scope.contains(rid)) {
1170                    left_keys.insert(*left);
1171                }
1172            }
1173        }
1174
1175        for left in left_keys {
1176            let snap_list: Vec<EntityId> = snap
1177                .jn_entity_from_entity_inherits_from
1178                .get(&left)
1179                .cloned()
1180                .unwrap_or_default();
1181            let new_list = {
1182                let live_jn = read_or_recover(&store.jn_entity_from_entity_inherits_from);
1183                let live_list: Vec<EntityId> = live_jn.get(&left).cloned().unwrap_or_default();
1184                let reconciled = crate::database::hashmap_store::reconcile_backref_list(
1185                    &live_list, &snap_list, &scope,
1186                );
1187                if reconciled == live_list {
1188                    None
1189                } else {
1190                    Some(reconciled)
1191                }
1192            };
1193            if let Some(reconciled) = new_list {
1194                write_or_recover(&store.jn_entity_from_entity_inherits_from)
1195                    .insert(left, reconciled);
1196                event_buffer.push(Event {
1197                    origin: Origin::DirectAccess(DirectAccessEntity::Entity(EntityEvent::Updated)),
1198                    ids: vec![left],
1199                    data: None,
1200                });
1201            }
1202        }
1203        Ok(())
1204    }
1205
1206    /// Surgically reconcile one external referrer junction (out-of-scope key -> in-scope values):
1207    /// `relationship.left_entity`.
1208    fn reconcile_backref_relationship_left_entity(
1209        &self,
1210        event_buffer: &mut EventBuffer,
1211        snap: &HashMapStoreSnapshot,
1212        scope_ids: &[EntityId],
1213    ) -> Result<(), RepositoryError> {
1214        let scope: std::collections::HashSet<EntityId> = scope_ids.iter().copied().collect();
1215        if scope.is_empty() {
1216            return Ok(());
1217        }
1218        let store = self.transaction.get_store();
1219
1220        // External left keys whose ordered list references any scope id, in snapshot or live.
1221        let mut left_keys: std::collections::HashSet<EntityId> = std::collections::HashSet::new();
1222        for (left, rights) in snap.jn_entity_from_relationship_left_entity.iter() {
1223            if rights.iter().any(|rid| scope.contains(rid)) {
1224                left_keys.insert(*left);
1225            }
1226        }
1227        {
1228            let live_jn = read_or_recover(&store.jn_entity_from_relationship_left_entity);
1229            for (left, rights) in live_jn.iter() {
1230                if rights.iter().any(|rid| scope.contains(rid)) {
1231                    left_keys.insert(*left);
1232                }
1233            }
1234        }
1235
1236        for left in left_keys {
1237            let snap_list: Vec<EntityId> = snap
1238                .jn_entity_from_relationship_left_entity
1239                .get(&left)
1240                .cloned()
1241                .unwrap_or_default();
1242            let new_list = {
1243                let live_jn = read_or_recover(&store.jn_entity_from_relationship_left_entity);
1244                let live_list: Vec<EntityId> = live_jn.get(&left).cloned().unwrap_or_default();
1245                let reconciled = crate::database::hashmap_store::reconcile_backref_list(
1246                    &live_list, &snap_list, &scope,
1247                );
1248                if reconciled == live_list {
1249                    None
1250                } else {
1251                    Some(reconciled)
1252                }
1253            };
1254            if let Some(reconciled) = new_list {
1255                write_or_recover(&store.jn_entity_from_relationship_left_entity)
1256                    .insert(left, reconciled);
1257                event_buffer.push(Event {
1258                    origin: Origin::DirectAccess(DirectAccessEntity::Relationship(
1259                        EntityEvent::Updated,
1260                    )),
1261                    ids: vec![left],
1262                    data: None,
1263                });
1264            }
1265        }
1266        Ok(())
1267    }
1268
1269    /// Surgically reconcile one external referrer junction (out-of-scope key -> in-scope values):
1270    /// `relationship.right_entity`.
1271    fn reconcile_backref_relationship_right_entity(
1272        &self,
1273        event_buffer: &mut EventBuffer,
1274        snap: &HashMapStoreSnapshot,
1275        scope_ids: &[EntityId],
1276    ) -> Result<(), RepositoryError> {
1277        let scope: std::collections::HashSet<EntityId> = scope_ids.iter().copied().collect();
1278        if scope.is_empty() {
1279            return Ok(());
1280        }
1281        let store = self.transaction.get_store();
1282
1283        // External left keys whose ordered list references any scope id, in snapshot or live.
1284        let mut left_keys: std::collections::HashSet<EntityId> = std::collections::HashSet::new();
1285        for (left, rights) in snap.jn_entity_from_relationship_right_entity.iter() {
1286            if rights.iter().any(|rid| scope.contains(rid)) {
1287                left_keys.insert(*left);
1288            }
1289        }
1290        {
1291            let live_jn = read_or_recover(&store.jn_entity_from_relationship_right_entity);
1292            for (left, rights) in live_jn.iter() {
1293                if rights.iter().any(|rid| scope.contains(rid)) {
1294                    left_keys.insert(*left);
1295                }
1296            }
1297        }
1298
1299        for left in left_keys {
1300            let snap_list: Vec<EntityId> = snap
1301                .jn_entity_from_relationship_right_entity
1302                .get(&left)
1303                .cloned()
1304                .unwrap_or_default();
1305            let new_list = {
1306                let live_jn = read_or_recover(&store.jn_entity_from_relationship_right_entity);
1307                let live_list: Vec<EntityId> = live_jn.get(&left).cloned().unwrap_or_default();
1308                let reconciled = crate::database::hashmap_store::reconcile_backref_list(
1309                    &live_list, &snap_list, &scope,
1310                );
1311                if reconciled == live_list {
1312                    None
1313                } else {
1314                    Some(reconciled)
1315                }
1316            };
1317            if let Some(reconciled) = new_list {
1318                write_or_recover(&store.jn_entity_from_relationship_right_entity)
1319                    .insert(left, reconciled);
1320                event_buffer.push(Event {
1321                    origin: Origin::DirectAccess(DirectAccessEntity::Relationship(
1322                        EntityEvent::Updated,
1323                    )),
1324                    ids: vec![left],
1325                    data: None,
1326                });
1327            }
1328        }
1329        Ok(())
1330    }
1331}
1332
1333pub struct EntityRepositoryRO<'a> {
1334    table: Box<dyn EntityTableRO + 'a>,
1335}
1336impl<'a> EntityRepositoryRO<'a> {
1337    pub fn new(table: Box<dyn EntityTableRO + 'a>) -> Self {
1338        EntityRepositoryRO { table }
1339    }
1340    pub fn get(&self, id: &EntityId) -> Result<Option<Entity>, RepositoryError> {
1341        self.table.get(id)
1342    }
1343    pub fn get_multi(&self, ids: &[EntityId]) -> Result<Vec<Option<Entity>>, RepositoryError> {
1344        self.table.get_multi(ids)
1345    }
1346    pub fn get_all(&self) -> Result<Vec<Entity>, RepositoryError> {
1347        self.table.get_all()
1348    }
1349    pub fn get_relationship(
1350        &self,
1351        id: &EntityId,
1352        field: &EntityRelationshipField,
1353    ) -> Result<Vec<EntityId>, RepositoryError> {
1354        self.table.get_relationship(id, field)
1355    }
1356    pub fn get_relationship_many(
1357        &self,
1358        ids: &[EntityId],
1359        field: &EntityRelationshipField,
1360    ) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>, RepositoryError> {
1361        self.table.get_relationship_many(ids, field)
1362    }
1363    pub fn get_relationship_count(
1364        &self,
1365        id: &EntityId,
1366        field: &EntityRelationshipField,
1367    ) -> Result<usize, RepositoryError> {
1368        self.table.get_relationship_count(id, field)
1369    }
1370    pub fn get_relationship_in_range(
1371        &self,
1372        id: &EntityId,
1373        field: &EntityRelationshipField,
1374        offset: usize,
1375        limit: usize,
1376    ) -> Result<Vec<EntityId>, RepositoryError> {
1377        self.table
1378            .get_relationship_in_range(id, field, offset, limit)
1379    }
1380    pub fn get_relationships_from_right_ids(
1381        &self,
1382        field: &EntityRelationshipField,
1383        right_ids: &[EntityId],
1384    ) -> Result<Vec<(EntityId, Vec<EntityId>)>, RepositoryError> {
1385        self.table
1386            .get_relationships_from_right_ids(field, right_ids)
1387    }
1388}