diff --git a/quickwit/quickwit-integration-tests/src/tests/update_tests/doc_mapping_tests.rs b/quickwit/quickwit-integration-tests/src/tests/update_tests/doc_mapping_tests.rs index 9751ebb7cfe..41d4e9913d2 100644 --- a/quickwit/quickwit-integration-tests/src/tests/update_tests/doc_mapping_tests.rs +++ b/quickwit/quickwit-integration-tests/src/tests/update_tests/doc_mapping_tests.rs @@ -23,7 +23,7 @@ use std::time::Duration; use quickwit_config::service::QuickwitService; use serde_json::{json, Value}; -use super::assert_hit_count; +use super::assert_hits_unordered; use crate::test_utils::ClusterSandbox; /// Update the doc mapping between 2 calls to local-ingest (forces separate indexing pipelines) and @@ -34,7 +34,7 @@ async fn validate_search_across_doc_mapping_updates( ingest_before_update: &[Value], updated_doc_mapping: Value, ingest_after_update: &[Value], - query_and_expect: &[(&str, Result)], + query_and_expect: &[(&str, Result<&[Value], ()>)], ) { quickwit_common::setup_logging_for_tests(); let nodes_services = vec![HashSet::from_iter([ @@ -123,8 +123,8 @@ async fn validate_search_across_doc_mapping_updates( .await .unwrap(); - for (query, expected_hits) in query_and_expect { - assert_hit_count(&sandbox, index_id, query, *expected_hits).await; + for (query, expected_hits) in query_and_expect.iter().copied() { + assert_hits_unordered(&sandbox, index_id, query, expected_hits).await; } sandbox.shutdown().await.unwrap(); @@ -152,8 +152,8 @@ async fn test_update_doc_mapping_text_to_u64() { updated_doc_mappings, ingest_after_update, &[ - ("body:14", Ok(1)), - ("body:16", Ok(1)), + ("body:14", Ok(&[json!({"body": 14})])), + ("body:16", Ok(&[json!({"body": 16})])), // error expected because the validation is performed // by latest doc mapping ("body:hello", Err(())), @@ -186,9 +186,9 @@ async fn test_update_doc_mapping_u64_to_text() { updated_doc_mappings, ingest_after_update, &[ - ("body:14", Ok(1)), - ("body:16", Ok(1)), - ("body:hello", Ok(1)), + ("body:14", Ok(&[json!({"body": "14"})])), + ("body:16", Ok(&[json!({"body": "16"})])), + ("body:hello", Ok(&[json!({"body": "hello world"})])), ], ) .await; @@ -219,7 +219,7 @@ async fn test_update_doc_mapping_json_to_text() { updated_doc_mappings, ingest_after_update, &[ - ("body:hello", Ok(1)), + ("body:hello", Ok(&[json!({"body": "hello world"})])), // error expected because the validation is performed // by latest doc mapping ("body.field1:hello", Err(())), @@ -262,7 +262,16 @@ async fn test_update_doc_mapping_json_to_object() { ingest_before_update, updated_doc_mappings, ingest_after_update, - &[("body.field1:hello", Ok(1)), ("body.field1:hola", Ok(1))], + &[ + ( + "body.field1:hello", + Ok(&[json!({"body": {"field1": "hello"}})]), + ), + ( + "body.field1:hola", + Ok(&[json!({"body": {"field1": "hola"}})]), + ), + ], ) .await; } @@ -291,16 +300,19 @@ async fn test_update_doc_mapping_tokenizer_default_to_raw() { updated_doc_mappings, ingest_after_update, &[ - ("body:hello", Ok(1)), - ("body:world", Ok(1)), + ("body:hello", Ok(&[json!({"body": "hello-world"})])), + ("body:world", Ok(&[json!({"body": "bonjour-monde"})])), // phrases queries won't apply to older splits that didn't support them - ("body:\"hello world\"", Ok(0)), - ("body:\"hello-world\"", Ok(0)), - ("body:bonjour", Ok(0)), - ("body:monde", Ok(0)), + ("body:\"hello world\"", Ok(&[])), + ("body:\"hello-world\"", Ok(&[])), + ("body:bonjour", Ok(&[])), + ("body:monde", Ok(&[])), // the raw tokenizer only returns exact matches - ("body:\"bonjour monde\"", Ok(0)), - ("body:\"bonjour-monde\"", Ok(1)), + ("body:\"bonjour monde\"", Ok(&[])), + ( + "body:\"bonjour-monde\"", + Ok(&[json!({"body": "bonjour-monde"})]), + ), ], ) .await; @@ -330,15 +342,21 @@ async fn test_update_doc_mapping_tokenizer_add_position() { updated_doc_mappings, ingest_after_update, &[ - ("body:hello", Ok(1)), - ("body:world", Ok(1)), + ("body:hello", Ok(&[json!({"body": "hello-world"})])), + ("body:world", Ok(&[json!({"body": "hello-world"})])), // phrases queries don't apply to older splits that didn't support them - ("body:\"hello-world\"", Ok(0)), - ("body:\"hello world\"", Ok(0)), - ("body:bonjour", Ok(1)), - ("body:monde", Ok(1)), - ("body:\"bonjour-monde\"", Ok(1)), - ("body:\"bonjour monde\"", Ok(1)), + ("body:\"hello-world\"", Ok(&[])), + ("body:\"hello world\"", Ok(&[])), + ("body:bonjour", Ok(&[json!({"body": "bonjour-monde"})])), + ("body:monde", Ok(&[json!({"body": "bonjour-monde"})])), + ( + "body:\"bonjour-monde\"", + Ok(&[json!({"body": "bonjour-monde"})]), + ), + ( + "body:\"bonjour monde\"", + Ok(&[json!({"body": "bonjour-monde"})]), + ), ], ) .await; @@ -366,15 +384,24 @@ async fn test_update_doc_mapping_tokenizer_raw_to_phrase() { updated_doc_mappings, ingest_after_update, &[ - ("body:hello", Ok(0)), - ("body:world", Ok(0)), + ("body:hello", Ok(&[])), + ("body:world", Ok(&[])), // raw tokenizer used here, only exact matches returned - ("body:\"hello-world\"", Ok(1)), - ("body:\"hello world\"", Ok(0)), - ("body:bonjour", Ok(1)), - ("body:monde", Ok(1)), - ("body:\"bonjour-monde\"", Ok(1)), - ("body:\"bonjour monde\"", Ok(1)), + ( + "body:\"hello-world\"", + Ok(&[json!({"body": "hello-world"})]), + ), + ("body:\"hello world\"", Ok(&[])), + ("body:bonjour", Ok(&[json!({"body": "bonjour-monde"})])), + ("body:monde", Ok(&[json!({"body": "bonjour-monde"})])), + ( + "body:\"bonjour-monde\"", + Ok(&[json!({"body": "bonjour-monde"})]), + ), + ( + "body:\"bonjour monde\"", + Ok(&[json!({"body": "bonjour-monde"})]), + ), ], ) .await; @@ -401,9 +428,15 @@ async fn test_update_doc_mapping_strict_to_dynamic() { updated_doc_mappings, ingest_after_update, &[ - ("body:hello", Ok(1)), - ("body:world", Ok(1)), - ("title:salutations", Ok(1)), + ("body:hello", Ok(&[json!({"body": "hello"})])), + ( + "body:world", + Ok(&[json!({"body": "world", "title": "salutations"})]), + ), + ( + "title:salutations", + Ok(&[json!({"body": "world", "title": "salutations"})]), + ), ], ) .await; @@ -429,7 +462,10 @@ async fn test_update_doc_mapping_dynamic_to_strict() { ingest_before_update, updated_doc_mappings, ingest_after_update, - &[("body:hello", Ok(1)), ("body:world", Ok(1))], + &[ + ("body:hello", Ok(&[json!({"body": "hello"})])), + ("body:world", Ok(&[json!({"body": "world"})])), + ], ) .await; } @@ -459,9 +495,15 @@ async fn test_update_doc_mapping_add_field_on_strict() { updated_doc_mappings, ingest_after_update, &[ - ("body:hello", Ok(1)), - ("body:world", Ok(1)), - ("title:salutations", Ok(1)), + ("body:hello", Ok(&[json!({"body": "hello"})])), + ( + "body:world", + Ok(&[json!({"body": "world", "title": "salutations"})]), + ), + ( + "title:salutations", + Ok(&[json!({"body": "world", "title": "salutations"})]), + ), ], ) .await; diff --git a/quickwit/quickwit-integration-tests/src/tests/update_tests/mod.rs b/quickwit/quickwit-integration-tests/src/tests/update_tests/mod.rs index cd411b5345f..4871be449ef 100644 --- a/quickwit/quickwit-integration-tests/src/tests/update_tests/mod.rs +++ b/quickwit/quickwit-integration-tests/src/tests/update_tests/mod.rs @@ -18,14 +18,16 @@ // along with this program. If not, see . use quickwit_serve::SearchRequestQueryString; +use serde_json::Value; use crate::test_utils::ClusterSandbox; -async fn assert_hit_count( +/// Checks that the result of the given query matches the expected values +async fn assert_hits_unordered( sandbox: &ClusterSandbox, index_id: &str, query: &str, - expected_hits: Result, + expected_result: Result<&[Value], ()>, ) { let search_res = sandbox .searcher_rest_client @@ -33,14 +35,29 @@ async fn assert_hit_count( index_id, SearchRequestQueryString { query: query.to_string(), + max_hits: expected_result.map(|hits| hits.len() as u64).unwrap_or(1), ..Default::default() }, ) .await; - if let Ok(expected_hit_count) = expected_hits { + if let Ok(expected_hits) = expected_result { let resp = search_res.unwrap_or_else(|_| panic!("query: {}", query)); assert_eq!(resp.errors.len(), 0, "query: {}", query); - assert_eq!(resp.num_hits, expected_hit_count, "query: {}", query); + assert_eq!( + resp.num_hits, + expected_hits.len() as u64, + "query: {}", + query + ); + for expected_hit in expected_hits { + assert!( + resp.hits.contains(expected_hit), + "query: {} -> expected hits: {:?}, got: {:?}", + query, + expected_hits, + resp.hits + ); + } } else if let Ok(search_response) = search_res { assert!(!search_response.errors.is_empty(), "query: {}", query); } diff --git a/quickwit/quickwit-integration-tests/src/tests/update_tests/search_settings_tests.rs b/quickwit/quickwit-integration-tests/src/tests/update_tests/search_settings_tests.rs index e7b766f8634..52d43627f22 100644 --- a/quickwit/quickwit-integration-tests/src/tests/update_tests/search_settings_tests.rs +++ b/quickwit/quickwit-integration-tests/src/tests/update_tests/search_settings_tests.rs @@ -24,7 +24,7 @@ use quickwit_config::service::QuickwitService; use quickwit_rest_client::rest_client::CommitType; use serde_json::json; -use super::assert_hit_count; +use super::assert_hits_unordered; use crate::ingest_json; use crate::test_utils::{ingest_with_retry, ClusterSandbox}; @@ -103,7 +103,7 @@ async fn test_update_search_settings_on_multi_nodes_cluster() { tokio::time::sleep(Duration::from_secs(4)).await; // No hit because `default_search_fields`` only covers the `title` field - assert_hit_count(&sandbox, "my-updatable-index", "record", Ok(0)).await; + assert_hits_unordered(&sandbox, "my-updatable-index", "record", Ok(&[])).await; // Update the index to also search `body` by default, the same search should // now have 1 hit @@ -131,7 +131,13 @@ async fn test_update_search_settings_on_multi_nodes_cluster() { .await .unwrap(); - assert_hit_count(&sandbox, "my-updatable-index", "record", Ok(1)).await; + assert_hits_unordered( + &sandbox, + "my-updatable-index", + "record", + Ok(&[json!({"title": "first", "body": "first record"})]), + ) + .await; sandbox.shutdown().await.unwrap(); }