2019-05-15 11:36:44 +02:00
|
|
|
use std::collections::hash_map::Entry;
|
|
|
|
use std::collections::{HashSet, HashMap};
|
|
|
|
use std::path::Path;
|
|
|
|
use std::sync::{Arc, RwLock};
|
|
|
|
|
|
|
|
use crate::Schema;
|
|
|
|
|
2019-05-15 12:01:08 +02:00
|
|
|
mod custom_settings;
|
2019-05-15 11:36:44 +02:00
|
|
|
mod docs_words_index;
|
|
|
|
mod documents_addition;
|
|
|
|
mod documents_deletion;
|
|
|
|
mod documents_index;
|
|
|
|
mod error;
|
|
|
|
mod index;
|
|
|
|
mod main_index;
|
|
|
|
mod raw_index;
|
|
|
|
mod words_index;
|
|
|
|
|
|
|
|
pub use self::error::Error;
|
|
|
|
pub use self::index::Index;
|
2019-05-15 12:01:08 +02:00
|
|
|
pub use self::custom_settings::CustomSettings;
|
2019-05-15 11:36:44 +02:00
|
|
|
|
|
|
|
use self::docs_words_index::DocsWordsIndex;
|
|
|
|
use self::documents_addition::DocumentsAddition;
|
|
|
|
use self::documents_deletion::DocumentsDeletion;
|
|
|
|
use self::documents_index::DocumentsIndex;
|
|
|
|
use self::index::InnerIndex;
|
|
|
|
use self::main_index::MainIndex;
|
2019-05-24 14:26:05 +02:00
|
|
|
use self::raw_index::{RawIndex, InnerRawIndex};
|
2019-05-15 11:36:44 +02:00
|
|
|
use self::words_index::WordsIndex;
|
|
|
|
|
|
|
|
pub struct Database {
|
|
|
|
cache: RwLock<HashMap<String, Arc<Index>>>,
|
2019-05-23 14:47:10 +02:00
|
|
|
inner: Arc<rocksdb::DB>,
|
2019-05-15 11:36:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Database {
|
|
|
|
pub fn start_default<P: AsRef<Path>>(path: P) -> Result<Database, Error> {
|
2019-05-23 14:47:10 +02:00
|
|
|
let path = path.as_ref();
|
2019-05-15 11:36:44 +02:00
|
|
|
let cache = RwLock::new(HashMap::new());
|
|
|
|
|
2019-05-23 14:47:10 +02:00
|
|
|
let inner = {
|
|
|
|
let options = {
|
|
|
|
let mut options = rocksdb::Options::default();
|
|
|
|
options.create_if_missing(true);
|
|
|
|
options
|
|
|
|
};
|
|
|
|
let cfs = rocksdb::DB::list_cf(&options, path).unwrap_or(Vec::new());
|
|
|
|
Arc::new(rocksdb::DB::open_cf(&options, path, cfs)?)
|
|
|
|
};
|
2019-05-20 14:41:15 +02:00
|
|
|
|
|
|
|
Ok(Database { cache, inner })
|
|
|
|
}
|
|
|
|
|
2019-05-15 11:36:44 +02:00
|
|
|
pub fn indexes(&self) -> Result<Option<HashSet<String>>, Error> {
|
|
|
|
let bytes = match self.inner.get("indexes")? {
|
|
|
|
Some(bytes) => bytes,
|
|
|
|
None => return Ok(None),
|
|
|
|
};
|
|
|
|
|
|
|
|
let indexes = bincode::deserialize(&bytes)?;
|
|
|
|
Ok(Some(indexes))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_indexes(&self, value: &HashSet<String>) -> Result<(), Error> {
|
|
|
|
let bytes = bincode::serialize(value)?;
|
2019-05-23 14:47:10 +02:00
|
|
|
self.inner.put("indexes", bytes)?;
|
2019-05-15 11:36:44 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn open_index(&self, name: &str) -> Result<Option<Arc<Index>>, Error> {
|
|
|
|
{
|
|
|
|
let cache = self.cache.read().unwrap();
|
|
|
|
if let Some(index) = cache.get(name).cloned() {
|
|
|
|
return Ok(Some(index))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut cache = self.cache.write().unwrap();
|
|
|
|
let index = match cache.entry(name.to_string()) {
|
|
|
|
Entry::Occupied(occupied) => {
|
|
|
|
occupied.get().clone()
|
|
|
|
},
|
|
|
|
Entry::Vacant(vacant) => {
|
|
|
|
if !self.indexes()?.map_or(false, |x| x.contains(name)) {
|
|
|
|
return Ok(None)
|
|
|
|
}
|
|
|
|
|
|
|
|
let main = {
|
2019-05-23 14:47:10 +02:00
|
|
|
self.inner.cf_handle(name).expect("cf not found");
|
2019-05-24 14:26:05 +02:00
|
|
|
MainIndex(InnerRawIndex::new(self.inner.clone(), Arc::from(name)))
|
2019-05-15 11:36:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
let words = {
|
2019-05-23 14:47:10 +02:00
|
|
|
let cf_name = format!("{}-words", name);
|
|
|
|
self.inner.cf_handle(&cf_name).expect("cf not found");
|
2019-05-24 14:26:05 +02:00
|
|
|
WordsIndex(InnerRawIndex::new(self.inner.clone(), Arc::from(cf_name)))
|
2019-05-15 11:36:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
let docs_words = {
|
2019-05-23 14:47:10 +02:00
|
|
|
let cf_name = format!("{}-docs-words", name);
|
|
|
|
self.inner.cf_handle(&cf_name).expect("cf not found");
|
2019-05-24 14:26:05 +02:00
|
|
|
DocsWordsIndex(InnerRawIndex::new(self.inner.clone(), Arc::from(cf_name)))
|
2019-05-15 11:36:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
let documents = {
|
2019-05-23 14:47:10 +02:00
|
|
|
let cf_name = format!("{}-documents", name);
|
|
|
|
self.inner.cf_handle(&cf_name).expect("cf not found");
|
2019-05-24 14:26:05 +02:00
|
|
|
DocumentsIndex(InnerRawIndex::new(self.inner.clone(), Arc::from(cf_name)))
|
2019-05-15 11:36:44 +02:00
|
|
|
};
|
|
|
|
|
2019-05-15 12:01:08 +02:00
|
|
|
let custom = {
|
2019-05-23 14:47:10 +02:00
|
|
|
let cf_name = format!("{}-custom", name);
|
|
|
|
self.inner.cf_handle(&cf_name).expect("cf not found");
|
2019-05-24 14:26:05 +02:00
|
|
|
CustomSettings(InnerRawIndex::new(self.inner.clone(), Arc::from(cf_name)))
|
2019-05-15 12:01:08 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
let raw_index = RawIndex { main, words, docs_words, documents, custom };
|
2019-05-15 11:36:44 +02:00
|
|
|
let index = Index::from_raw(raw_index)?;
|
|
|
|
|
|
|
|
vacant.insert(Arc::new(index)).clone()
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Some(index))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn create_index(&self, name: &str, schema: Schema) -> Result<Arc<Index>, Error> {
|
|
|
|
let mut cache = self.cache.write().unwrap();
|
|
|
|
|
|
|
|
let index = match cache.entry(name.to_string()) {
|
|
|
|
Entry::Occupied(occupied) => {
|
|
|
|
occupied.get().clone()
|
|
|
|
},
|
|
|
|
Entry::Vacant(vacant) => {
|
|
|
|
let main = {
|
2019-05-23 14:47:10 +02:00
|
|
|
self.inner.create_cf(name, &rocksdb::Options::default())?;
|
2019-05-24 14:26:05 +02:00
|
|
|
MainIndex(InnerRawIndex::new(self.inner.clone(), Arc::from(name)))
|
2019-05-15 11:36:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(prev_schema) = main.schema()? {
|
|
|
|
if prev_schema != schema {
|
|
|
|
return Err(Error::SchemaDiffer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
main.set_schema(&schema)?;
|
|
|
|
|
|
|
|
let words = {
|
2019-05-23 14:47:10 +02:00
|
|
|
let cf_name = format!("{}-words", name);
|
|
|
|
self.inner.create_cf(&cf_name, &rocksdb::Options::default())?;
|
2019-05-24 14:26:05 +02:00
|
|
|
WordsIndex(InnerRawIndex::new(self.inner.clone(), Arc::from(cf_name)))
|
2019-05-15 11:36:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
let docs_words = {
|
2019-05-23 14:47:10 +02:00
|
|
|
let cf_name = format!("{}-docs-words", name);
|
|
|
|
self.inner.create_cf(&cf_name, &rocksdb::Options::default())?;
|
2019-05-24 14:26:05 +02:00
|
|
|
DocsWordsIndex(InnerRawIndex::new(self.inner.clone(), Arc::from(cf_name)))
|
2019-05-15 11:36:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
let documents = {
|
2019-05-23 14:47:10 +02:00
|
|
|
let cf_name = format!("{}-documents", name);
|
|
|
|
self.inner.create_cf(&cf_name, &rocksdb::Options::default())?;
|
2019-05-24 14:26:05 +02:00
|
|
|
DocumentsIndex(InnerRawIndex::new(self.inner.clone(), Arc::from(cf_name)))
|
2019-05-15 11:36:44 +02:00
|
|
|
};
|
|
|
|
|
2019-05-15 12:01:08 +02:00
|
|
|
let custom = {
|
2019-05-23 14:47:10 +02:00
|
|
|
let cf_name = format!("{}-custom", name);
|
|
|
|
self.inner.create_cf(&cf_name, &rocksdb::Options::default())?;
|
2019-05-24 14:26:05 +02:00
|
|
|
CustomSettings(InnerRawIndex::new(self.inner.clone(), Arc::from(cf_name)))
|
2019-05-15 12:01:08 +02:00
|
|
|
};
|
|
|
|
|
2019-05-15 11:36:44 +02:00
|
|
|
let mut indexes = self.indexes()?.unwrap_or_else(HashSet::new);
|
|
|
|
indexes.insert(name.to_string());
|
|
|
|
self.set_indexes(&indexes)?;
|
|
|
|
|
2019-05-15 12:01:08 +02:00
|
|
|
let raw_index = RawIndex { main, words, docs_words, documents, custom };
|
2019-05-15 11:36:44 +02:00
|
|
|
let index = Index::from_raw(raw_index)?;
|
|
|
|
|
|
|
|
vacant.insert(Arc::new(index)).clone()
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(index)
|
|
|
|
}
|
|
|
|
}
|