use std::error::Error; use std::path::Path; use std::ops::Deref; use std::{fmt, marker}; use rocksdb::rocksdb_options::{ReadOptions, EnvOptions, ColumnFamilyOptions}; use rocksdb::rocksdb::{DB, DBVector, Snapshot, SeekKey, SstFileWriter}; use serde::de::DeserializeOwned; use crate::database::{DocumentKey, DocumentKeyAttr}; use crate::database::{retrieve_data_schema, retrieve_data_index}; use crate::database::blob::positive::PositiveBlob; use crate::database::deserializer::Deserializer; use crate::database::schema::Schema; use crate::rank::QueryBuilder; use crate::DocumentId; pub struct DatabaseView where D: Deref { snapshot: Snapshot, blob: PositiveBlob, schema: Schema, } impl DatabaseView where D: Deref { pub fn new(snapshot: Snapshot) -> Result, Box> { let schema = retrieve_data_schema(&snapshot)?; let blob = retrieve_data_index(&snapshot)?; Ok(DatabaseView { snapshot, blob, schema }) } pub fn schema(&self) -> &Schema { &self.schema } pub fn blob(&self) -> &PositiveBlob { &self.blob } pub fn into_snapshot(self) -> Snapshot { self.snapshot } pub fn snapshot(&self) -> &Snapshot { &self.snapshot } pub fn get(&self, key: &[u8]) -> Result, Box> { Ok(self.snapshot.get(key)?) } pub fn dump_all>(&self, path: P) -> Result<(), Box> { let path = path.as_ref().to_string_lossy(); let env_options = EnvOptions::new(); let column_family_options = ColumnFamilyOptions::new(); let mut file_writer = SstFileWriter::new(env_options, column_family_options); file_writer.open(&path)?; let mut iter = self.snapshot.iter(); iter.seek(SeekKey::Start); for (key, value) in &mut iter { file_writer.put(&key, &value)?; } file_writer.finish()?; Ok(()) } pub fn query_builder(&self) -> Result, Box> { QueryBuilder::new(self) } pub fn document_by_id(&self, id: DocumentId) -> Result> where T: DeserializeOwned { let mut deserializer = Deserializer::new(&self.snapshot, &self.schema, id); Ok(T::deserialize(&mut deserializer)?) } pub fn documents_by_id(&self, ids: I) -> DocumentIter where T: DeserializeOwned, I: IntoIterator, { DocumentIter { database_view: self, document_ids: ids.into_iter(), _phantom: marker::PhantomData, } } } impl fmt::Debug for DatabaseView where D: Deref { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut options = ReadOptions::new(); let lower = DocumentKey::new(DocumentId(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, T, I> where D: Deref { database_view: &'a DatabaseView, document_ids: I, _phantom: marker::PhantomData, } impl<'a, D, T, I> Iterator for DocumentIter<'a, D, T, I> where D: Deref, T: 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.document_by_id(id)), None => None } } } impl<'a, D, T, I> ExactSizeIterator for DocumentIter<'a, D, T, I> where D: Deref, T: DeserializeOwned, I: ExactSizeIterator + Iterator, { } impl<'a, D, T, I> DoubleEndedIterator for DocumentIter<'a, D, T, I> where D: Deref, T: DeserializeOwned, I: DoubleEndedIterator + Iterator, { fn next_back(&mut self) -> Option { match self.document_ids.next_back() { Some(id) => Some(self.database_view.document_by_id(id)), None => None } } }