From 20319f797405e0527b39ee8b7edf8dbbac0aae21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Tue, 5 Nov 2019 12:02:42 +0100 Subject: [PATCH 1/2] Allow to introduce attributes only at the end of a schema --- meilidb-core/src/error.rs | 6 +++--- meilidb-core/src/update/schema_update.rs | 9 +++++++-- meilidb-schema/src/lib.rs | 4 ++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/meilidb-core/src/error.rs b/meilidb-core/src/error.rs index 3fb4d199c..7d58e2756 100644 --- a/meilidb-core/src/error.rs +++ b/meilidb-core/src/error.rs @@ -97,7 +97,7 @@ pub enum UnsupportedOperation { SchemaAlreadyExists, CannotUpdateSchemaIdentifier, CannotReorderSchemaAttribute, - CannotIntroduceNewSchemaAttribute, + CanOnlyIntroduceNewSchemaAttributesAtEnd, CannotRemoveSchemaAttribute, } @@ -108,8 +108,8 @@ impl fmt::Display for UnsupportedOperation { SchemaAlreadyExists => write!(f, "Cannot update index which already have a schema"), CannotUpdateSchemaIdentifier => write!(f, "Cannot update the identifier of a schema"), CannotReorderSchemaAttribute => write!(f, "Cannot reorder the attributes of a schema"), - CannotIntroduceNewSchemaAttribute => { - write!(f, "Cannot introduce new attributes in a schema") + CanOnlyIntroduceNewSchemaAttributesAtEnd => { + write!(f, "Can only introduce new attributes at end of a schema") } CannotRemoveSchemaAttribute => write!(f, "Cannot remove attributes from a schema"), } diff --git a/meilidb-core/src/update/schema_update.rs b/meilidb-core/src/update/schema_update.rs index 06f37b617..c033df6cf 100644 --- a/meilidb-core/src/update/schema_update.rs +++ b/meilidb-core/src/update/schema_update.rs @@ -14,7 +14,7 @@ pub fn apply_schema_update( docs_words_store: store::DocsWords, ) -> MResult<()> { use UnsupportedOperation::{ - CannotIntroduceNewSchemaAttribute, CannotRemoveSchemaAttribute, + CanOnlyIntroduceNewSchemaAttributesAtEnd, CannotRemoveSchemaAttribute, CannotReorderSchemaAttribute, CannotUpdateSchemaIdentifier, }; @@ -33,7 +33,12 @@ pub fn apply_schema_update( need_full_reindexing = true; } } - Diff::NewAttr { .. } => return Err(CannotIntroduceNewSchemaAttribute.into()), + Diff::NewAttr { pos, .. } => { + // new attribute not at the end of the schema + if pos < old_schema.number_of_attributes() { + return Err(CanOnlyIntroduceNewSchemaAttributesAtEnd.into()); + } + } Diff::RemovedAttr { .. } => return Err(CannotRemoveSchemaAttribute.into()), } } diff --git a/meilidb-schema/src/lib.rs b/meilidb-schema/src/lib.rs index 5ec64f3b6..929316d41 100644 --- a/meilidb-schema/src/lib.rs +++ b/meilidb-schema/src/lib.rs @@ -169,6 +169,10 @@ impl Schema { attributes } + pub fn number_of_attributes(&self) -> usize { + self.inner.attrs.len() + } + pub fn props(&self, attr: SchemaAttr) -> SchemaProps { let (_, props) = self.inner.props[attr.0 as usize]; props From 5782fb9e5295c248d607a1ef8f70036c2b5c9b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Renault?= Date: Tue, 5 Nov 2019 12:03:37 +0100 Subject: [PATCH 2/2] Test the add of attributes only at the end of a schema --- meilidb-core/src/database.rs | 170 +++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/meilidb-core/src/database.rs b/meilidb-core/src/database.rs index 25d23d164..5210db72f 100644 --- a/meilidb-core/src/database.rs +++ b/meilidb-core/src/database.rs @@ -359,4 +359,174 @@ mod tests { let result = index.update_status(&reader, update_id).unwrap(); assert_matches!(result, UpdateStatus::Processed(status) if status.result.is_err()); } + + #[test] + fn add_schema_attributes_at_end() { + let dir = tempfile::tempdir().unwrap(); + + let database = Database::open_or_create(dir.path()).unwrap(); + let env = &database.env; + + let (sender, receiver) = mpsc::sync_channel(100); + let update_fn = move |update: ProcessedUpdateResult| sender.send(update.update_id).unwrap(); + let index = database.create_index("test").unwrap(); + + let done = database.set_update_callback("test", Box::new(update_fn)); + assert!(done, "could not set the index update function"); + + let schema = { + let data = r#" + identifier = "id" + + [attributes."name"] + displayed = true + indexed = true + + [attributes."description"] + displayed = true + indexed = true + "#; + toml::from_str(data).unwrap() + }; + + let mut writer = env.write_txn().unwrap(); + let _update_id = index.schema_update(&mut writer, schema).unwrap(); + writer.commit().unwrap(); + + let mut additions = index.documents_addition(); + + let doc1 = serde_json::json!({ + "id": 123, + "name": "Marvin", + "description": "My name is Marvin", + }); + + let doc2 = serde_json::json!({ + "id": 234, + "name": "Kevin", + "description": "My name is Kevin", + }); + + additions.update_document(doc1); + additions.update_document(doc2); + + let mut writer = env.write_txn().unwrap(); + let _update_id = additions.finalize(&mut writer).unwrap(); + writer.commit().unwrap(); + + let schema = { + let data = r#" + identifier = "id" + + [attributes."name"] + displayed = true + indexed = true + + [attributes."description"] + displayed = true + indexed = true + + [attributes."age"] + displayed = true + indexed = true + + [attributes."sex"] + displayed = true + indexed = true + "#; + toml::from_str(data).unwrap() + }; + + let mut writer = env.write_txn().unwrap(); + let update_id = index.schema_update(&mut writer, schema).unwrap(); + writer.commit().unwrap(); + + // block until the transaction is processed + let _ = receiver.iter().find(|id| *id == update_id); + + // check if it has been accepted + let reader = env.read_txn().unwrap(); + let result = index.update_status(&reader, update_id).unwrap(); + assert_matches!(result, UpdateStatus::Processed(status) if status.result.is_ok()); + reader.abort(); + + let mut additions = index.documents_addition(); + + let doc1 = serde_json::json!({ + "id": 123, + "name": "Marvin", + "description": "My name is Marvin", + "age": 21, + "sex": "Male", + }); + + let doc2 = serde_json::json!({ + "id": 234, + "name": "Kevin", + "description": "My name is Kevin", + "age": 23, + "sex": "Male", + }); + + additions.update_document(doc1); + additions.update_document(doc2); + + let mut writer = env.write_txn().unwrap(); + let update_id = additions.finalize(&mut writer).unwrap(); + writer.commit().unwrap(); + + // block until the transaction is processed + let _ = receiver.iter().find(|id| *id == update_id); + + // check if it has been accepted + let reader = env.read_txn().unwrap(); + let result = index.update_status(&reader, update_id).unwrap(); + assert_matches!(result, UpdateStatus::Processed(status) if status.result.is_ok()); + + // even try to search for a document + let results = index.query_builder().query(&reader, "21 ", 0..20).unwrap(); + assert_matches!(results.len(), 1); + + reader.abort(); + + // try to introduce attributes in the middle of the schema + let schema = { + let data = r#" + identifier = "id" + + [attributes."name"] + displayed = true + indexed = true + + [attributes."description"] + displayed = true + indexed = true + + [attributes."city"] + displayed = true + indexed = true + + [attributes."age"] + displayed = true + indexed = true + + [attributes."sex"] + displayed = true + indexed = true + "#; + toml::from_str(data).unwrap() + }; + + let mut writer = env.write_txn().unwrap(); + let update_id = index.schema_update(&mut writer, schema).unwrap(); + writer.commit().unwrap(); + + // block until the transaction is processed + let _ = receiver.iter().find(|id| *id == update_id); + + // check if it has been accepted + let reader = env.read_txn().unwrap(); + let result = index.update_status(&reader, update_id).unwrap(); + assert_matches!(result, UpdateStatus::Processed(status) if status.result.is_err()); + } }