From 91c8c7a2e3596423b1d4cff215455ad6315c0b3b Mon Sep 17 00:00:00 2001 From: qdequele Date: Mon, 17 Aug 2020 17:01:53 +0200 Subject: [PATCH 1/6] lazily create an index during document addition --- meilisearch-http/src/routes/document.rs | 33 ++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 38e333aab..78d8345d6 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -159,6 +159,36 @@ struct UpdateDocumentsQuery { primary_key: Option, } +fn create_index(data: &Data, uid: &str) -> Result { + if !uid + .chars() + .all(|x| x.is_ascii_alphanumeric() || x == '-' || x == '_') + { + return Err(Error::InvalidIndexUid.into()); + } + + let created_index = data.db.create_index(&uid).map_err(|e| match e { + meilisearch_core::Error::IndexAlreadyExists => e.into(), + _ => ResponseError::from(Error::create_index(e)), + })?; + + data.db.main_write::<_, _, ResponseError>(|mut writer| { + created_index.main.put_name(&mut writer, uid)?; + + created_index + .main + .created_at(&writer)? + .ok_or(Error::internal("Impossible to read created at"))?; + + created_index + .main + .updated_at(&writer)? + .ok_or(Error::internal("Impossible to read updated at"))?; + Ok(()) + })?; + + Ok(created_index) +} async fn update_multiple_documents( data: web::Data, path: web::Path, @@ -169,7 +199,8 @@ async fn update_multiple_documents( let index = data .db .open_index(&path.index_uid) - .ok_or(Error::index_not_found(&path.index_uid))?; + .ok_or(Error::index_not_found(&path.index_uid)) + .or(create_index(&data, &path.index_uid))?; let reader = data.db.main_read_txn()?; From 2644f087d008bf0b356ee916058d9412088e4f3c Mon Sep 17 00:00:00 2001 From: qdequele Date: Mon, 17 Aug 2020 17:02:55 +0200 Subject: [PATCH 2/6] add tests --- meilisearch-http/tests/documents_add.rs | 67 ++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/meilisearch-http/tests/documents_add.rs b/meilisearch-http/tests/documents_add.rs index fae29f15e..b4a7a8afc 100644 --- a/meilisearch-http/tests/documents_add.rs +++ b/meilisearch-http/tests/documents_add.rs @@ -213,5 +213,70 @@ async fn documents_with_same_id_are_overwritten() { server.add_or_replace_multiple_documents(documents).await; let (response, _status) = server.get_all_documents().await; assert_eq!(response.as_array().unwrap().len(), 1); - assert_eq!(response.as_array().unwrap()[0].as_object().unwrap()["content"], "test2"); + +#[actix_rt::test] +async fn create_index_lazy_by_pushing_documents() { + let mut server = common::Server::with_uid("movies"); + + // 1 - Add documents + + let body = json!([{ + "title": "Test", + "comment": "comment test" + }]); + + let url = "/indexes/movies/documents?primaryKey=title"; + let (response, status_code) = server.post_request(&url, body).await; + eprintln!("{:#?}", response); + assert_eq!(status_code, 202); + let update_id = response["updateId"].as_u64().unwrap(); + server.wait_update_id(update_id).await; + + // 3 - Check update success + + let (response, status_code) = server.get_update_status(update_id).await; + assert_eq!(status_code, 200); + assert_eq!(response["status"], "processed"); +} + +#[actix_rt::test] +async fn create_index_lazy_by_pushing_documents_and_discover_pk() { + let mut server = common::Server::with_uid("movies"); + + // 1 - Add documents + + let body = json!([{ + "id": 1, + "title": "Test", + "comment": "comment test" + }]); + + let url = "/indexes/movies/documents"; + let (response, status_code) = server.post_request(&url, body).await; + eprintln!("{:#?}", response); + assert_eq!(status_code, 202); + let update_id = response["updateId"].as_u64().unwrap(); + server.wait_update_id(update_id).await; + + // 3 - Check update success + + let (response, status_code) = server.get_update_status(update_id).await; + assert_eq!(status_code, 200); + assert_eq!(response["status"], "processed"); +} + +#[actix_rt::test] +async fn create_index_lazy_by_pushing_documents_with_wwrong_name() { + let mut server = common::Server::with_uid("wrong&name"); + + let body = json!([{ + "title": "Test", + "comment": "comment test" + }]); + + let url = "/indexes/wrong&name/documents?primaryKey=title"; + let (response, status_code) = server.post_request(&url, body).await; + eprintln!("{:#?}", response); + assert_eq!(status_code, 400); + assert_eq!(response["errorCode"], "invalid_index_uid"); } From 0a67248bfeaabc083f0401ecd3278737e27d3ac1 Mon Sep 17 00:00:00 2001 From: qdequele Date: Mon, 17 Aug 2020 17:03:57 +0200 Subject: [PATCH 3/6] cargo fmt --- meilisearch-http/src/routes/document.rs | 15 +++++++-------- meilisearch-http/tests/documents_add.rs | 9 ++++++++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 78d8345d6..9290a8710 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -3,14 +3,14 @@ use std::collections::{BTreeSet, HashSet}; use actix_web::{delete, get, post, put}; use actix_web::{web, HttpResponse}; use indexmap::IndexMap; -use meilisearch_core::{update, MainReader}; +use meilisearch_core::{update, Index}; use serde_json::Value; use serde::Deserialize; -use crate::Data; use crate::error::{Error, ResponseError}; use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; +use crate::Data; type Document = IndexMap; @@ -45,7 +45,8 @@ async fn get_document( let reader = data.db.main_read_txn()?; - let internal_id = index.main + let internal_id = index + .main .external_to_internal_docid(&reader, &path.document_id)? .ok_or(Error::document_not_found(&path.document_id))?; @@ -189,6 +190,7 @@ fn create_index(data: &Data, uid: &str) -> Result { Ok(created_index) } + async fn update_multiple_documents( data: web::Data, path: web::Path, @@ -215,12 +217,10 @@ async fn update_multiple_documents( None => body .first() .and_then(find_primary_key) - .ok_or(meilisearch_core::Error::MissingPrimaryKey)? + .ok_or(meilisearch_core::Error::MissingPrimaryKey)?, }; - schema - .set_primary_key(&id) - .map_err(Error::bad_request)?; + schema.set_primary_key(&id).map_err(Error::bad_request)?; data.db.main_write(|w| index.main.put_schema(w, &schema))?; } @@ -274,7 +274,6 @@ async fn delete_documents( .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; - let mut documents_deletion = index.documents_deletion(); for document_id in body.into_inner() { diff --git a/meilisearch-http/tests/documents_add.rs b/meilisearch-http/tests/documents_add.rs index b4a7a8afc..140a194c3 100644 --- a/meilisearch-http/tests/documents_add.rs +++ b/meilisearch-http/tests/documents_add.rs @@ -192,7 +192,9 @@ async fn add_document_with_long_field() { "url":"/configuration/app/web.html#locations" }]); server.add_or_replace_multiple_documents(body).await; - let (response, _status) = server.search_post(json!({ "q": "request_buffering" })).await; + let (response, _status) = server + .search_post(json!({ "q": "request_buffering" })) + .await; assert!(!response["hits"].as_array().unwrap().is_empty()); } @@ -213,6 +215,11 @@ async fn documents_with_same_id_are_overwritten() { server.add_or_replace_multiple_documents(documents).await; let (response, _status) = server.get_all_documents().await; assert_eq!(response.as_array().unwrap().len(), 1); + assert_eq!( + response.as_array().unwrap()[0].as_object().unwrap()["content"], + "test2" + ); +} #[actix_rt::test] async fn create_index_lazy_by_pushing_documents() { From bfe3bb0eebe2e42ea74ec2637403de83e13d538f Mon Sep 17 00:00:00 2001 From: qdequele Date: Tue, 8 Sep 2020 19:16:17 +0200 Subject: [PATCH 4/6] create an helper to allow to delete the index on error --- Cargo.lock | 6 +- meilisearch-http/src/data.rs | 59 ++++++++++++- meilisearch-http/src/routes/document.rs | 94 +++++++-------------- meilisearch-http/src/routes/setting.rs | 107 +++++++++++++----------- 4 files changed, 149 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3abc34c71..196deb1bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1914,7 +1914,8 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pest" version = "2.1.3" -source = "git+https://github.com/pest-parser/pest.git?rev=51fd1d49f1041f7839975664ef71fe15c7dcaf67#51fd1d49f1041f7839975664ef71fe15c7dcaf67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" dependencies = [ "ucd-trie", ] @@ -1922,8 +1923,7 @@ dependencies = [ [[package]] name = "pest" version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +source = "git+https://github.com/MarinPostma/pest.git?tag=meilisearch-patch1#e1031ad0134d5e9893c470dbea50811b2b746926" dependencies = [ "ucd-trie", ] diff --git a/meilisearch-http/src/data.rs b/meilisearch-http/src/data.rs index ae90e2a03..b552e7f6e 100644 --- a/meilisearch-http/src/data.rs +++ b/meilisearch-http/src/data.rs @@ -3,9 +3,10 @@ use std::ops::Deref; use std::path::PathBuf; use std::sync::Arc; -use meilisearch_core::{Database, DatabaseOptions}; +use meilisearch_core::{Database, DatabaseOptions, Index}; use sha2::Digest; +use crate::error::{Error as MSError, ResponseError}; use crate::index_update_callback; use crate::option::Opt; @@ -102,4 +103,60 @@ impl Data { Ok(data) } + + fn create_index(&self, uid: &str) -> Result { + if !uid + .chars() + .all(|x| x.is_ascii_alphanumeric() || x == '-' || x == '_') + { + return Err(MSError::InvalidIndexUid.into()); + } + + let created_index = self.db.create_index(&uid).map_err(|e| match e { + meilisearch_core::Error::IndexAlreadyExists => e.into(), + _ => ResponseError::from(MSError::create_index(e)), + })?; + + self.db.main_write::<_, _, ResponseError>(|mut writer| { + created_index.main.put_name(&mut writer, uid)?; + + created_index + .main + .created_at(&writer)? + .ok_or(MSError::internal("Impossible to read created at"))?; + + created_index + .main + .updated_at(&writer)? + .ok_or(MSError::internal("Impossible to read updated at"))?; + Ok(()) + })?; + + Ok(created_index) + } + + pub fn get_or_create_index(&self, uid: &str, f: F) -> Result + where + F: FnOnce(&Index) -> Result, + { + let mut index_has_been_created = false; + + let index = match self.db.open_index(&uid) { + Some(index) => index, + None => { + index_has_been_created = true; + self.create_index(&uid)? + } + }; + + match f(&index) { + Ok(r) => Ok(r), + Err(err) => { + if index_has_been_created { + let _ = self.db.delete_index(&uid); + } + Err(err) + } + } + } } diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index 9290a8710..f565c1e6b 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -160,37 +160,6 @@ struct UpdateDocumentsQuery { primary_key: Option, } -fn create_index(data: &Data, uid: &str) -> Result { - if !uid - .chars() - .all(|x| x.is_ascii_alphanumeric() || x == '-' || x == '_') - { - return Err(Error::InvalidIndexUid.into()); - } - - let created_index = data.db.create_index(&uid).map_err(|e| match e { - meilisearch_core::Error::IndexAlreadyExists => e.into(), - _ => ResponseError::from(Error::create_index(e)), - })?; - - data.db.main_write::<_, _, ResponseError>(|mut writer| { - created_index.main.put_name(&mut writer, uid)?; - - created_index - .main - .created_at(&writer)? - .ok_or(Error::internal("Impossible to read created at"))?; - - created_index - .main - .updated_at(&writer)? - .ok_or(Error::internal("Impossible to read updated at"))?; - Ok(()) - })?; - - Ok(created_index) -} - async fn update_multiple_documents( data: web::Data, path: web::Path, @@ -198,46 +167,41 @@ async fn update_multiple_documents( body: web::Json>, is_partial: bool, ) -> Result { - let index = data - .db - .open_index(&path.index_uid) - .ok_or(Error::index_not_found(&path.index_uid)) - .or(create_index(&data, &path.index_uid))?; + let update_id = data.get_or_create_index(&path.index_uid, |index| { + let reader = data.db.main_read_txn()?; - let reader = data.db.main_read_txn()?; + let mut schema = index + .main + .schema(&reader)? + .ok_or(meilisearch_core::Error::SchemaMissing)?; - let mut schema = index - .main - .schema(&reader)? - .ok_or(meilisearch_core::Error::SchemaMissing)?; + if schema.primary_key().is_none() { + let id = match ¶ms.primary_key { + Some(id) => id.to_string(), + None => body + .first() + .and_then(find_primary_key) + .ok_or(meilisearch_core::Error::MissingPrimaryKey)?, + }; - if schema.primary_key().is_none() { - let id = match ¶ms.primary_key { - Some(id) => id.to_string(), - None => body - .first() - .and_then(find_primary_key) - .ok_or(meilisearch_core::Error::MissingPrimaryKey)?, + schema.set_primary_key(&id).map_err(Error::bad_request)?; + + data.db.main_write(|w| index.main.put_schema(w, &schema))?; + } + + let mut document_addition = if is_partial { + index.documents_partial_addition() + } else { + index.documents_addition() }; - schema.set_primary_key(&id).map_err(Error::bad_request)?; + for document in body.into_inner() { + document_addition.update_document(document); + } - data.db.main_write(|w| index.main.put_schema(w, &schema))?; - } - - let mut document_addition = if is_partial { - index.documents_partial_addition() - } else { - index.documents_addition() - }; - - for document in body.into_inner() { - document_addition.update_document(document); - } - - let update_id = data.db.update_write(|w| document_addition.finalize(w))?; - - Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) + Ok(data.db.update_write(|w| document_addition.finalize(w))?) + })?; + return Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))); } #[post("/indexes/{index_uid}/documents", wrap = "Authentication::Private")] diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index 77067d9dd..3ec273008 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -53,13 +53,12 @@ async fn update_all( path: web::Path, body: web::Json, ) -> Result { - let settings = body - .into_inner() - .to_update() - .map_err(Error::bad_request)?; - - let update_id = data.db.update_write::<_, _, Error>(|writer| { - update_all_settings_txn(&data, settings, &path.index_uid, writer) + let update_id = data.get_or_create_index(&path.index_uid, |index| { + Ok(data.db.update_write::<_, _, ResponseError>(|writer| { + let settings = body.into_inner().to_update().map_err(Error::bad_request)?; + let update_id = index.settings_update(writer, settings)?; + Ok(update_id) + })?) })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) @@ -71,11 +70,7 @@ pub fn get_all_sync(data: &web::Data, reader: &MainReader, index_uid: &str .open_index(index_uid) .ok_or(Error::index_not_found(index_uid))?; - let stop_words: BTreeSet = index - .main - .stop_words(reader)? - .into_iter() - .collect(); + let stop_words: BTreeSet = index.main.stop_words(&reader)?.into_iter().collect(); let synonyms_list = index.main.synonyms(reader)?; @@ -94,22 +89,19 @@ pub fn get_all_sync(data: &web::Data, reader: &MainReader, index_uid: &str .map(|r| r.to_string()) .collect(); - - let schema = index.main.schema(reader)?; + let schema = index.main.schema(&reader)?; let distinct_attribute = match (index.main.distinct_attribute(reader)?, &schema) { (Some(id), Some(schema)) => schema.name(id).map(str::to_string), _ => None, }; - let attributes_for_faceting = match (&schema, &index.main.attributes_for_faceting(reader)?) { - (Some(schema), Some(attrs)) => { - attrs - .iter() - .filter_map(|&id| schema.name(id)) - .map(str::to_string) - .collect() - } + let attributes_for_faceting = match (&schema, &index.main.attributes_for_faceting(&reader)?) { + (Some(schema), Some(attrs)) => attrs + .iter() + .filter_map(|&id| schema.name(id)) + .map(str::to_string) + .collect(), _ => vec![], }; @@ -159,7 +151,9 @@ async fn delete_all( attributes_for_faceting: UpdateState::Clear, }; - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + let update_id = data + .db + .update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -209,7 +203,9 @@ async fn update_rules( }; let settings = settings.to_update().map_err(Error::bad_request)?; - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + let update_id = data + .db + .update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -232,7 +228,9 @@ async fn delete_rules( ..SettingsUpdate::default() }; - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + let update_id = data + .db + .update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -280,7 +278,9 @@ async fn update_distinct( }; let settings = settings.to_update().map_err(Error::bad_request)?; - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + let update_id = data + .db + .update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -303,7 +303,9 @@ async fn delete_distinct( ..SettingsUpdate::default() }; - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + let update_id = data + .db + .update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -322,8 +324,7 @@ async fn get_searchable( .ok_or(Error::index_not_found(&path.index_uid))?; let reader = data.db.main_read_txn()?; let schema = index.main.schema(&reader)?; - let searchable_attributes: Option> = - schema.as_ref().map(get_indexed_attributes); + let searchable_attributes: Option> = schema.as_ref().map(get_indexed_attributes); Ok(HttpResponse::Ok().json(searchable_attributes)) } @@ -349,7 +350,9 @@ async fn update_searchable( let settings = settings.to_update().map_err(Error::bad_request)?; - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + let update_id = data + .db + .update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -372,7 +375,9 @@ async fn delete_searchable( ..SettingsUpdate::default() }; - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + let update_id = data + .db + .update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -418,7 +423,9 @@ async fn update_displayed( }; let settings = settings.to_update().map_err(Error::bad_request)?; - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + let update_id = data + .db + .update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -441,7 +448,9 @@ async fn delete_displayed( ..SettingsUpdate::default() }; - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + let update_id = data + .db + .update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -459,20 +468,16 @@ async fn get_attributes_for_faceting( .open_index(&path.index_uid) .ok_or(Error::index_not_found(&path.index_uid))?; - let attributes_for_faceting = data - .db - .main_read::<_, _, ResponseError>(|reader| { + let attributes_for_faceting = data.db.main_read::<_, _, ResponseError>(|reader| { let schema = index.main.schema(reader)?; let attrs = index.main.attributes_for_faceting(reader)?; let attr_names = match (&schema, &attrs) { - (Some(schema), Some(attrs)) => { - attrs - .iter() - .filter_map(|&id| schema.name(id)) - .map(str::to_string) - .collect() - } - _ => vec![] + (Some(schema), Some(attrs)) => attrs + .iter() + .filter_map(|&id| schema.name(id)) + .map(str::to_string) + .collect(), + _ => vec![], }; Ok(attr_names) })?; @@ -500,7 +505,9 @@ async fn update_attributes_for_faceting( }; let settings = settings.to_update().map_err(Error::bad_request)?; - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + let update_id = data + .db + .update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -523,7 +530,9 @@ async fn delete_attributes_for_faceting( ..SettingsUpdate::default() }; - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + let update_id = data + .db + .update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -532,7 +541,8 @@ fn get_indexed_attributes(schema: &Schema) -> Vec { if schema.is_indexed_all() { ["*"].iter().map(|s| s.to_string()).collect() } else { - schema.indexed_name() + schema + .indexed_name() .iter() .map(|s| s.to_string()) .collect() @@ -543,7 +553,8 @@ fn get_displayed_attributes(schema: &Schema) -> HashSet { if schema.is_displayed_all() { ["*"].iter().map(|s| s.to_string()).collect() } else { - schema.displayed_name() + schema + .displayed_name() .iter() .map(|s| s.to_string()) .collect() From 17f71a1a5579721926d12108c87f3ef8e399a4be Mon Sep 17 00:00:00 2001 From: qdequele Date: Tue, 8 Sep 2020 19:23:09 +0200 Subject: [PATCH 5/6] add lazy create index on settings handlers --- meilisearch-http/src/routes/setting.rs | 115 ++++++++++------------ meilisearch-http/src/routes/stop_words.rs | 23 ++--- meilisearch-http/src/routes/synonym.rs | 23 ++--- 3 files changed, 74 insertions(+), 87 deletions(-) diff --git a/meilisearch-http/src/routes/setting.rs b/meilisearch-http/src/routes/setting.rs index 3ec273008..43ffb167b 100644 --- a/meilisearch-http/src/routes/setting.rs +++ b/meilisearch-http/src/routes/setting.rs @@ -192,20 +192,17 @@ async fn update_rules( path: web::Path, body: web::Json>>, ) -> Result { - let index = data - .db - .open_index(&path.index_uid) - .ok_or(Error::index_not_found(&path.index_uid))?; + let update_id = data.get_or_create_index(&path.index_uid, |index| { + let settings = Settings { + ranking_rules: Some(body.into_inner()), + ..Settings::default() + }; - let settings = Settings { - ranking_rules: Some(body.into_inner()), - ..Settings::default() - }; - - let settings = settings.to_update().map_err(Error::bad_request)?; - let update_id = data - .db - .update_write(|w| index.settings_update(w, settings))?; + let settings = settings.to_update().map_err(Error::bad_request)?; + Ok(data + .db + .update_write(|w| index.settings_update(w, settings))?) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -267,20 +264,17 @@ async fn update_distinct( path: web::Path, body: web::Json>, ) -> Result { - let index = data - .db - .open_index(&path.index_uid) - .ok_or(Error::index_not_found(&path.index_uid))?; + let update_id = data.get_or_create_index(&path.index_uid, |index| { + let settings = Settings { + distinct_attribute: Some(body.into_inner()), + ..Settings::default() + }; - let settings = Settings { - distinct_attribute: Some(body.into_inner()), - ..Settings::default() - }; - - let settings = settings.to_update().map_err(Error::bad_request)?; - let update_id = data - .db - .update_write(|w| index.settings_update(w, settings))?; + let settings = settings.to_update().map_err(Error::bad_request)?; + Ok(data + .db + .update_write(|w| index.settings_update(w, settings))?) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -338,21 +332,18 @@ async fn update_searchable( path: web::Path, body: web::Json>>, ) -> Result { - let index = data - .db - .open_index(&path.index_uid) - .ok_or(Error::index_not_found(&path.index_uid))?; + let update_id = data.get_or_create_index(&path.index_uid, |index| { + let settings = Settings { + searchable_attributes: Some(body.into_inner()), + ..Settings::default() + }; - let settings = Settings { - searchable_attributes: Some(body.into_inner()), - ..Settings::default() - }; + let settings = settings.to_update().map_err(Error::bad_request)?; - let settings = settings.to_update().map_err(Error::bad_request)?; - - let update_id = data - .db - .update_write(|w| index.settings_update(w, settings))?; + Ok(data + .db + .update_write(|w| index.settings_update(w, settings))?) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -412,20 +403,17 @@ async fn update_displayed( path: web::Path, body: web::Json>>, ) -> Result { - let index = data - .db - .open_index(&path.index_uid) - .ok_or(Error::index_not_found(&path.index_uid))?; + let update_id = data.get_or_create_index(&path.index_uid, |index| { + let settings = Settings { + displayed_attributes: Some(body.into_inner()), + ..Settings::default() + }; - let settings = Settings { - displayed_attributes: Some(body.into_inner()), - ..Settings::default() - }; - - let settings = settings.to_update().map_err(Error::bad_request)?; - let update_id = data - .db - .update_write(|w| index.settings_update(w, settings))?; + let settings = settings.to_update().map_err(Error::bad_request)?; + Ok(data + .db + .update_write(|w| index.settings_update(w, settings))?) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -494,20 +482,17 @@ async fn update_attributes_for_faceting( path: web::Path, body: web::Json>>, ) -> Result { - let index = data - .db - .open_index(&path.index_uid) - .ok_or(Error::index_not_found(&path.index_uid))?; + let update_id = data.get_or_create_index(&path.index_uid, |index| { + let settings = Settings { + attributes_for_faceting: Some(body.into_inner()), + ..Settings::default() + }; - let settings = Settings { - attributes_for_faceting: Some(body.into_inner()), - ..Settings::default() - }; - - let settings = settings.to_update().map_err(Error::bad_request)?; - let update_id = data - .db - .update_write(|w| index.settings_update(w, settings))?; + let settings = settings.to_update().map_err(Error::bad_request)?; + Ok(data + .db + .update_write(|w| index.settings_update(w, settings))?) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/stop_words.rs b/meilisearch-http/src/routes/stop_words.rs index 858b91705..c757b4d14 100644 --- a/meilisearch-http/src/routes/stop_words.rs +++ b/meilisearch-http/src/routes/stop_words.rs @@ -39,17 +39,16 @@ async fn update( path: web::Path, body: web::Json>, ) -> Result { - let index = data - .db - .open_index(&path.index_uid) - .ok_or(Error::index_not_found(&path.index_uid))?; + let update_id = data.get_or_create_index(&path.index_uid, |index| { + let settings = SettingsUpdate { + stop_words: UpdateState::Update(body.into_inner()), + ..SettingsUpdate::default() + }; - let settings = SettingsUpdate { - stop_words: UpdateState::Update(body.into_inner()), - ..SettingsUpdate::default() - }; - - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + Ok(data + .db + .update_write(|w| index.settings_update(w, settings))?) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -72,7 +71,9 @@ async fn delete( ..SettingsUpdate::default() }; - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + let update_id = data + .db + .update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } diff --git a/meilisearch-http/src/routes/synonym.rs b/meilisearch-http/src/routes/synonym.rs index fd0488534..5aefaaca5 100644 --- a/meilisearch-http/src/routes/synonym.rs +++ b/meilisearch-http/src/routes/synonym.rs @@ -50,17 +50,16 @@ async fn update( path: web::Path, body: web::Json>>, ) -> Result { - let index = data - .db - .open_index(&path.index_uid) - .ok_or(Error::index_not_found(&path.index_uid))?; + let update_id = data.get_or_create_index(&path.index_uid, |index| { + let settings = SettingsUpdate { + synonyms: UpdateState::Update(body.into_inner()), + ..SettingsUpdate::default() + }; - let settings = SettingsUpdate { - synonyms: UpdateState::Update(body.into_inner()), - ..SettingsUpdate::default() - }; - - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + Ok(data + .db + .update_write(|w| index.settings_update(w, settings))?) + })?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } @@ -83,7 +82,9 @@ async fn delete( ..SettingsUpdate::default() }; - let update_id = data.db.update_write(|w| index.settings_update(w, settings))?; + let update_id = data + .db + .update_write(|w| index.settings_update(w, settings))?; Ok(HttpResponse::Accepted().json(IndexUpdateResponse::with_id(update_id))) } From a5a47911d176f492ff7a368d1c977499025820ac Mon Sep 17 00:00:00 2001 From: qdequele Date: Thu, 10 Sep 2020 13:44:15 +0200 Subject: [PATCH 6/6] add tests --- Cargo.lock | 2 +- meilisearch-http/src/routes/document.rs | 4 +- meilisearch-http/tests/common.rs | 92 +++- meilisearch-http/tests/documents_add.rs | 67 --- meilisearch-http/tests/lazy_index_creation.rs | 446 ++++++++++++++++++ 5 files changed, 527 insertions(+), 84 deletions(-) create mode 100644 meilisearch-http/tests/lazy_index_creation.rs diff --git a/Cargo.lock b/Cargo.lock index 196deb1bb..3dfc74f7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1923,7 +1923,7 @@ dependencies = [ [[package]] name = "pest" version = "2.1.3" -source = "git+https://github.com/MarinPostma/pest.git?tag=meilisearch-patch1#e1031ad0134d5e9893c470dbea50811b2b746926" +source = "git+https://github.com/pest-parser/pest.git?rev=51fd1d49f1041f7839975664ef71fe15c7dcaf67#51fd1d49f1041f7839975664ef71fe15c7dcaf67" dependencies = [ "ucd-trie", ] diff --git a/meilisearch-http/src/routes/document.rs b/meilisearch-http/src/routes/document.rs index f565c1e6b..e8fa0f646 100644 --- a/meilisearch-http/src/routes/document.rs +++ b/meilisearch-http/src/routes/document.rs @@ -3,14 +3,14 @@ use std::collections::{BTreeSet, HashSet}; use actix_web::{delete, get, post, put}; use actix_web::{web, HttpResponse}; use indexmap::IndexMap; -use meilisearch_core::{update, Index}; +use meilisearch_core::{update, MainReader}; use serde_json::Value; use serde::Deserialize; +use crate::Data; use crate::error::{Error, ResponseError}; use crate::helpers::Authentication; use crate::routes::{IndexParam, IndexUpdateResponse}; -use crate::Data; type Document = IndexMap; diff --git a/meilisearch-http/tests/common.rs b/meilisearch-http/tests/common.rs index da2bc39ad..0cdd3b618 100644 --- a/meilisearch-http/tests/common.rs +++ b/meilisearch-http/tests/common.rs @@ -15,15 +15,24 @@ use meilisearch_http::option::Opt; #[macro_export] macro_rules! test_post_get_search { ($server:expr, $query:expr, |$response:ident, $status_code:ident | $block:expr) => { - let post_query: meilisearch_http::routes::search::SearchQueryPost = serde_json::from_str(&$query.clone().to_string()).unwrap(); + let post_query: meilisearch_http::routes::search::SearchQueryPost = + serde_json::from_str(&$query.clone().to_string()).unwrap(); let get_query: meilisearch_http::routes::search::SearchQuery = post_query.into(); let get_query = ::serde_url_params::to_string(&get_query).unwrap(); let ($response, $status_code) = $server.search_get(&get_query).await; - let _ =::std::panic::catch_unwind(|| $block) - .map_err(|e| panic!("panic in get route: {:?}", e.downcast_ref::<&str>().unwrap())); + let _ = ::std::panic::catch_unwind(|| $block).map_err(|e| { + panic!( + "panic in get route: {:?}", + e.downcast_ref::<&str>().unwrap() + ) + }); let ($response, $status_code) = $server.search_post($query).await; - let _ = ::std::panic::catch_unwind(|| $block) - .map_err(|e| panic!("panic in post route: {:?}", e.downcast_ref::<&str>().unwrap())); + let _ = ::std::panic::catch_unwind(|| $block).map_err(|e| { + panic!( + "panic in post route: {:?}", + e.downcast_ref::<&str>().unwrap() + ) + }); }; } @@ -61,7 +70,6 @@ impl Server { } pub async fn test_server() -> Self { - let mut server = Self::with_uid("test"); let body = json!({ @@ -151,7 +159,8 @@ impl Server { pub async fn get_request(&mut self, url: &str) -> (Value, StatusCode) { eprintln!("get_request: {}", url); - let mut app = test::init_service(meilisearch_http::create_app(&self.data).wrap(NormalizePath)).await; + let mut app = + test::init_service(meilisearch_http::create_app(&self.data).wrap(NormalizePath)).await; let req = test::TestRequest::get().uri(url).to_request(); let res = test::call_service(&mut app, req).await; @@ -165,7 +174,8 @@ impl Server { pub async fn post_request(&self, url: &str, body: Value) -> (Value, StatusCode) { eprintln!("post_request: {}", url); - let mut app = test::init_service(meilisearch_http::create_app(&self.data).wrap(NormalizePath)).await; + let mut app = + test::init_service(meilisearch_http::create_app(&self.data).wrap(NormalizePath)).await; let req = test::TestRequest::post() .uri(url) @@ -183,8 +193,7 @@ impl Server { eprintln!("post_request_async: {}", url); let (response, status_code) = self.post_request(url, body).await; - // eprintln!("response: {}", response); - assert_eq!(status_code, 202); + eprintln!("response: {}", response); assert!(response["updateId"].as_u64().is_some()); self.wait_update_id(response["updateId"].as_u64().unwrap()) .await; @@ -194,7 +203,8 @@ impl Server { pub async fn put_request(&mut self, url: &str, body: Value) -> (Value, StatusCode) { eprintln!("put_request: {}", url); - let mut app = test::init_service(meilisearch_http::create_app(&self.data).wrap(NormalizePath)).await; + let mut app = + test::init_service(meilisearch_http::create_app(&self.data).wrap(NormalizePath)).await; let req = test::TestRequest::put() .uri(url) @@ -222,7 +232,8 @@ impl Server { pub async fn delete_request(&mut self, url: &str) -> (Value, StatusCode) { eprintln!("delete_request: {}", url); - let mut app = test::init_service(meilisearch_http::create_app(&self.data).wrap(NormalizePath)).await; + let mut app = + test::init_service(meilisearch_http::create_app(&self.data).wrap(NormalizePath)).await; let req = test::TestRequest::delete().uri(url).to_request(); let res = test::call_service(&mut app, req).await; @@ -340,9 +351,9 @@ impl Server { self.delete_request_async(&url).await } - pub async fn delete_multiple_documents(&mut self, body: Value) { + pub async fn delete_multiple_documents(&mut self, body: Value) -> (Value, StatusCode) { let url = format!("/indexes/{}/documents/delete-batch", self.uid); - self.post_request_async(&url, body).await; + self.post_request_async(&url, body).await } pub async fn get_all_settings(&mut self) -> (Value, StatusCode) { @@ -355,6 +366,11 @@ impl Server { self.post_request_async(&url, body).await; } + pub async fn update_all_settings_sync(&mut self, body: Value) -> (Value, StatusCode) { + let url = format!("/indexes/{}/settings", self.uid); + self.post_request(&url, body).await + } + pub async fn delete_all_settings(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings", self.uid); self.delete_request_async(&url).await @@ -390,6 +406,11 @@ impl Server { self.post_request_async(&url, body).await; } + pub async fn update_distinct_attribute_sync(&mut self, body: Value) -> (Value, StatusCode) { + let url = format!("/indexes/{}/settings/distinct-attribute", self.uid); + self.post_request(&url, body).await + } + pub async fn delete_distinct_attribute(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/distinct-attribute", self.uid); self.delete_request_async(&url).await @@ -410,6 +431,11 @@ impl Server { self.post_request_async(&url, body).await; } + pub async fn update_searchable_attributes_sync(&mut self, body: Value) -> (Value, StatusCode) { + let url = format!("/indexes/{}/settings/searchable-attributes", self.uid); + self.post_request(&url, body).await + } + pub async fn delete_searchable_attributes(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/searchable-attributes", self.uid); self.delete_request_async(&url).await @@ -425,11 +451,39 @@ impl Server { self.post_request_async(&url, body).await; } + pub async fn update_displayed_attributes_sync(&mut self, body: Value) -> (Value, StatusCode) { + let url = format!("/indexes/{}/settings/displayed-attributes", self.uid); + self.post_request(&url, body).await + } + pub async fn delete_displayed_attributes(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/displayed-attributes", self.uid); self.delete_request_async(&url).await } + pub async fn get_attributes_for_faceting(&mut self) -> (Value, StatusCode) { + let url = format!("/indexes/{}/settings/attributes-for-faceting", self.uid); + self.get_request(&url).await + } + + pub async fn update_attributes_for_faceting(&mut self, body: Value) { + let url = format!("/indexes/{}/settings/attributes-for-faceting", self.uid); + self.post_request_async(&url, body).await; + } + + pub async fn update_attributes_for_faceting_sync( + &mut self, + body: Value, + ) -> (Value, StatusCode) { + let url = format!("/indexes/{}/settings/attributes-for-faceting", self.uid); + self.post_request(&url, body).await + } + + pub async fn delete_attributes_for_faceting(&mut self) -> (Value, StatusCode) { + let url = format!("/indexes/{}/settings/attributes-for-faceting", self.uid); + self.delete_request_async(&url).await + } + pub async fn get_synonyms(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/synonyms", self.uid); self.get_request(&url).await @@ -440,6 +494,11 @@ impl Server { self.post_request_async(&url, body).await; } + pub async fn update_synonyms_sync(&mut self, body: Value) -> (Value, StatusCode) { + let url = format!("/indexes/{}/settings/synonyms", self.uid); + self.post_request(&url, body).await + } + pub async fn delete_synonyms(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/synonyms", self.uid); self.delete_request_async(&url).await @@ -455,6 +514,11 @@ impl Server { self.post_request_async(&url, body).await; } + pub async fn update_stop_words_sync(&mut self, body: Value) -> (Value, StatusCode) { + let url = format!("/indexes/{}/settings/stop-words", self.uid); + self.post_request(&url, body).await + } + pub async fn delete_stop_words(&mut self) -> (Value, StatusCode) { let url = format!("/indexes/{}/settings/stop-words", self.uid); self.delete_request_async(&url).await diff --git a/meilisearch-http/tests/documents_add.rs b/meilisearch-http/tests/documents_add.rs index 140a194c3..382a1ed43 100644 --- a/meilisearch-http/tests/documents_add.rs +++ b/meilisearch-http/tests/documents_add.rs @@ -220,70 +220,3 @@ async fn documents_with_same_id_are_overwritten() { "test2" ); } - -#[actix_rt::test] -async fn create_index_lazy_by_pushing_documents() { - let mut server = common::Server::with_uid("movies"); - - // 1 - Add documents - - let body = json!([{ - "title": "Test", - "comment": "comment test" - }]); - - let url = "/indexes/movies/documents?primaryKey=title"; - let (response, status_code) = server.post_request(&url, body).await; - eprintln!("{:#?}", response); - assert_eq!(status_code, 202); - let update_id = response["updateId"].as_u64().unwrap(); - server.wait_update_id(update_id).await; - - // 3 - Check update success - - let (response, status_code) = server.get_update_status(update_id).await; - assert_eq!(status_code, 200); - assert_eq!(response["status"], "processed"); -} - -#[actix_rt::test] -async fn create_index_lazy_by_pushing_documents_and_discover_pk() { - let mut server = common::Server::with_uid("movies"); - - // 1 - Add documents - - let body = json!([{ - "id": 1, - "title": "Test", - "comment": "comment test" - }]); - - let url = "/indexes/movies/documents"; - let (response, status_code) = server.post_request(&url, body).await; - eprintln!("{:#?}", response); - assert_eq!(status_code, 202); - let update_id = response["updateId"].as_u64().unwrap(); - server.wait_update_id(update_id).await; - - // 3 - Check update success - - let (response, status_code) = server.get_update_status(update_id).await; - assert_eq!(status_code, 200); - assert_eq!(response["status"], "processed"); -} - -#[actix_rt::test] -async fn create_index_lazy_by_pushing_documents_with_wwrong_name() { - let mut server = common::Server::with_uid("wrong&name"); - - let body = json!([{ - "title": "Test", - "comment": "comment test" - }]); - - let url = "/indexes/wrong&name/documents?primaryKey=title"; - let (response, status_code) = server.post_request(&url, body).await; - eprintln!("{:#?}", response); - assert_eq!(status_code, 400); - assert_eq!(response["errorCode"], "invalid_index_uid"); -} diff --git a/meilisearch-http/tests/lazy_index_creation.rs b/meilisearch-http/tests/lazy_index_creation.rs new file mode 100644 index 000000000..6730db82e --- /dev/null +++ b/meilisearch-http/tests/lazy_index_creation.rs @@ -0,0 +1,446 @@ +use serde_json::json; + +mod common; + +#[actix_rt::test] +async fn create_index_lazy_by_pushing_documents() { + let mut server = common::Server::with_uid("movies"); + + // 1 - Add documents + + let body = json!([{ + "title": "Test", + "comment": "comment test" + }]); + + let url = "/indexes/movies/documents?primaryKey=title"; + let (response, status_code) = server.post_request(&url, body).await; + assert_eq!(status_code, 202); + let update_id = response["updateId"].as_u64().unwrap(); + server.wait_update_id(update_id).await; + + // 3 - Check update success + + let (response, status_code) = server.get_update_status(update_id).await; + assert_eq!(status_code, 200); + assert_eq!(response["status"], "processed"); +} + +#[actix_rt::test] +async fn create_index_lazy_by_pushing_documents_and_discover_pk() { + let mut server = common::Server::with_uid("movies"); + + // 1 - Add documents + + let body = json!([{ + "id": 1, + "title": "Test", + "comment": "comment test" + }]); + + let url = "/indexes/movies/documents"; + let (response, status_code) = server.post_request(&url, body).await; + assert_eq!(status_code, 202); + let update_id = response["updateId"].as_u64().unwrap(); + server.wait_update_id(update_id).await; + + // 3 - Check update success + + let (response, status_code) = server.get_update_status(update_id).await; + assert_eq!(status_code, 200); + assert_eq!(response["status"], "processed"); +} + +#[actix_rt::test] +async fn create_index_lazy_by_pushing_documents_with_wrong_name() { + let server = common::Server::with_uid("wrong&name"); + + let body = json!([{ + "title": "Test", + "comment": "comment test" + }]); + + let url = "/indexes/wrong&name/documents?primaryKey=title"; + let (response, status_code) = server.post_request(&url, body).await; + assert_eq!(status_code, 400); + assert_eq!(response["errorCode"], "invalid_index_uid"); +} + +#[actix_rt::test] +async fn create_index_lazy_add_documents_failed() { + let mut server = common::Server::with_uid("wrong&name"); + + let body = json!([{ + "title": "Test", + "comment": "comment test" + }]); + + let url = "/indexes/wrong&name/documents"; + let (response, status_code) = server.post_request(&url, body).await; + assert_eq!(status_code, 400); + assert_eq!(response["errorCode"], "invalid_index_uid"); + + let (_, status_code) = server.get_index().await; + assert_eq!(status_code, 404); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_settings() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!({ + "rankingRules": [ + "typo", + "words", + "proximity", + "attribute", + "wordsPosition", + "exactness", + "desc(registered)", + "desc(age)", + ], + "distinctAttribute": "id", + "searchableAttributes": [ + "id", + "name", + "color", + "gender", + "email", + "phone", + "address", + "registered", + "about" + ], + "displayedAttributes": [ + "name", + "gender", + "email", + "registered", + "age", + ], + "stopWords": [ + "ad", + "in", + "ut", + ], + "synonyms": { + "road": ["street", "avenue"], + "street": ["avenue"], + }, + "attributesForFaceting": ["name"], + }); + + server.update_all_settings(body.clone()).await; + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 200); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_settings_with_error() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!({ + "rankingRules": [ + "other", + "words", + "proximity", + "attribute", + "wordsPosition", + "exactness", + "desc(registered)", + "desc(age)", + ], + "distinctAttribute": "id", + "searchableAttributes": [ + "id", + "name", + "color", + "gender", + "email", + "phone", + "address", + "registered", + "about" + ], + "displayedAttributes": [ + "name", + "gender", + "email", + "registered", + "age", + ], + "stopWords": [ + "ad", + "in", + "ut", + ], + "synonyms": { + "road": ["street", "avenue"], + "street": ["avenue"], + }, + "anotherSettings": ["name"], + }); + + let (_, status_code) = server.update_all_settings_sync(body.clone()).await; + assert_eq!(status_code, 400); + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 404); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_ranking_rules() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!([ + "typo", + "words", + "proximity", + "attribute", + "wordsPosition", + "exactness", + "desc(registered)", + "desc(age)", + ]); + + server.update_ranking_rules(body.clone()).await; + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 200); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_ranking_rules_with_error() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!({ + "rankingRules": 123, + }); + + let (_, status_code) = server.update_ranking_rules_sync(body.clone()).await; + assert_eq!(status_code, 400); + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 404); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_distinct_attribute() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!("type"); + + server.update_distinct_attribute(body.clone()).await; + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 200); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_distinct_attribute_with_error() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!(123); + + let (resp, status_code) = server.update_distinct_attribute_sync(body.clone()).await; + eprintln!("resp: {:?}", resp); + assert_eq!(status_code, 400); + + // 3 - Get all settings and compare to the previous one + + let (resp, status_code) = server.get_all_settings().await; + eprintln!("resp: {:?}", resp); + assert_eq!(status_code, 404); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_searchable_attributes() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!(["title", "description"]); + + server.update_searchable_attributes(body.clone()).await; + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 200); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_searchable_attributes_with_error() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!(123); + + let (_, status_code) = server.update_searchable_attributes_sync(body.clone()).await; + assert_eq!(status_code, 400); + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 404); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_displayed_attributes() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!(["title", "description"]); + + server.update_displayed_attributes(body.clone()).await; + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 200); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_displayed_attributes_with_error() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!(123); + + let (_, status_code) = server.update_displayed_attributes_sync(body.clone()).await; + assert_eq!(status_code, 400); + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 404); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_attributes_for_faceting() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!(["title", "description"]); + + server.update_attributes_for_faceting(body.clone()).await; + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 200); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_attributes_for_faceting_with_error() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!(123); + + let (_, status_code) = server + .update_attributes_for_faceting_sync(body.clone()) + .await; + assert_eq!(status_code, 400); + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 404); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_synonyms() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!({ + "road": ["street", "avenue"], + "street": ["avenue"], + }); + + server.update_synonyms(body.clone()).await; + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 200); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_synonyms_with_error() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!(123); + + let (_, status_code) = server.update_synonyms_sync(body.clone()).await; + assert_eq!(status_code, 400); + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 404); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_stop_words() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!(["le", "la", "les"]); + + server.update_stop_words(body.clone()).await; + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 200); +} + +#[actix_rt::test] +async fn create_index_lazy_by_sending_stop_words_with_error() { + let mut server = common::Server::with_uid("movies"); + // 2 - Send the settings + + let body = json!(123); + + let (_, status_code) = server.update_stop_words_sync(body.clone()).await; + assert_eq!(status_code, 400); + + // 3 - Get all settings and compare to the previous one + + let (_, status_code) = server.get_all_settings().await; + + assert_eq!(status_code, 404); +}