use std::error::Error; use std::{fmt, marker}; use rocksdb::rocksdb::{DB, DBVector, Snapshot, SeekKey}; use rocksdb::rocksdb_options::ReadOptions; use serde::de::DeserializeOwned; use crate::database::{retrieve_data_schema, DocumentKey, DocumentKeyAttr}; use crate::database::deserializer::Deserializer; use crate::rank::criterion::Criterion; use crate::database::schema::Schema; use crate::rank::QueryBuilder; use crate::DocumentId; pub struct DatabaseView<'a> { snapshot: Snapshot<&'a DB>, schema: Schema, } impl<'a> DatabaseView<'a> { pub fn new(snapshot: Snapshot<&'a DB>) -> Result> { let schema = retrieve_data_schema(&snapshot)?; Ok(DatabaseView { snapshot, schema }) } pub fn schema(&self) -> &Schema { &self.schema } pub fn into_snapshot(self) -> Snapshot<&'a DB> { self.snapshot } pub fn snapshot(&self) -> &Snapshot<&'a DB> { &self.snapshot } pub fn get(&self, key: &[u8]) -> Result, Box> { Ok(self.snapshot.get(key)?) } pub fn query_builder(&self) -> Result>, Box> { QueryBuilder::new(self) } // TODO create an enum error type pub fn retrieve_document(&self, id: DocumentId) -> Result> where D: DeserializeOwned { let mut deserializer = Deserializer::new(&self.snapshot, &self.schema, id); Ok(D::deserialize(&mut deserializer)?) } pub fn retrieve_documents(&self, ids: I) -> DocumentIter where D: DeserializeOwned, I: IntoIterator, { DocumentIter { database_view: self, document_ids: ids.into_iter(), _phantom: marker::PhantomData, } } } impl<'a> fmt::Debug for DatabaseView<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut options = ReadOptions::new(); let lower = DocumentKey::new(0); options.set_iterate_lower_bound(lower.as_ref()); let mut iter = self.snapshot.iter_opt(options); iter.seek(SeekKey::Start); let iter = iter.map(|(key, _)| DocumentKeyAttr::from_bytes(&key)); if f.alternate() { writeln!(f, "DatabaseView(")?; } else { write!(f, "DatabaseView(")?; } self.schema.fmt(f)?; if f.alternate() { writeln!(f, ",")?; } else { write!(f, ", ")?; } f.debug_list().entries(iter).finish()?; write!(f, ")") } } // TODO this is just an iter::Map !!! pub struct DocumentIter<'a, D, I> { database_view: &'a DatabaseView<'a>, document_ids: I, _phantom: marker::PhantomData, } impl<'a, D, I> Iterator for DocumentIter<'a, D, I> where D: DeserializeOwned, I: Iterator, { type Item = Result>; fn size_hint(&self) -> (usize, Option) { self.document_ids.size_hint() } fn next(&mut self) -> Option { match self.document_ids.next() { Some(id) => Some(self.database_view.retrieve_document(id)), None => None } } } impl<'a, D, I> ExactSizeIterator for DocumentIter<'a, D, I> where D: DeserializeOwned, I: ExactSizeIterator + Iterator, { } impl<'a, D, I> DoubleEndedIterator for DocumentIter<'a, D, I> where D: DeserializeOwned, I: DoubleEndedIterator + Iterator, { fn next_back(&mut self) -> Option { match self.document_ids.next_back() { Some(id) => Some(self.database_view.retrieve_document(id)), None => None } } }