Skip to main content

nitrite/repository/
cursor.rs

1use crate::collection::{Document, FindPlan, NitriteId};
2use crate::common::{
3    Convertible, DocumentCursor, JoinedDocumentCursor, Lookup, ProjectedDocumentCursor, Value,
4};
5use crate::errors::{ErrorKind, NitriteError, NitriteResult};
6use crate::repository::NitriteEntity;
7use std::marker::PhantomData;
8
9pub struct ObjectCursor<T> {
10    cursor: DocumentCursor,
11    _phantom: PhantomData<T>,
12}
13
14impl<T> ObjectCursor<T>
15where
16    T: Convertible<Output = T> + NitriteEntity,
17{
18    pub fn new(cursor: DocumentCursor) -> Self {
19        ObjectCursor {
20            cursor,
21            _phantom: PhantomData,
22        }
23    }
24
25    pub fn reset(&mut self) {
26        self.cursor.reset();
27    }
28    
29    pub fn size(&mut self) -> usize {
30        // Reset the underlying cursor to ensure we count from the beginning.
31        self.reset();
32        let count = self.cursor.size();
33        // Reset again for replayability.
34        self.reset();
35        count
36    }
37
38    pub fn first(&mut self) -> Option<NitriteResult<T>> {
39        let doc_result = self.cursor.first();
40        match doc_result {
41            Some(Ok(doc)) => {
42                let result = T::from_value(&Value::Document(doc));
43                match result {
44                    Ok(obj) => Some(Ok(obj)),
45                    Err(e) => Some(Err(e)),
46                }
47            }
48            Some(Err(e)) => Some(Err(e)),
49            None => None,
50        }
51    }
52
53    pub fn find_plan(&self) -> Option<&FindPlan> {
54        self.cursor.find_plan()
55    }
56
57    pub(crate) fn set_find_plan(mut self, find_plan: FindPlan) -> Self {
58        self.cursor = self.cursor.set_find_plan(find_plan);
59        self
60    }
61
62    pub fn join<'a, J: Convertible<Output = J> + NitriteEntity>(
63        &'a mut self,
64        foreign_cursor: &'a mut ObjectCursor<J>,
65        lookup: &'a Lookup,
66    ) -> NitriteResult<JoinedObjectCursor<'a, J>> {
67        let joined_doc_cursor = self.cursor.join(&mut foreign_cursor.cursor, lookup)?;
68        Ok(JoinedObjectCursor::new(joined_doc_cursor))
69    }
70
71    pub fn project<P>(&'_ mut self) -> NitriteResult<ProjectedObjectCursor<'_, P>>
72    where
73        P: Convertible<Output = P> + NitriteEntity + Default,
74    {
75        ProjectedObjectCursor::new(&mut self.cursor)
76    }
77    
78    /// Returns an iterator that yields `(NitriteId, T)` pairs.
79    /// This is useful when you need to update entities after retrieving them,
80    /// as it provides the NitriteId needed for efficient O(1) updates via
81    /// `update_by_nitrite_id`.
82    pub fn iter_with_id(&mut self) -> ObjectCursorWithId<'_, T> {
83        ObjectCursorWithId {
84            cursor: &mut self.cursor,
85            _phantom: PhantomData,
86        }
87    }
88}
89
90/// An iterator adapter that yields `(NitriteId, T)` pairs from an ObjectCursor.
91/// This enables efficient updates after retrieval by providing the NitriteId.
92pub struct ObjectCursorWithId<'a, T> {
93    cursor: &'a mut DocumentCursor,
94    _phantom: PhantomData<T>,
95}
96
97impl<'a, T> Iterator for ObjectCursorWithId<'a, T>
98where
99    T: Convertible<Output = T> + NitriteEntity,
100{
101    type Item = NitriteResult<(NitriteId, T)>;
102
103    fn next(&mut self) -> Option<Self::Item> {
104        let doc_result = self.cursor.next();
105        match doc_result {
106            Some(Ok(mut doc)) => {
107                // Get the NitriteId from the document
108                let id = match doc.id() {
109                    Ok(id) => id,
110                    Err(e) => return Some(Err(e)),
111                };
112                
113                // Convert document to entity
114                let result = T::from_value(&Value::Document(doc));
115                match result {
116                    Ok(obj) => Some(Ok((id, obj))),
117                    Err(e) => Some(Err(e)),
118                }
119            }
120            Some(Err(e)) => Some(Err(e)),
121            None => None,
122        }
123    }
124}
125
126impl<T> Iterator for ObjectCursor<T>
127where
128    T: Convertible<Output = T> + NitriteEntity,
129{
130    type Item = NitriteResult<T>;
131
132    fn next(&mut self) -> Option<Self::Item> {
133        let doc_result = self.cursor.next();
134        match doc_result {
135            Some(Ok(doc)) => {
136                let result = T::from_value(&Value::Document(doc));
137                match result {
138                    Ok(obj) => Some(Ok(obj)),
139                    Err(e) => Some(Err(e)),
140                }
141            }
142            Some(Err(e)) => Some(Err(e)),
143            None => None,
144        }
145    }
146}
147
148pub struct JoinedObjectCursor<'a, T>
149where
150    T: Convertible<Output = T> + NitriteEntity,
151{
152    cursor: JoinedDocumentCursor<'a>,
153    _phantom: PhantomData<T>,
154}
155
156impl<'a, T> JoinedObjectCursor<'a, T>
157where
158    T: Convertible<Output = T> + NitriteEntity,
159{
160    pub fn new(cursor: JoinedDocumentCursor<'a>) -> Self {
161        JoinedObjectCursor {
162            cursor,
163            _phantom: PhantomData,
164        }
165    }
166
167    pub fn size(&mut self) -> usize {
168        let mut count = 0;
169        // Consume all joined documents.
170        while self.next().is_some() {
171            count += 1;
172        }
173        // Reset the underlying cursor for replayability.
174        self.cursor.reset();
175        count
176    }
177}
178
179impl<'a, T> Iterator for JoinedObjectCursor<'a, T>
180where
181    T: Convertible<Output = T> + NitriteEntity,
182{
183    type Item = NitriteResult<T>;
184
185    fn next(&mut self) -> Option<Self::Item> {
186        let doc_result = self.cursor.next();
187        match doc_result {
188            Some(Ok(doc)) => {
189                let result = T::from_value(&Value::Document(doc));
190                match result {
191                    Ok(obj) => Some(Ok(obj)),
192                    Err(e) => Some(Err(e)),
193                }
194            }
195            Some(Err(e)) => Some(Err(e)),
196            None => None,
197        }
198    }
199}
200
201pub struct ProjectedObjectCursor<'a, P>
202where
203    P: Convertible<Output = P> + NitriteEntity + Default,
204{
205    cursor: ProjectedDocumentCursor<'a>,
206    _phantom: PhantomData<P>,
207}
208
209impl<'a, P> ProjectedObjectCursor<'a, P>
210where
211    P: Convertible<Output = P> + NitriteEntity + Default,
212{
213    pub fn new(cursor: &'a mut DocumentCursor) -> NitriteResult<Self> {
214        let projection = P::default().to_value()?;
215        let projection = projection.as_document().cloned().ok_or_else(|| {
216            log::error!("Projection type is not convertible to document");
217            NitriteError::new(
218                "Projection type is not convertible to document",
219                ErrorKind::ObjectMappingError,
220            )
221        })?;
222
223
224        Ok(ProjectedObjectCursor {
225            cursor: cursor.project(projection)?,
226            _phantom: PhantomData,
227        })
228    }
229
230    pub fn size(&mut self) -> usize {
231        let count = self.cursor.size();
232        // Reset the underlying cursor for replayability.
233        self.cursor.reset();
234        count
235    }
236}
237
238impl<'a, P> Iterator for ProjectedObjectCursor<'a, P>
239where
240    P: Convertible<Output = P> + NitriteEntity + Default,
241{
242    type Item = NitriteResult<P>;
243
244    fn next(&mut self) -> Option<Self::Item> {
245        let doc_result = self.cursor.next();
246        match doc_result {
247            Some(Ok(doc)) => {
248                let result = P::from_value(&Value::Document(doc));
249                match result {
250                    Ok(obj) => Some(Ok(obj)),
251                    Err(e) => Some(Err(e)),
252                }
253            }
254            Some(Err(e)) => Some(Err(e)),
255            None => None,
256        }
257    }
258}
259
260#[cfg(test)]
261mod tests {
262    use super::*;
263    use crate::collection::Document;
264    use crate::common::{ProcessorChain, NON_UNIQUE_INDEX};
265    use crate::doc;
266    use crate::errors::{ErrorKind, NitriteError};
267    use crate::repository::{EntityId, EntityIndex};
268
269    struct TestEntity {
270        first: String,
271        last: String,
272    }
273
274    impl Convertible for TestEntity {
275        type Output = TestEntity;
276
277        fn to_value(&self) -> NitriteResult<Value> {
278            let doc = doc!{
279                "first": (self.first.to_string()),
280                "last": (self.last.to_string()),
281            };
282            Ok(Value::Document(doc))
283        }
284
285        fn from_value(value: &Value) -> NitriteResult<Self::Output> {
286            match value {
287                Value::Document(doc) => {
288                    let first = doc.get("first")?;
289                    let first = match first.as_string() {
290                        Some(s) => s.to_string(),
291                        None => {
292                            log::error!("TestEntity field 'first' should be string, got: {:?}", first);
293                            return Err(NitriteError::new(
294                                "TestEntity field 'first' must be a string",
295                                ErrorKind::ObjectMappingError,
296                            ));
297                        }
298                    };
299                    
300                    let last = doc.get("last")?;
301                    let last = match last.as_string() {
302                        Some(s) => s.to_string(),
303                        None => {
304                            log::error!("TestEntity field 'last' should be string, got: {:?}", last);
305                            return Err(NitriteError::new(
306                                "TestEntity field 'last' must be a string",
307                                ErrorKind::ObjectMappingError,
308                            ));
309                        }
310                    };
311                    
312                    Ok(TestEntity { first, last })
313                }
314                _ => {
315                    log::error!("Expected Document for TestEntity, got: {:?}", value);
316                    Err(NitriteError::new(
317                        "Object cursor deserialization error: expected document but found another value type for TestEntity",
318                        ErrorKind::ObjectMappingError
319                    ))
320                }
321            }
322        }
323    }
324    
325    impl NitriteEntity for TestEntity {
326        type Id = ();
327
328        fn entity_name(&self) -> String {
329            "TestEntity".to_string()
330        }
331
332        fn entity_indexes(&self) -> Option<Vec<EntityIndex>> {
333            Some(vec![
334                EntityIndex::new(vec!["first", "last"], Some(NON_UNIQUE_INDEX)),
335            ])
336        }
337
338        fn entity_id(&self) -> Option<EntityId> {
339            Some(EntityId::new("first", None, None))
340        }
341    }
342    
343    impl Default for TestEntity {
344        fn default() -> Self {
345            TestEntity {
346                first: "".to_string(),
347                last: "".to_string(),
348            }
349        }
350    }
351
352    fn create_document(first: &str, last: &str) -> Document {
353        let doc = doc!{
354            "first": first,
355            "last": last,
356        };
357        doc
358    }
359
360    #[test]
361    fn test_new_object_cursor() {
362        let docs = vec![
363            Ok(create_document("John", "Doe")),
364            Ok(create_document("Jane", "Doe")),
365        ];
366        let iter = Box::new(docs.into_iter());
367        let cursor = DocumentCursor::new(iter, ProcessorChain::new());
368        let object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
369        assert_eq!(object_cursor.cursor.count(), 2);
370    }
371
372    #[test]
373    fn test_object_cursor_first() {
374        let docs = vec![
375            Ok(create_document("John", "Doe")),
376            Ok(create_document("Jane", "Doe")),
377        ];
378        let iter = Box::new(docs.into_iter());
379        let cursor = DocumentCursor::new(iter, ProcessorChain::new());
380        let mut object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
381        let first = object_cursor.first().unwrap().unwrap();
382        assert_eq!(first.first, "John");
383    }
384
385    #[test]
386    fn test_object_cursor_first_with_error() {
387        let docs = vec![
388            Err(NitriteError::new("Test Error", ErrorKind::IOError)),
389        ];
390        let iter = Box::new(docs.into_iter());
391        let cursor = DocumentCursor::new(iter, ProcessorChain::new());
392        let mut object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
393        let object_cursor = &mut object_cursor;
394        assert!(object_cursor.next().unwrap().is_err());
395    }
396
397    #[test]
398    fn test_object_cursor_next() {
399        let docs = vec![
400            Ok(create_document("John", "Doe")),
401            Ok(create_document("Jane", "Doe")),
402        ];
403        let iter = Box::new(docs.into_iter());
404        let cursor = DocumentCursor::new(iter, ProcessorChain::new());
405        let mut object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
406        let iter = &mut object_cursor;
407        let first = iter.next().unwrap().unwrap();
408        assert_eq!(first.first, "John");
409        let second = iter.next().unwrap().unwrap();
410        assert_eq!(second.first, "Jane");
411        assert!(iter.next().is_none());
412    }
413
414    #[test]
415    fn test_object_cursor_next_with_error() {
416        let docs = vec![
417            Ok(create_document("John", "Doe")),
418            Err(NitriteError::new("Test Error", ErrorKind::IOError)),
419        ];
420        let iter = Box::new(docs.into_iter());
421        let cursor = DocumentCursor::new(iter, ProcessorChain::new());
422        let mut object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
423        let iter = &mut object_cursor;
424        let first = iter.next().unwrap().unwrap();
425        assert_eq!(first.first, "John");
426        assert!(iter.next().unwrap().is_err());
427    }
428
429    #[test]
430    fn test_object_cursor_find_plan() {
431        let docs = vec![Ok(create_document("John", "Doe"))];
432        let iter = Box::new(docs.into_iter());
433        let cursor = DocumentCursor::new(iter, ProcessorChain::new());
434        let object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
435        assert!(object_cursor.find_plan().is_none());
436    }
437
438    #[test]
439    fn test_object_cursor_set_find_plan() {
440        let docs = vec![Ok(create_document("John", "Doe"))];
441        let iter = Box::new(docs.into_iter());
442        let find_plan = FindPlan::new(); // Assuming FindPlan has a default() method
443        let cursor = DocumentCursor::new(iter, ProcessorChain::new()).set_find_plan(find_plan.clone());
444        let object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor).set_find_plan(find_plan.clone());
445        assert!(object_cursor.find_plan().is_some());
446        assert_eq!(object_cursor.find_plan().unwrap().index_descriptor(), find_plan.index_descriptor());
447    }
448
449    #[test]
450    fn test_object_cursor_join() {
451        let docs1 = vec![Ok(create_document("John", "Doe"))];
452        let docs2 = vec![Ok(create_document("Jane", "Doe"))];
453        let iter1 = Box::new(docs1.into_iter());
454        let iter2 = Box::new(docs2.into_iter());
455        let cursor1 = DocumentCursor::new(iter1, ProcessorChain::new());
456        let cursor2 = DocumentCursor::new(iter2, ProcessorChain::new());
457        let mut object_cursor1: ObjectCursor<TestEntity> = ObjectCursor::new(cursor1);
458        let mut object_cursor2: ObjectCursor<TestEntity> = ObjectCursor::new(cursor2);
459        let lookup = Lookup {
460            local_field: "last".to_string(),
461            foreign_field: "last".to_string(),
462            target_field: "surname".to_string(),
463        };
464        let joined_cursor = object_cursor1.join(&mut object_cursor2, &lookup).expect("Failed to join");
465        assert_eq!(joined_cursor.count(), 1);
466    }
467
468    #[test]
469    fn test_object_cursor_project() {
470        let docs = vec![Ok(create_document("John", "Doe"))];
471        let iter = Box::new(docs.into_iter());
472        let cursor = DocumentCursor::new(iter, ProcessorChain::new());
473        let mut object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
474        let mut projected_cursor = object_cursor.project::<TestEntity>().expect("Failed to project");
475        assert_eq!(projected_cursor.size(), 1);
476    }
477
478    #[test]
479    fn test_test_entity_from_value_with_valid_document() {
480        // Test safe deserialization with valid document
481        let doc = create_document("John", "Doe");
482        let result = TestEntity::from_value(&Value::Document(doc));
483        assert!(result.is_ok());
484        
485        let entity = result.unwrap();
486        assert_eq!(entity.first, "John");
487        assert_eq!(entity.last, "Doe");
488    }
489
490    #[test]
491    fn test_test_entity_from_value_with_invalid_type() {
492        // Test that non-document value is rejected with error
493        let result = TestEntity::from_value(&Value::I32(42));
494        assert!(result.is_err());
495    }
496
497    #[test]
498    fn test_test_entity_from_value_with_wrong_first_type() {
499        // Test that wrong first field type is rejected with error
500        let mut doc = Document::new();
501        doc.put("first", Value::I32(123)).unwrap(); // Wrong type - should be string
502        doc.put("last", Value::String("Doe".to_string())).unwrap();
503        
504        let result = TestEntity::from_value(&Value::Document(doc));
505        assert!(result.is_err());
506        if let Err(e) = result {
507            assert_eq!(e.kind(), &ErrorKind::ObjectMappingError);
508        }
509    }
510
511    #[test]
512    fn test_test_entity_from_value_with_wrong_last_type() {
513        // Test that wrong last field type is rejected with error
514        let mut doc = Document::new();
515        doc.put("first", Value::String("John".to_string())).unwrap();
516        doc.put("last", Value::Bool(true)).unwrap(); // Wrong type - should be string
517        
518        let result = TestEntity::from_value(&Value::Document(doc));
519        assert!(result.is_err());
520        if let Err(e) = result {
521            assert_eq!(e.kind(), &ErrorKind::ObjectMappingError);
522        }
523    }
524
525    #[test]
526    fn test_object_cursor_iteration_with_type_validation() {
527        // Test that cursor iteration validates types and doesn't panic
528        let docs = vec![
529            Ok(create_document("John", "Doe")),
530            Ok(create_document("Jane", "Smith")),
531        ];
532        let iter = Box::new(docs.into_iter());
533        let cursor = DocumentCursor::new(iter, ProcessorChain::new());
534        let mut object_cursor: ObjectCursor<TestEntity> = ObjectCursor::new(cursor);
535        let iter = &mut object_cursor;
536        
537        let mut count = 0;
538        for result in iter {
539            assert!(result.is_ok());
540            let entity = result.unwrap();
541            assert!(!entity.first.is_empty());
542            assert!(!entity.last.is_empty());
543            count += 1;
544        }
545        assert_eq!(count, 2);
546    }
547}