diff --git a/meilisearch-types/src/locales.rs b/meilisearch-types/src/locales.rs index 8c15fe528..c6902dd71 100644 --- a/meilisearch-types/src/locales.rs +++ b/meilisearch-types/src/locales.rs @@ -40,19 +40,7 @@ macro_rules! make_locale { impl std::fmt::Display for LocaleFormatError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let valid_locales = [$(Locale::$language),+].iter().map(|l| format!("`{}`", json!(l).as_str().unwrap())).collect::>().join(", "); - write!(f, "Unknown value `{}`, expected one of {}", self.invalid_locale, valid_locales) - } - } - - impl std::error::Error for LocaleFormatError {} - - impl std::str::FromStr for Locale { - type Err = LocaleFormatError; - - fn from_str(s: &str) -> Result { - milli::tokenizer::Language::from_code(s).map(Self::from).ok_or(LocaleFormatError { - invalid_locale: s.to_string(), - }) + write!(f, "Unsupported locale `{}`, expected one of {}", self.invalid_locale, valid_locales) } } }; @@ -130,6 +118,18 @@ make_locale! { Hye } +impl std::error::Error for LocaleFormatError {} + +impl std::str::FromStr for Locale { + type Err = LocaleFormatError; + + fn from_str(s: &str) -> Result { + milli::tokenizer::Language::from_code(s) + .map(Self::from) + .ok_or(LocaleFormatError { invalid_locale: s.to_string() }) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Deserr, Serialize, Deserialize)] #[deserr(rename_all = camelCase)] #[serde(rename_all = "camelCase")] diff --git a/meilisearch/tests/dumps/mod.rs b/meilisearch/tests/dumps/mod.rs index 92f72fe78..ea98e200f 100644 --- a/meilisearch/tests/dumps/mod.rs +++ b/meilisearch/tests/dumps/mod.rs @@ -78,7 +78,8 @@ async fn import_dump_v1_movie_raw() { "pagination": { "maxTotalHits": 1000 }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "### ); @@ -240,7 +241,8 @@ async fn import_dump_v1_movie_with_settings() { "pagination": { "maxTotalHits": 1000 }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "### ); @@ -388,7 +390,8 @@ async fn import_dump_v1_rubygems_with_settings() { "pagination": { "maxTotalHits": 1000 }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "### ); @@ -522,7 +525,8 @@ async fn import_dump_v2_movie_raw() { "pagination": { "maxTotalHits": 1000 }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "### ); @@ -668,7 +672,8 @@ async fn import_dump_v2_movie_with_settings() { "pagination": { "maxTotalHits": 1000 }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "### ); @@ -813,7 +818,8 @@ async fn import_dump_v2_rubygems_with_settings() { "pagination": { "maxTotalHits": 1000 }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "### ); @@ -947,7 +953,8 @@ async fn import_dump_v3_movie_raw() { "pagination": { "maxTotalHits": 1000 }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "### ); @@ -1093,7 +1100,8 @@ async fn import_dump_v3_movie_with_settings() { "pagination": { "maxTotalHits": 1000 }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "### ); @@ -1238,7 +1246,8 @@ async fn import_dump_v3_rubygems_with_settings() { "pagination": { "maxTotalHits": 1000 }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "### ); @@ -1372,7 +1381,8 @@ async fn import_dump_v4_movie_raw() { "pagination": { "maxTotalHits": 1000 }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "### ); @@ -1518,7 +1528,8 @@ async fn import_dump_v4_movie_with_settings() { "pagination": { "maxTotalHits": 1000 }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "### ); @@ -1663,7 +1674,8 @@ async fn import_dump_v4_rubygems_with_settings() { "pagination": { "maxTotalHits": 1000 }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "### ); @@ -1909,7 +1921,8 @@ async fn import_dump_v6_containing_experimental_features() { "pagination": { "maxTotalHits": 1000 }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "###); @@ -2087,7 +2100,8 @@ async fn generate_and_import_dump_containing_vectors() { "documentTemplate": "{{doc.doggo}}" } }, - "searchCutoffMs": null + "searchCutoffMs": null, + "localizedAttributes": null } "###); diff --git a/meilisearch/tests/search/locales.rs b/meilisearch/tests/search/locales.rs index 722694ba3..9f1c22b75 100644 --- a/meilisearch/tests/search/locales.rs +++ b/meilisearch/tests/search/locales.rs @@ -103,12 +103,41 @@ async fn simple_search() { // english index - .search(json!({"q": "Atta", "attributesToRetrieve": ["id"]}), |response, code| { + .search(json!({"q": "Atta", "attributesToHighlight": ["*"]}), |response, code| { snapshot!(response, @r###" { "hits": [ { - "id": 852 + "name_en": "Attack on Titan", + "name_ja": "進撃の巨人", + "author_en": "Hajime Isayama", + "author_ja": "諫山 創", + "description_en": "Attack on Titan is a Japanese manga series written and illustrated by Hajime Isayama", + "description_ja": "進撃の巨人は、日本の漫画シリーズであり、諫山 創によって作画されている。", + "id": 852, + "_vectors": { + "manual": [ + 1.0, + 2.0, + 3.0 + ] + }, + "_formatted": { + "name_en": "Attack on Titan", + "name_ja": "進撃の巨人", + "author_en": "Hajime Isayama", + "author_ja": "諫山 創", + "description_en": "Attack on Titan is a Japanese manga series written and illustrated by Hajime Isayama", + "description_ja": "進撃の巨人は、日本の漫画シリーズであり、諫山 創によって作画されている。", + "id": "852", + "_vectors": { + "manual": [ + "1.0", + "2.0", + "3.0" + ] + } + } } ], "query": "Atta", @@ -124,12 +153,35 @@ async fn simple_search() { // japanese index - .search(json!({"q": "進撃", "attributesToRetrieve": ["id"]}), |response, code| { + .search(json!({"q": "進撃", "attributesToHighlight": ["*"]}), |response, code| { snapshot!(response, @r###" { "hits": [ { - "id": 853 + "name_zh": "进击的巨人", + "author_zh": "諫山創", + "description_zh": "进击的巨人是日本的漫画系列,由諫山 創作画。", + "id": 853, + "_vectors": { + "manual": [ + 1.0, + 2.0, + 3.0 + ] + }, + "_formatted": { + "name_zh": "进击的巨人", + "author_zh": "諫山創", + "description_zh": "进击的巨人是日本的漫画系列,由諫山 創作画。", + "id": "853", + "_vectors": { + "manual": [ + "1.0", + "2.0", + "3.0" + ] + } + } } ], "query": "進撃", @@ -145,25 +197,77 @@ async fn simple_search() { index .search( - json!({"q": "進撃", "attributesToRetrieve": ["id"], "locales": ["jpn"]}), + json!({"q": "進撃", "locales": ["jpn"], "attributesToHighlight": ["*"]}), |response, code| { snapshot!(response, @r###" - { - "hits": [ { - "id": 852 - }, - { - "id": 853 + "hits": [ + { + "name_en": "Attack on Titan", + "name_ja": "進撃の巨人", + "author_en": "Hajime Isayama", + "author_ja": "諫山 創", + "description_en": "Attack on Titan is a Japanese manga series written and illustrated by Hajime Isayama", + "description_ja": "進撃の巨人は、日本の漫画シリーズであり、諫山 創によって作画されている。", + "id": 852, + "_vectors": { + "manual": [ + 1.0, + 2.0, + 3.0 + ] + }, + "_formatted": { + "name_en": "Attack on Titan", + "name_ja": "進撃の巨人", + "author_en": "Hajime Isayama", + "author_ja": "諫山 創", + "description_en": "Attack on Titan is a Japanese manga series written and illustrated by Hajime Isayama", + "description_ja": "進撃の巨人は、日本の漫画シリーズであり、諫山 創によって作画されている。", + "id": "852", + "_vectors": { + "manual": [ + "1.0", + "2.0", + "3.0" + ] + } + } + }, + { + "name_zh": "进击的巨人", + "author_zh": "諫山創", + "description_zh": "进击的巨人是日本的漫画系列,由諫山 創作画。", + "id": 853, + "_vectors": { + "manual": [ + 1.0, + 2.0, + 3.0 + ] + }, + "_formatted": { + "name_zh": "进击的巨人", + "author_zh": "諫山創", + "description_zh": "进击的巨人是日本的漫画系列,由諫山 創作画。", + "id": "853", + "_vectors": { + "manual": [ + "1.0", + "2.0", + "3.0" + ] + } + } + } + ], + "query": "進撃", + "processingTimeMs": "[duration]", + "limit": 20, + "offset": 0, + "estimatedTotalHits": 2 } - ], - "query": "進撃", - "processingTimeMs": "[duration]", - "limit": 20, - "offset": 0, - "estimatedTotalHits": 2 - } - "###); + "###); snapshot!(code, @"200 OK"); }, ) @@ -171,15 +275,67 @@ async fn simple_search() { // chinese index - .search(json!({"q": "进击", "attributesToRetrieve": ["id"]}), |response, code| { + .search(json!({"q": "进击", "attributesToHighlight": ["*"]}), |response, code| { snapshot!(response, @r###" { "hits": [ { - "id": 853 + "name_zh": "进击的巨人", + "author_zh": "諫山創", + "description_zh": "进击的巨人是日本的漫画系列,由諫山 創作画。", + "id": 853, + "_vectors": { + "manual": [ + 1.0, + 2.0, + 3.0 + ] + }, + "_formatted": { + "name_zh": "进击的巨人", + "author_zh": "諫山創", + "description_zh": "进击的巨人是日本的漫画系列,由諫山 創作画。", + "id": "853", + "_vectors": { + "manual": [ + "1.0", + "2.0", + "3.0" + ] + } + } }, { - "id": 852 + "name_en": "Attack on Titan", + "name_ja": "進撃の巨人", + "author_en": "Hajime Isayama", + "author_ja": "諫山 創", + "description_en": "Attack on Titan is a Japanese manga series written and illustrated by Hajime Isayama", + "description_ja": "進撃の巨人は、日本の漫画シリーズであり、諫山 創によって作画されている。", + "id": 852, + "_vectors": { + "manual": [ + 1.0, + 2.0, + 3.0 + ] + }, + "_formatted": { + "name_en": "Attack on Titan", + "name_ja": "進撃の巨人", + "author_en": "Hajime Isayama", + "author_ja": "諫山 創", + "description_en": "Attack on Titan is a Japanese manga series written and illustrated by Hajime Isayama", + "description_ja": "進撃の巨人は、日本の漫画シリーズであり、諫山 創によって作画されている。", + "id": "852", + "_vectors": { + "manual": [ + "1.0", + "2.0", + "3.0" + ] + } + } } ], "query": "进击", @@ -226,7 +382,7 @@ async fn force_locales() { // chinese detection index .search( - json!({"q": "\"进击的巨人\"", "attributesToRetrieve": ["id"]}), + json!({"q": "\"进击的巨人\"", "attributesToHighlight": ["*"]}), |response, code| { snapshot!(response, @r###" { @@ -246,13 +402,36 @@ async fn force_locales() { // force japanese index .search( - json!({"q": "\"进击的巨人\"", "attributesToRetrieve": ["id"], "locales": ["jpn"]}), + json!({"q": "\"进击的巨人\"", "locales": ["jpn"], "attributesToHighlight": ["*"]}), |response, code| { snapshot!(response, @r###" { "hits": [ { - "id": 853 + "name_zh": "进击的巨人", + "author_zh": "諫山創", + "description_zh": "进击的巨人是日本的漫画系列,由諫山 創作画。", + "id": 853, + "_vectors": { + "manual": [ + 1.0, + 2.0, + 3.0 + ] + }, + "_formatted": { + "name_zh": "巨人", + "author_zh": "諫山創", + "description_zh": "巨人是日本的漫画系列,由諫山 創作画。", + "id": "853", + "_vectors": { + "manual": [ + "1.0", + "2.0", + "3.0" + ] + } + } } ], "query": "\"进击的巨人\"", @@ -300,7 +479,7 @@ async fn force_locales_with_pattern() { // chinese detection index .search( - json!({"q": "\"进击的巨人\"", "attributesToRetrieve": ["id"]}), + json!({"q": "\"进击的巨人\"", "attributesToHighlight": ["*"]}), |response, code| { snapshot!(response, @r###" { @@ -320,13 +499,36 @@ async fn force_locales_with_pattern() { // force japanese index .search( - json!({"q": "\"进击的巨人\"", "attributesToRetrieve": ["id"], "locales": ["jpn"]}), + json!({"q": "\"进击的巨人\"", "locales": ["jpn"], "attributesToHighlight": ["*"]}), |response, code| { snapshot!(response, @r###" { "hits": [ { - "id": 853 + "name_zh": "进击的巨人", + "author_zh": "諫山創", + "description_zh": "进击的巨人是日本的漫画系列,由諫山 創作画。", + "id": 853, + "_vectors": { + "manual": [ + 1.0, + 2.0, + 3.0 + ] + }, + "_formatted": { + "name_zh": "巨人", + "author_zh": "諫山創", + "description_zh": "巨人是日本的漫画系列,由諫山 創作画。", + "id": "853", + "_vectors": { + "manual": [ + "1.0", + "2.0", + "3.0" + ] + } + } } ], "query": "\"进击的巨人\"", @@ -372,7 +574,7 @@ async fn force_locales_with_pattern_nested() { // chinese index .search( - json!({"q": "\"进击的巨人\"", "attributesToRetrieve": ["id"], "locales": ["cmn"]}), + json!({"q": "\"进击的巨人\"", "locales": ["cmn"], "attributesToHighlight": ["*"]}), |response, code| { snapshot!(response, @r###" { @@ -392,13 +594,60 @@ async fn force_locales_with_pattern_nested() { // force japanese index .search( - json!({"q": "\"进击的巨人\"", "attributesToRetrieve": ["id"], "locales": ["jpn"]}), + json!({"q": "\"进击的巨人\"", "locales": ["jpn"], "attributesToHighlight": ["*"]}), |response, code| { snapshot!(response, @r###" { "hits": [ { - "id": 852 + "document_en": { + "name": "Attack on Titan", + "description": "Attack on Titan is a Japanese manga series written and illustrated by Hajime Isayama", + "author": "Hajime Isayama" + }, + "document_ja": { + "name": "進撃の巨人", + "description": "進撃の巨人は、日本の漫画シリーズであり、諫山 創によって作画されている。", + "author": "諫山 創" + }, + "document_zh": { + "name": "进击的巨人", + "description": "进击的巨人是日本的漫画系列,由諫山 創作画。", + "author": "諫山創" + }, + "id": 852, + "_vectors": { + "manual": [ + 1.0, + 2.0, + 3.0 + ] + }, + "_formatted": { + "document_en": { + "name": "Attack on Titan", + "description": "Attack on Titan is a Japanese manga series written and illustrated by Hajime Isayama", + "author": "Hajime Isayama" + }, + "document_ja": { + "name": "進撃の巨人", + "description": "進撃の巨人は、日本の漫画シリーズであり、諫山 創によって作画されている。", + "author": "諫山 創" + }, + "document_zh": { + "name": "巨人", + "description": "巨人是日本的漫画系列,由諫山 創作画。", + "author": "諫山創" + }, + "id": "852", + "_vectors": { + "manual": [ + "1.0", + "2.0", + "3.0" + ] + } + } } ], "query": "\"进击的巨人\"", @@ -413,6 +662,357 @@ async fn force_locales_with_pattern_nested() { ) .await; } +#[actix_rt::test] +async fn force_different_locales_with_pattern() { + let server = Server::new().await; + + let index = server.index("test"); + let documents = DOCUMENTS.clone(); + let (response, _) = index + .update_settings( + json!({ + "searchableAttributes": ["name_en", "name_ja", "name_zh", "author_en", "author_ja", "author_zh", "description_en", "description_ja", "description_zh"], + "localizedAttributes": [ + // force japanese + {"attributePatterns": ["*_zh"], "locales": ["jpn"]}, + // force chinese + {"attributePatterns": ["*_ja"], "locales": ["cmn"]} + ] + }), + ) + .await; + snapshot!(response, @r###" + { + "taskUid": 0, + "indexUid": "test", + "status": "enqueued", + "type": "settingsUpdate", + "enqueuedAt": "[date]" + } + "###); + index.add_documents(documents, None).await; + index.wait_task(1).await; + + // force chinese + index + .search( + json!({"q": "\"进击的巨人\"", "locales": ["cmn"], "attributesToHighlight": ["*"]}), + |response, code| { + snapshot!(response, @r###" + { + "hits": [], + "query": "\"进击的巨人\"", + "processingTimeMs": "[duration]", + "limit": 20, + "offset": 0, + "estimatedTotalHits": 0 + } + "###); + snapshot!(code, @"200 OK"); + }, + ) + .await; + + // force japanese + index + .search( + json!({"q": "\"进击的巨人\"", "locales": ["jpn"], "attributesToHighlight": ["*"]}), + |response, code| { + snapshot!(response, @r###" + { + "hits": [ + { + "name_zh": "进击的巨人", + "author_zh": "諫山創", + "description_zh": "进击的巨人是日本的漫画系列,由諫山 創作画。", + "id": 853, + "_vectors": { + "manual": [ + 1.0, + 2.0, + 3.0 + ] + }, + "_formatted": { + "name_zh": "巨人", + "author_zh": "諫山創", + "description_zh": "巨人是日本的漫画系列,由諫山 創作画。", + "id": "853", + "_vectors": { + "manual": [ + "1.0", + "2.0", + "3.0" + ] + } + } + } + ], + "query": "\"进击的巨人\"", + "processingTimeMs": "[duration]", + "limit": 20, + "offset": 0, + "estimatedTotalHits": 1 + } + "###); + snapshot!(code, @"200 OK"); + }, + ) + .await; +} + +#[actix_rt::test] +async fn force_different_locales_with_pattern_nested() { + let server = Server::new().await; + + let index = server.index("test"); + let documents = NESTED_DOCUMENTS.clone(); + let (response, _) = index + .update_settings(json!({ + "searchableAttributes": ["document_en", "document_ja", "document_zh"], + "localizedAttributes": [ + // force japanese + {"attributePatterns": ["*_zh.*"], "locales": ["jpn"]}, + // force chinese + {"attributePatterns": ["document_ja.*", "document_zh.*"], "locales": ["cmn"]} + ] + })) + .await; + snapshot!(response, @r###" + { + "taskUid": 0, + "indexUid": "test", + "status": "enqueued", + "type": "settingsUpdate", + "enqueuedAt": "[date]" + } + "###); + index.add_documents(documents, None).await; + index.wait_task(1).await; + + // chinese + index + .search( + json!({"q": "\"进击的巨人\"", "locales": ["cmn"], "attributesToHighlight": ["*"]}), + |response, code| { + snapshot!(response, @r###" + { + "hits": [], + "query": "\"进击的巨人\"", + "processingTimeMs": "[duration]", + "limit": 20, + "offset": 0, + "estimatedTotalHits": 0 + } + "###); + snapshot!(code, @"200 OK"); + }, + ) + .await; + + // force japanese + index + .search( + json!({"q": "\"进击的巨人\"", "locales": ["jpn"], "attributesToHighlight": ["*"]}), + |response, code| { + snapshot!(response, @r###" + { + "hits": [ + { + "document_en": { + "name": "Attack on Titan", + "description": "Attack on Titan is a Japanese manga series written and illustrated by Hajime Isayama", + "author": "Hajime Isayama" + }, + "document_ja": { + "name": "進撃の巨人", + "description": "進撃の巨人は、日本の漫画シリーズであり、諫山 創によって作画されている。", + "author": "諫山 創" + }, + "document_zh": { + "name": "进击的巨人", + "description": "进击的巨人是日本的漫画系列,由諫山 創作画。", + "author": "諫山創" + }, + "id": 852, + "_vectors": { + "manual": [ + 1.0, + 2.0, + 3.0 + ] + }, + "_formatted": { + "document_en": { + "name": "Attack on Titan", + "description": "Attack on Titan is a Japanese manga series written and illustrated by Hajime Isayama", + "author": "Hajime Isayama" + }, + "document_ja": { + "name": "進撃の巨人", + "description": "進撃の巨人は、日本の漫画シリーズであり、諫山 創によって作画されている。", + "author": "諫山 創" + }, + "document_zh": { + "name": "巨人", + "description": "巨人是日本的漫画系列,由諫山 創作画。", + "author": "諫山創" + }, + "id": "852", + "_vectors": { + "manual": [ + "1.0", + "2.0", + "3.0" + ] + } + } + } + ], + "query": "\"进击的巨人\"", + "processingTimeMs": "[duration]", + "limit": 20, + "offset": 0, + "estimatedTotalHits": 1 + } + "###); + snapshot!(code, @"200 OK"); + }, + ) + .await; +} + +#[actix_rt::test] +async fn settings_change() { + let server = Server::new().await; + + let index = server.index("test"); + let documents = NESTED_DOCUMENTS.clone(); + index.add_documents(documents, None).await; + index.wait_task(0).await; + let (response, _) = index + .update_settings(json!({ + "searchableAttributes": ["document_en", "document_ja", "document_zh"], + "localizedAttributes": [ + // force japanese + {"attributePatterns": ["document_ja.*", "*_zh.*"], "locales": ["jpn"]} + ] + })) + .await; + snapshot!(response, @r###" + { + "taskUid": 1, + "indexUid": "test", + "status": "enqueued", + "type": "settingsUpdate", + "enqueuedAt": "[date]" + } + "###); + index.wait_task(1).await; + + // chinese + index + .search( + json!({"q": "\"进击的巨人\"", "locales": ["cmn"], "attributesToHighlight": ["*"]}), + |response, code| { + snapshot!(response, @r###" + { + "hits": [], + "query": "\"进击的巨人\"", + "processingTimeMs": "[duration]", + "limit": 20, + "offset": 0, + "estimatedTotalHits": 0 + } + "###); + snapshot!(code, @"200 OK"); + }, + ) + .await; + + // force japanese + index + .search( + json!({"q": "\"进击的巨人\"", "locales": ["jpn"], "attributesToHighlight": ["*"]}), + |response, code| { + snapshot!(response, @r###" + { + "hits": [], + "query": "\"进击的巨人\"", + "processingTimeMs": "[duration]", + "limit": 20, + "offset": 0, + "estimatedTotalHits": 0 + } + "###); + snapshot!(code, @"200 OK"); + }, + ) + .await; + + // change settings + let (response, _) = index + .update_settings(json!({ + "searchableAttributes": ["document_en", "document_ja", "document_zh"], + "localizedAttributes": [ + // force japanese + {"attributePatterns": ["*_zh.*"], "locales": ["jpn"]}, + // force chinese + {"attributePatterns": ["document_ja.*"], "locales": ["cmn"]} + ] + })) + .await; + snapshot!(response, @r###" + { + "taskUid": 2, + "indexUid": "test", + "status": "enqueued", + "type": "settingsUpdate", + "enqueuedAt": "[date]" + } + "###); + index.wait_task(2).await; + + // chinese + index + .search( + json!({"q": "\"进击的巨人\"", "locales": ["cmn"], "attributesToHighlight": ["*"]}), + |response, code| { + snapshot!(response, @r###" + { + "hits": [], + "query": "\"进击的巨人\"", + "processingTimeMs": "[duration]", + "limit": 20, + "offset": 0, + "estimatedTotalHits": 0 + } + "###); + snapshot!(code, @"200 OK"); + }, + ) + .await; + + // force japanese + index + .search( + json!({"q": "\"进击的巨人\"", "locales": ["jpn"], "attributesToHighlight": ["*"]}), + |response, code| { + snapshot!(response, @r###" + { + "hits": [], + "query": "\"进击的巨人\"", + "processingTimeMs": "[duration]", + "limit": 20, + "offset": 0, + "estimatedTotalHits": 0 + } + "###); + snapshot!(code, @"200 OK"); + }, + ) + .await; +} #[actix_rt::test] async fn invalid_locales() { @@ -428,9 +1028,7 @@ async fn invalid_locales() { index.add_documents(documents, None).await; index.wait_task(1).await; - let (response, code) = index - .search_post(json!({"q": "Atta", "attributesToRetrieve": ["id"], "locales": ["invalid"]})) - .await; + let (response, code) = index.search_post(json!({"q": "Atta", "locales": ["invalid"]})).await; snapshot!(code, @"400 Bad Request"); snapshot!(json_string!(response), @r###" { @@ -442,17 +1040,12 @@ async fn invalid_locales() { "###); let (response, code) = index - .search_get( - &yaup::to_string( - &json!({"q": "Atta", "attributesToRetrieve": ["id"], "locales": ["invalid"]}), - ) - .unwrap(), - ) + .search_get(&yaup::to_string(&json!({"q": "Atta", "locales": ["invalid"]})).unwrap()) .await; snapshot!(code, @"400 Bad Request"); snapshot!(json_string!(response), @r###" { - "message": "Invalid value in parameter `locales`: Unknown value `invalid`, expected one of `epo`, `eng`, `rus`, `cmn`, `spa`, `por`, `ita`, `ben`, `fra`, `deu`, `ukr`, `kat`, `ara`, `hin`, `jpn`, `heb`, `yid`, `pol`, `amh`, `jav`, `kor`, `nob`, `dan`, `swe`, `fin`, `tur`, `nld`, `hun`, `ces`, `ell`, `bul`, `bel`, `mar`, `kan`, `ron`, `slv`, `hrv`, `srp`, `mkd`, `lit`, `lav`, `est`, `tam`, `vie`, `urd`, `tha`, `guj`, `uzb`, `pan`, `aze`, `ind`, `tel`, `pes`, `mal`, `ori`, `mya`, `nep`, `sin`, `khm`, `tuk`, `aka`, `zul`, `sna`, `afr`, `lat`, `slk`, `cat`, `tgl`, `hye`", + "message": "Invalid value in parameter `locales`: Unsupported locale `invalid`, expected one of `epo`, `eng`, `rus`, `cmn`, `spa`, `por`, `ita`, `ben`, `fra`, `deu`, `ukr`, `kat`, `ara`, `hin`, `jpn`, `heb`, `yid`, `pol`, `amh`, `jav`, `kor`, `nob`, `dan`, `swe`, `fin`, `tur`, `nld`, `hun`, `ces`, `ell`, `bul`, `bel`, `mar`, `kan`, `ron`, `slv`, `hrv`, `srp`, `mkd`, `lit`, `lav`, `est`, `tam`, `vie`, `urd`, `tha`, `guj`, `uzb`, `pan`, `aze`, `ind`, `tel`, `pes`, `mal`, `ori`, `mya`, `nep`, `sin`, `khm`, `tuk`, `aka`, `zul`, `sna`, `afr`, `lat`, `slk`, `cat`, `tgl`, `hye`", "code": "invalid_search_locales", "type": "invalid_request", "link": "https://docs.meilisearch.com/errors#invalid_search_locales" diff --git a/milli/src/index.rs b/milli/src/index.rs index f5342f2c0..3a2f3169c 100644 --- a/milli/src/index.rs +++ b/milli/src/index.rs @@ -1558,7 +1558,7 @@ impl Index { rtxn: &RoTxn<'_>, ) -> heed::Result>> { self.main - .remap_types::>>() + .remap_types::>>() .get(rtxn, main_key::LOCALIZED_ATTRIBUTES_RULES) } @@ -1567,7 +1567,7 @@ impl Index { txn: &mut RwTxn<'_>, val: Vec, ) -> heed::Result<()> { - self.main.remap_types::>>().put( + self.main.remap_types::>>().put( txn, main_key::LOCALIZED_ATTRIBUTES_RULES, &val, diff --git a/milli/src/localized_attributes_rules.rs b/milli/src/localized_attributes_rules.rs index aa4eddee1..739d03043 100644 --- a/milli/src/localized_attributes_rules.rs +++ b/milli/src/localized_attributes_rules.rs @@ -71,6 +71,8 @@ impl LocalizedFieldIds { for rule in rules { if rule.match_str(field_name) { locales.extend(rule.locales.iter()); + // Take the first rule that matches + break; } } diff --git a/milli/src/search/facet/search.rs b/milli/src/search/facet/search.rs index 6ef62e39a..39fb7374a 100644 --- a/milli/src/search/facet/search.rs +++ b/milli/src/search/facet/search.rs @@ -346,5 +346,5 @@ fn normalize_facet_string(facet_string: &str, locales: Option<&[Language]>) -> S ..Default::default() }; - token.normalize(&options).lemma.to_string() + token.normalize(&options).lemma.into_owned() } diff --git a/milli/src/update/settings.rs b/milli/src/update/settings.rs index 2cac2777d..e423852f1 100644 --- a/milli/src/update/settings.rs +++ b/milli/src/update/settings.rs @@ -1128,22 +1128,21 @@ impl<'a, 't, 'i> Settings<'a, 't, 'i> { Ok(changed) } - fn update_localized_attributes_rules(&mut self) -> Result { - let changed = match &self.localized_attributes_rules { + fn update_localized_attributes_rules(&mut self) -> Result<()> { + match &self.localized_attributes_rules { Setting::Set(new) => { let old = self.index.localized_attributes_rules(self.wtxn)?; - if old.as_ref() == Some(new) { - false - } else { + if old.as_ref() != Some(new) { self.index.put_localized_attributes_rules(self.wtxn, new.clone())?; - true } } - Setting::Reset => self.index.delete_localized_attributes_rules(self.wtxn)?, - Setting::NotSet => false, - }; + Setting::Reset => { + self.index.delete_localized_attributes_rules(self.wtxn)?; + } + Setting::NotSet => (), + } - Ok(changed) + Ok(()) } pub fn execute(mut self, progress_callback: FP, should_abort: FA) -> Result<()>