pub mod storage;
pub mod types;
pub mod ingest;
pub mod index;
pub mod query;
pub mod bench;
pub mod fst_index;
pub mod incremental;
pub mod inverted_index;
pub mod tombstone;
pub mod locking;
pub mod compaction;
pub use types::*;
pub use bench::{SiftDBBenchmark, AdvancedBenchmark, AdvancedBenchmarkResults};
pub use compaction::{CollectionCompactor, CompactionManager, CompactionStatus};
use anyhow::Result;
use crate::locking::{SWMRLockManager, ReadLock, WriteLock};
pub struct SiftDB {
path: std::path::PathBuf,
lock_manager: SWMRLockManager,
}
impl SiftDB {
pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
let path = path.as_ref().to_path_buf();
if !path.exists() {
anyhow::bail!("SiftDB collection does not exist at: {}", path.display());
}
let lock_manager = SWMRLockManager::new(&path);
Ok(Self { path, lock_manager })
}
pub fn init<P: AsRef<std::path::Path>>(path: P) -> Result<Self> {
let path = path.as_ref().to_path_buf();
std::fs::create_dir_all(&path)?;
std::fs::create_dir_all(path.join("store"))?;
std::fs::create_dir_all(path.join("index"))?;
std::fs::create_dir_all(path.join("tmp"))?;
std::fs::create_dir_all(path.join("gc"))?;
let manifest = Manifest::new(0);
manifest.write_to_file(&path.join("MANIFEST.a"))?;
let lock_manager = SWMRLockManager::new(&path);
Ok(Self { path, lock_manager })
}
pub fn snapshot(&self) -> Result<Snapshot> {
self.snapshot_with_lock_config(30, "sift-cli".to_string())
}
pub fn snapshot_with_lock_config(&self, timeout_secs: u64, holder_info: String) -> Result<Snapshot> {
let manifest_path = self.path.join("MANIFEST.a");
let manifest = if manifest_path.exists() {
Manifest::read_from_file(&manifest_path)?
} else {
let manifest_path = self.path.join("MANIFEST.b");
if manifest_path.exists() {
Manifest::read_from_file(&manifest_path)?
} else {
anyhow::bail!("No valid manifest found");
}
};
let path_index = crate::index::PathIndex::read_from_file(&self.path.join("index/path.json"))?;
let handles_map = crate::index::HandlesMap::read_from_file(&self.path.join("index/handles.json"))?;
let inverted_index = if self.path.join("index/terms.fst").exists() && self.path.join("index/posting_lists.json").exists() {
crate::inverted_index::InvertedIndex::load_from_files(
&self.path.join("index/terms.fst"),
&self.path.join("index/posting_lists.json")
)?
} else {
crate::inverted_index::InvertedIndex::new()
};
let _read_lock = self.lock_manager.acquire_read_lock(timeout_secs, holder_info)?;
Ok(Snapshot {
collection_path: self.path.clone(),
epoch: manifest.epoch,
path_index,
handles_map,
inverted_index,
segment_cache: std::collections::HashMap::new(),
_read_lock: None, })
}
pub fn incremental_update(
&self,
source_path: &std::path::Path,
includes: &[String],
excludes: &[String],
) -> Result<crate::incremental::DeltaManifest> {
let updater = crate::incremental::IncrementalUpdater::new(&self.path);
let changes = updater.scan_for_changes(source_path, includes, excludes)?;
if changes.is_empty() {
anyhow::bail!("No changes detected since last import");
}
println!("Found {} changed files:", changes.len());
for change in &changes {
match change.change_type {
crate::incremental::ChangeType::Added => {
println!(" + {}", change.path.display());
}
crate::incremental::ChangeType::Modified => {
println!(" M {}", change.path.display());
}
crate::incremental::ChangeType::Deleted => {
println!(" - {}", change.path.display());
}
}
}
let delta_manifest = updater.apply_changes(changes, source_path)?;
Ok(delta_manifest)
}
pub fn has_incremental_cache(&self) -> bool {
let cache_path = self.path.join("index").join("file_cache.json");
cache_path.exists()
}
}
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}