cypherlite_storage/version/
mod.rs1use cypherlite_core::{NodeRecord, RelationshipRecord};
8use std::collections::BTreeMap;
9
10#[derive(Debug, Clone, PartialEq)]
12pub enum VersionRecord {
13 Node(NodeRecord),
15 Relationship(RelationshipRecord),
17}
18
19pub struct VersionStore {
24 versions: BTreeMap<(u64, u64), VersionRecord>,
26 next_seq: BTreeMap<u64, u64>,
28}
29
30impl VersionStore {
31 pub fn new() -> Self {
33 Self {
34 versions: BTreeMap::new(),
35 next_seq: BTreeMap::new(),
36 }
37 }
38
39 pub fn snapshot_node(&mut self, entity_id: u64, record: NodeRecord) -> u64 {
42 let seq = self.next_seq.entry(entity_id).or_insert(1);
43 let current_seq = *seq;
44 self.versions
45 .insert((entity_id, current_seq), VersionRecord::Node(record));
46 *seq += 1;
47 current_seq
48 }
49
50 pub fn snapshot_relationship(&mut self, entity_id: u64, record: RelationshipRecord) -> u64 {
53 let seq = self.next_seq.entry(entity_id).or_insert(1);
54 let current_seq = *seq;
55 self.versions.insert(
56 (entity_id, current_seq),
57 VersionRecord::Relationship(record),
58 );
59 *seq += 1;
60 current_seq
61 }
62
63 pub fn get_version(&self, entity_id: u64, version_seq: u64) -> Option<&VersionRecord> {
65 self.versions.get(&(entity_id, version_seq))
66 }
67
68 pub fn get_latest_version(&self, entity_id: u64) -> Option<&VersionRecord> {
70 let current_seq = self.next_seq.get(&entity_id)?;
71 if *current_seq <= 1 {
72 return None;
73 }
74 self.versions.get(&(entity_id, *current_seq - 1))
75 }
76
77 pub fn get_version_chain(&self, entity_id: u64) -> Vec<(u64, &VersionRecord)> {
79 self.versions
80 .range((entity_id, 0)..=(entity_id, u64::MAX))
81 .map(|((_, seq), record)| (*seq, record))
82 .collect()
83 }
84
85 pub fn version_count(&self, entity_id: u64) -> u64 {
87 self.next_seq.get(&entity_id).copied().unwrap_or(1) - 1
88 }
89
90 pub fn total_versions(&self) -> usize {
92 self.versions.len()
93 }
94
95 pub fn insert_loaded_record(
99 &mut self,
100 entity_id: u64,
101 version_seq: u64,
102 record: VersionRecord,
103 ) {
104 self.versions.insert((entity_id, version_seq), record);
105 let seq = self.next_seq.entry(entity_id).or_insert(1);
106 if version_seq >= *seq {
107 *seq = version_seq + 1;
108 }
109 }
110
111 pub fn all(&self) -> impl Iterator<Item = (&(u64, u64), &VersionRecord)> {
113 self.versions.iter()
114 }
115}
116
117impl Default for VersionStore {
118 fn default() -> Self {
119 Self::new()
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use cypherlite_core::{Direction, EdgeId, NodeId, PropertyValue};
127
128 fn sample_node(id: u64, name: &str) -> NodeRecord {
129 NodeRecord {
130 node_id: NodeId(id),
131 labels: vec![1],
132 properties: vec![(1, PropertyValue::String(name.to_string()))],
133 next_edge_id: None,
134 overflow_page: None,
135 }
136 }
137
138 fn sample_edge(id: u64) -> RelationshipRecord {
139 RelationshipRecord {
140 edge_id: EdgeId(id),
141 start_node: NodeId(1),
142 end_node: NodeId(2),
143 rel_type_id: 1,
144 direction: Direction::Outgoing,
145 next_out_edge: None,
146 next_in_edge: None,
147 properties: vec![(1, PropertyValue::String("v1".to_string()))],
148 #[cfg(feature = "subgraph")]
149 start_is_subgraph: false,
150 #[cfg(feature = "subgraph")]
151 end_is_subgraph: false,
152 }
153 }
154
155 #[test]
157 fn test_version_store_new_is_empty() {
158 let store = VersionStore::new();
159 assert_eq!(store.total_versions(), 0);
160 }
161
162 #[test]
164 fn test_snapshot_node() {
165 let mut store = VersionStore::new();
166 let node = sample_node(1, "Alice");
167 let seq = store.snapshot_node(1, node.clone());
168 assert_eq!(seq, 1);
169 assert_eq!(store.total_versions(), 1);
170
171 let version = store.get_version(1, 1).expect("version exists");
172 assert_eq!(*version, VersionRecord::Node(node));
173 }
174
175 #[test]
177 fn test_snapshot_relationship() {
178 let mut store = VersionStore::new();
179 let edge = sample_edge(1);
180 let seq = store.snapshot_relationship(1, edge.clone());
181 assert_eq!(seq, 1);
182
183 let version = store.get_version(1, 1).expect("version exists");
184 assert_eq!(*version, VersionRecord::Relationship(edge));
185 }
186
187 #[test]
189 fn test_multiple_snapshots_incrementing_seq() {
190 let mut store = VersionStore::new();
191
192 let seq1 = store.snapshot_node(1, sample_node(1, "v1"));
193 let seq2 = store.snapshot_node(1, sample_node(1, "v2"));
194 let seq3 = store.snapshot_node(1, sample_node(1, "v3"));
195
196 assert_eq!(seq1, 1);
197 assert_eq!(seq2, 2);
198 assert_eq!(seq3, 3);
199 assert_eq!(store.version_count(1), 3);
200 }
201
202 #[test]
204 fn test_version_chain() {
205 let mut store = VersionStore::new();
206
207 store.snapshot_node(1, sample_node(1, "v1"));
208 store.snapshot_node(1, sample_node(1, "v2"));
209 store.snapshot_node(1, sample_node(1, "v3"));
210
211 let chain = store.get_version_chain(1);
212 assert_eq!(chain.len(), 3);
213 assert_eq!(chain[0].0, 1); assert_eq!(chain[2].0, 3); }
216
217 #[test]
219 fn test_get_latest_version() {
220 let mut store = VersionStore::new();
221
222 store.snapshot_node(1, sample_node(1, "v1"));
223 store.snapshot_node(1, sample_node(1, "v2"));
224
225 let latest = store.get_latest_version(1).expect("latest exists");
226 match latest {
227 VersionRecord::Node(n) => {
228 assert_eq!(n.properties[0].1, PropertyValue::String("v2".to_string()));
229 }
230 _ => panic!("expected node version"),
231 }
232 }
233
234 #[test]
236 fn test_get_latest_version_nonexistent() {
237 let store = VersionStore::new();
238 assert!(store.get_latest_version(999).is_none());
239 }
240
241 #[test]
243 fn test_version_chain_empty() {
244 let store = VersionStore::new();
245 let chain = store.get_version_chain(999);
246 assert!(chain.is_empty());
247 }
248
249 #[test]
251 fn test_independent_sequences_per_entity() {
252 let mut store = VersionStore::new();
253
254 let s1 = store.snapshot_node(1, sample_node(1, "A"));
255 let s2 = store.snapshot_node(2, sample_node(2, "B"));
256 let s3 = store.snapshot_node(1, sample_node(1, "A2"));
257
258 assert_eq!(s1, 1);
259 assert_eq!(s2, 1); assert_eq!(s3, 2); assert_eq!(store.version_count(1), 2);
263 assert_eq!(store.version_count(2), 1);
264 }
265
266 #[test]
268 fn test_version_store_default() {
269 let store = VersionStore::default();
270 assert_eq!(store.total_versions(), 0);
271 }
272
273 #[test]
275 fn test_version_count_unseen_entity() {
276 let store = VersionStore::new();
277 assert_eq!(store.version_count(999), 0);
278 }
279}