From 58a1124e9a496c3072d55bc19312620e755f5fa7 Mon Sep 17 00:00:00 2001 From: Irevoire Date: Tue, 19 Apr 2022 16:49:38 +0200 Subject: [PATCH] fix(search): formatted field --- meilisearch-http/tests/search/formatted.rs | 376 +++++++++++++++++++++ meilisearch-http/tests/search/mod.rs | 5 +- meilisearch-lib/src/index/search.rs | 19 +- 3 files changed, 392 insertions(+), 8 deletions(-) create mode 100644 meilisearch-http/tests/search/formatted.rs diff --git a/meilisearch-http/tests/search/formatted.rs b/meilisearch-http/tests/search/formatted.rs new file mode 100644 index 000000000..13b8a07d8 --- /dev/null +++ b/meilisearch-http/tests/search/formatted.rs @@ -0,0 +1,376 @@ +use super::*; +use crate::common::Server; +use serde_json::json; + +#[actix_rt::test] +async fn formatted_contain_wildcard() { + let server = Server::new().await; + let index = server.index("test"); + + index + .update_settings(json!({ "displayedAttributes": ["id", "cattos"] })) + .await; + + let documents = NESTED_DOCUMENTS.clone(); + index.add_documents(documents, None).await; + index.wait_task(1).await; + + let (response, code) = index + .search_post(json!({ "q": "pesti", "attributesToRetrieve": ["father", "mother"], "attributesToHighlight": ["father", "mother", "*"], "attributesToCrop": ["doggos"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "_formatted": { + "id": "852", + "cattos": "pesti", + } + }) + ); + + let (response, code) = index + .search_post(json!({ "q": "pesti", "attributesToRetrieve": ["*"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "id": 852, + "cattos": "pesti", + }) + ); + + let (response, code) = index + .search_post( + json!({ "q": "pesti", "attributesToRetrieve": ["*"], "attributesToHighlight": ["id"] }), + ) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "id": 852, + "cattos": "pesti", + "_formatted": { + "id": "852", + "cattos": "pesti", + } + }) + ); + + let (response, code) = index + .search_post( + json!({ "q": "pesti", "attributesToRetrieve": ["*"], "attributesToCrop": ["*"] }), + ) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "id": 852, + "cattos": "pesti", + "_formatted": { + "id": "852", + "cattos": "pesti", + } + }) + ); + + let (response, code) = index + .search_post(json!({ "q": "pesti", "attributesToCrop": ["*"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "id": 852, + "cattos": "pesti", + "_formatted": { + "id": "852", + "cattos": "pesti", + } + }) + ); +} + +#[actix_rt::test] +async fn format_nested() { + 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, code) = index + .search_post(json!({ "q": "pesti", "attributesToRetrieve": ["doggos"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "doggos": [ + { + "name": "bobby", + "age": 2, + }, + { + "name": "buddy", + "age": 4, + }, + ], + }) + ); + + let (response, code) = index + .search_post(json!({ "q": "pesti", "attributesToRetrieve": ["doggos.name"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "doggos": [ + { + "name": "bobby", + }, + { + "name": "buddy", + }, + ], + }) + ); + + let (response, code) = index + .search_post(json!({ "q": "pesti", "attributesToRetrieve": [], "attributesToHighlight": ["doggos.name"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "_formatted": { + "doggos": [ + { + "name": "bobby", + }, + { + "name": "buddy", + }, + ], + }, + }) + ); + + let (response, code) = index + .search_post(json!({ "q": "pesti", "attributesToRetrieve": [], "attributesToCrop": ["doggos.name"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "_formatted": { + "doggos": [ + { + "name": "bobby", + }, + { + "name": "buddy", + }, + ], + }, + }) + ); + + let (response, code) = index + .search_post(json!({ "q": "pesti", "attributesToRetrieve": ["doggos.name"], "attributesToHighlight": ["doggos.age"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "doggos": [ + { + "name": "bobby", + }, + { + "name": "buddy", + }, + ], + "_formatted": { + "doggos": [ + { + "name": "bobby", + "age": "2", + }, + { + "name": "buddy", + "age": "4", + }, + ], + }, + }) + ); + + let (response, code) = index + .search_post(json!({ "q": "pesti", "attributesToRetrieve": [], "attributesToHighlight": ["doggos.age"], "attributesToCrop": ["doggos.name"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "_formatted": { + "doggos": [ + { + "name": "bobby", + "age": "2", + }, + { + "name": "buddy", + "age": "4", + }, + ], + }, + }) + ); +} + +#[actix_rt::test] +async fn displayedattr_2_smol() { + let server = Server::new().await; + let index = server.index("test"); + + // not enough displayed for the other settings + index + .update_settings(json!({ "displayedAttributes": ["id"] })) + .await; + + let documents = NESTED_DOCUMENTS.clone(); + index.add_documents(documents, None).await; + index.wait_task(1).await; + + let (response, code) = index + .search_post(json!({ "attributesToRetrieve": ["father", "id"], "attributesToHighlight": ["mother"], "attributesToCrop": ["cattos"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "id": 852, + }) + ); + + let (response, code) = index + .search_post(json!({ "attributesToRetrieve": ["id"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "id": 852, + }) + ); + + let (response, code) = index + .search_post(json!({ "attributesToHighlight": ["id"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "id": 852, + "_formatted": { + "id": "852", + } + }) + ); + + let (response, code) = index + .search_post(json!({ "attributesToCrop": ["id"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "id": 852, + "_formatted": { + "id": "852", + } + }) + ); + + let (response, code) = index + .search_post(json!({ "attributesToHighlight": ["id"], "attributesToCrop": ["id"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "id": 852, + "_formatted": { + "id": "852", + } + }) + ); + + let (response, code) = index + .search_post(json!({ "attributesToHighlight": ["cattos"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "id": 852, + }) + ); + + let (response, code) = index + .search_post(json!({ "attributesToCrop": ["cattos"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "id": 852, + }) + ); + + let (response, code) = index + .search_post(json!({ "attributesToRetrieve": ["cattos"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!(response["hits"][0], json!({})); + + let (response, code) = index + .search_post( + json!({ "attributesToRetrieve": ["cattos"], "attributesToHighlight": ["cattos"], "attributesToCrop": ["cattos"] }), + ) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!(response["hits"][0], json!({})); + + let (response, code) = index + .search_post(json!({ "attributesToRetrieve": ["cattos"], "attributesToHighlight": ["id"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "_formatted": { + "id": "852", + } + }) + ); + + let (response, code) = index + .search_post(json!({ "attributesToRetrieve": ["cattos"], "attributesToCrop": ["id"] })) + .await; + assert_eq!(code, 200, "{}", response); + assert_eq!( + response["hits"][0], + json!({ + "_formatted": { + "id": "852", + } + }) + ); +} diff --git a/meilisearch-http/tests/search/mod.rs b/meilisearch-http/tests/search/mod.rs index 65ea67a70..3353f16c7 100644 --- a/meilisearch-http/tests/search/mod.rs +++ b/meilisearch-http/tests/search/mod.rs @@ -2,12 +2,13 @@ // should be tested in its own module to isolate tests and keep the tests readable. mod errors; +mod formatted; use crate::common::Server; use once_cell::sync::Lazy; use serde_json::{json, Value}; -static DOCUMENTS: Lazy = Lazy::new(|| { +pub(self) static DOCUMENTS: Lazy = Lazy::new(|| { json!([ { "title": "Shazam!", @@ -32,7 +33,7 @@ static DOCUMENTS: Lazy = Lazy::new(|| { ]) }); -static NESTED_DOCUMENTS: Lazy = Lazy::new(|| { +pub(self) static NESTED_DOCUMENTS: Lazy = Lazy::new(|| { json!([ { "id": 852, diff --git a/meilisearch-lib/src/index/search.rs b/meilisearch-lib/src/index/search.rs index ea542ec10..7c12f985e 100644 --- a/meilisearch-lib/src/index/search.rs +++ b/meilisearch-lib/src/index/search.rs @@ -232,14 +232,22 @@ impl Index { let documents_iter = self.documents(&rtxn, documents_ids)?; for (_id, obkv) in documents_iter { - let mut document = make_document(&to_retrieve_ids, &fields_ids_map, obkv)?; + // First generate a document with all the displayed fields + let displayed_document = make_document(&displayed_ids, &fields_ids_map, obkv)?; + + // select the attributes to retrieve + let attributes_to_retrieve = to_retrieve_ids + .iter() + .map(|&fid| fields_ids_map.name(fid).expect("Missing field name")); + let mut document = + permissive_json_pointer::select_values(&displayed_document, attributes_to_retrieve); let matches_info = query .matches .then(|| compute_matches(&matching_words, &document, &analyzer)); let formatted = format_fields( - &document, + &displayed_document, &fields_ids_map, &formatter, &matching_words, @@ -475,7 +483,7 @@ fn add_non_formatted_ids_to_formatted_options( } fn make_document( - attributes_to_retrieve: &BTreeSet, + displayed_attributes: &BTreeSet, field_ids_map: &FieldsIdsMap, obkv: obkv::KvReaderU16, ) -> Result { @@ -493,11 +501,11 @@ fn make_document( } // select the attributes to retrieve - let attributes_to_retrieve = attributes_to_retrieve + let displayed_attributes = displayed_attributes .iter() .map(|&fid| field_ids_map.name(fid).expect("Missing field name")); - let document = permissive_json_pointer::select_values(&document, attributes_to_retrieve); + let document = permissive_json_pointer::select_values(&document, displayed_attributes); Ok(document) } @@ -514,7 +522,6 @@ fn format_fields>( // before. .map(|&fid| field_ids_map.name(fid).unwrap()) .collect(); - let mut document = permissive_json_pointer::select_values(document, selectors.iter().copied()); permissive_json_pointer::map_leaf_values(&mut document, selectors, |key, value| {