diff --git a/src/core/index.rs b/src/core/index.rs index 3eafb90cc..103db95c1 100644 --- a/src/core/index.rs +++ b/src/core/index.rs @@ -49,6 +49,11 @@ pub struct Index { } impl Index { + /// Examines the director to see if it contains an index + pub fn exists(dir: &Dir) -> bool { + dir.exists(&META_FILEPATH) + } + /// Creates a new index using the `RAMDirectory`. /// /// The index will be allocated in anonymous memory. @@ -65,9 +70,28 @@ impl Index { #[cfg(feature = "mmap")] pub fn create_in_dir>(directory_path: P, schema: Schema) -> Result { let mmap_directory = MmapDirectory::open(directory_path)?; + if Index::exists(&mmap_directory) { + return Err(TantivyError::IndexAlreadyExists); + } + Index::create(mmap_directory, schema) } + /// Opens or creates a new index in the provided directory + #[cfg(feature = "mmap")] + pub fn open_or_create(dir: Dir, schema: Schema) -> Result { + if Index::exists(&dir) { + let index = Index::open(dir)?; + if index.schema() == schema { + Ok(index) + } else { + Err(TantivyError::SchemaError("An index exists but the schema does not match.".to_string())) + } + } else { + Index::create(dir, schema) + } + } + /// Creates a new index in a temp directory. /// /// The index will use the `MMapDirectory` in a newly created directory. @@ -89,6 +113,8 @@ impl Index { } /// Create a new index from a directory. + /// + /// This will overwrite existing meta.json fn from_directory(mut directory: ManagedDirectory, schema: Schema) -> Result { save_new_metas(schema.clone(), 0, directory.borrow_mut())?; let metas = IndexMeta::with_schema(schema); @@ -328,8 +354,9 @@ impl Clone for Index { #[cfg(test)] mod tests { - use schema::{SchemaBuilder, INT_INDEXED, TEXT}; + use schema::{Schema, SchemaBuilder, INT_INDEXED, TEXT}; use Index; + use directory::RAMDirectory; #[test] fn test_indexer_for_field() { @@ -345,4 +372,52 @@ mod tests { ); } + #[test] + fn test_index_exists() { + let directory = RAMDirectory::create(); + assert!(!Index::exists(&directory)); + assert!(Index::create(directory.clone(), throw_away_schema()).is_ok()); + assert!(Index::exists(&directory)); + } + + #[test] + fn open_or_create_should_create() { + let directory = RAMDirectory::create(); + assert!(!Index::exists(&directory)); + assert!(Index::open_or_create(directory.clone(), throw_away_schema()).is_ok()); + assert!(Index::exists(&directory)); + } + + + #[test] + fn open_or_create_should_open() { + let directory = RAMDirectory::create(); + assert!(Index::create(directory.clone(), throw_away_schema()).is_ok()); + assert!(Index::exists(&directory)); + assert!(Index::open_or_create(directory, throw_away_schema()).is_ok()); + } + + #[test] + fn create_should_wipeoff_existing() { + let directory = RAMDirectory::create(); + assert!(Index::create(directory.clone(), throw_away_schema()).is_ok()); + assert!(Index::exists(&directory)); + assert!(Index::create(directory.clone(), SchemaBuilder::default().build()).is_ok()); + } + + #[test] + fn open_or_create_exists_but_schema_does_not_match() { + let directory = RAMDirectory::create(); + assert!(Index::create(directory.clone(), throw_away_schema()).is_ok()); + assert!(Index::exists(&directory)); + assert!(Index::open_or_create(directory.clone(), throw_away_schema()).is_ok()); + let err = Index::open_or_create(directory, SchemaBuilder::default().build()); + assert_eq!(format!("{:?}", err.unwrap_err()), "SchemaError(\"An index exists but the schema does not match.\")"); + } + + fn throw_away_schema() -> Schema { + let mut schema_builder = SchemaBuilder::default(); + let _ = schema_builder.add_u64_field("num_likes", INT_INDEXED); + schema_builder.build() + } } diff --git a/src/directory/mmap_directory.rs b/src/directory/mmap_directory.rs index 619e0fd19..03031562f 100644 --- a/src/directory/mmap_directory.rs +++ b/src/directory/mmap_directory.rs @@ -364,6 +364,11 @@ mod tests { use super::*; + #[test] + fn test_open_non_existant_path() { + assert!(MmapDirectory::open(PathBuf::from("./nowhere")).is_err()); + } + #[test] fn test_open_empty() { // empty file is actually an edge case because those diff --git a/src/error.rs b/src/error.rs index a84befbc8..c651ad70f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -20,6 +20,9 @@ pub enum TantivyError { /// File already exists, this is a problem when we try to write into a new file. #[fail(display = "file already exists: '{:?}'", _0)] FileAlreadyExists(PathBuf), + /// Index already exists in this directory + #[fail(display = "index already exists")] + IndexAlreadyExists, /// Failed to acquire file lock #[fail( display = "Failed to acquire Lockfile: {:?}. Possible causes: another IndexWriter instance or panic during previous lock drop.", diff --git a/src/schema/field_entry.rs b/src/schema/field_entry.rs index 380ecf8c9..e63fa99ab 100644 --- a/src/schema/field_entry.rs +++ b/src/schema/field_entry.rs @@ -14,7 +14,7 @@ use std::fmt; /// - a field name /// - a field type, itself wrapping up options describing /// how the field should be indexed. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct FieldEntry { name: String, field_type: FieldType, diff --git a/src/schema/schema.rs b/src/schema/schema.rs index 85d8d14f3..179579ef4 100644 --- a/src/schema/schema.rs +++ b/src/schema/schema.rs @@ -134,6 +134,15 @@ struct InnerSchema { fields_map: HashMap, // transient } +impl PartialEq for InnerSchema { + fn eq(&self, other: &InnerSchema) -> bool { + self.fields == other.fields + } +} + +impl Eq for InnerSchema {} + + /// Tantivy has a very strict schema. /// You need to specify in advance, whether a field is indexed or not, /// stored or not, and RAM-based or not. @@ -154,7 +163,7 @@ struct InnerSchema { /// let schema = schema_builder.build(); /// /// ``` -#[derive(Clone)] +#[derive(Clone, Eq, PartialEq)] pub struct Schema(Arc); impl Schema {