Skip to content

Commit 5ae877c

Browse files
committed
Add unit tests and improve some wording
1 parent 26d40cb commit 5ae877c

File tree

7 files changed

+174
-17
lines changed

7 files changed

+174
-17
lines changed

quickwit/quickwit-proto/protos/quickwit/search.proto

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,8 @@ message SearchRequest {
244244

245245
CountHits count_hits = 17;
246246

247-
// When an exact index_id is provided (not a pattern), the query fails if that
248-
// index is missing and this is false.
247+
// When an exact index ID is provided (not a pattern), the query fails only if
248+
// that index is not found and this parameter is set to `false`.
249249
bool ignore_missing_indexes = 18;
250250
}
251251

quickwit/quickwit-proto/src/codegen/quickwit/quickwit.search.rs

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

quickwit/quickwit-search/src/root.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3251,6 +3251,102 @@ mod tests {
32513251
Ok(())
32523252
}
32533253

3254+
#[tokio::test]
3255+
async fn test_root_search_missing_index() -> anyhow::Result<()> {
3256+
let mut mock_metastore = MockMetastoreService::new();
3257+
let index_metadata = IndexMetadata::for_test("test-index1", "ram:///test-index");
3258+
let index_uid = index_metadata.index_uid.clone();
3259+
mock_metastore
3260+
.expect_list_indexes_metadata()
3261+
.returning(move |_index_ids_query| {
3262+
Ok(ListIndexesMetadataResponse::for_test(vec![
3263+
index_metadata.clone(),
3264+
]))
3265+
});
3266+
mock_metastore
3267+
.expect_list_splits()
3268+
.returning(move |_list_splits_request| {
3269+
let splits = vec![
3270+
MockSplitBuilder::new("split1")
3271+
.with_index_uid(&index_uid)
3272+
.build(),
3273+
];
3274+
let splits_response = ListSplitsResponse::try_from_splits(splits).unwrap();
3275+
Ok(ServiceStream::from(vec![Ok(splits_response)]))
3276+
});
3277+
let mock_metastore_client = MetastoreServiceClient::from_mock(mock_metastore);
3278+
let mut mock_search_service = MockSearchService::new();
3279+
mock_search_service.expect_leaf_search().returning(
3280+
|_leaf_search_req: quickwit_proto::search::LeafSearchRequest| {
3281+
Ok(quickwit_proto::search::LeafSearchResponse {
3282+
num_hits: 3,
3283+
partial_hits: vec![
3284+
mock_partial_hit("split1", 3, 1),
3285+
mock_partial_hit("split1", 2, 2),
3286+
mock_partial_hit("split1", 1, 3),
3287+
],
3288+
failed_splits: Vec::new(),
3289+
num_attempted_splits: 1,
3290+
..Default::default()
3291+
})
3292+
},
3293+
);
3294+
mock_search_service.expect_fetch_docs().returning(
3295+
|fetch_docs_req: quickwit_proto::search::FetchDocsRequest| {
3296+
Ok(quickwit_proto::search::FetchDocsResponse {
3297+
hits: get_doc_for_fetch_req(fetch_docs_req),
3298+
})
3299+
},
3300+
);
3301+
let searcher_pool = searcher_pool_for_test([("127.0.0.1:1001", mock_search_service)]);
3302+
let search_job_placer = SearchJobPlacer::new(searcher_pool);
3303+
let cluster_client = ClusterClient::new(search_job_placer.clone());
3304+
3305+
let searcher_context = SearcherContext::for_test();
3306+
3307+
// search with ignore_missing_indexes=true succeeds
3308+
let search_request = quickwit_proto::search::SearchRequest {
3309+
index_id_patterns: vec!["test-index1".to_string(), "test-index2".to_string()],
3310+
query_ast: qast_json_helper("test", &["body"]),
3311+
max_hits: 10,
3312+
ignore_missing_indexes: true,
3313+
..Default::default()
3314+
};
3315+
let search_response = root_search(
3316+
&searcher_context,
3317+
search_request,
3318+
mock_metastore_client.clone(),
3319+
&cluster_client,
3320+
)
3321+
.await
3322+
.unwrap();
3323+
assert_eq!(search_response.num_hits, 3);
3324+
assert_eq!(search_response.hits.len(), 3);
3325+
3326+
// search with ignore_missing_indexes=false fails
3327+
let search_request = quickwit_proto::search::SearchRequest {
3328+
index_id_patterns: vec!["test-index1".to_string(), "test-index2".to_string()],
3329+
query_ast: qast_json_helper("test", &["body"]),
3330+
max_hits: 10,
3331+
ignore_missing_indexes: false,
3332+
..Default::default()
3333+
};
3334+
let search_error = root_search(
3335+
&searcher_context,
3336+
search_request,
3337+
mock_metastore_client,
3338+
&cluster_client,
3339+
)
3340+
.await
3341+
.unwrap_err();
3342+
if let SearchError::IndexesNotFound { index_ids } = search_error {
3343+
assert_eq!(index_ids, vec!["test-index2".to_string()]);
3344+
} else {
3345+
panic!("unexpected error type: {search_error}");
3346+
}
3347+
Ok(())
3348+
}
3349+
32543350
#[tokio::test]
32553351
async fn test_root_search_multiple_splits_retry_on_other_node() -> anyhow::Result<()> {
32563352
let search_request = quickwit_proto::search::SearchRequest {
@@ -4123,6 +4219,69 @@ mod tests {
41234219
Ok(())
41244220
}
41254221

4222+
#[tokio::test]
4223+
async fn test_search_plan_missing_index() -> anyhow::Result<()> {
4224+
let mut mock_metastore = MockMetastoreService::new();
4225+
let index_metadata = IndexMetadata::for_test("test-index1", "ram:///test-index");
4226+
let index_uid = index_metadata.index_uid.clone();
4227+
mock_metastore
4228+
.expect_list_indexes_metadata()
4229+
.returning(move |_index_ids_query| {
4230+
Ok(ListIndexesMetadataResponse::for_test(vec![
4231+
index_metadata.clone(),
4232+
]))
4233+
});
4234+
mock_metastore
4235+
.expect_list_splits()
4236+
.returning(move |_filter| {
4237+
let splits = vec![
4238+
MockSplitBuilder::new("split1")
4239+
.with_index_uid(&index_uid)
4240+
.build(),
4241+
MockSplitBuilder::new("split2")
4242+
.with_index_uid(&index_uid)
4243+
.build(),
4244+
];
4245+
let splits_response = ListSplitsResponse::try_from_splits(splits).unwrap();
4246+
Ok(ServiceStream::from(vec![Ok(splits_response)]))
4247+
});
4248+
let mock_metastore_service = MetastoreServiceClient::from_mock(mock_metastore);
4249+
4250+
// plan with ignore_missing_indexes=true succeeds
4251+
search_plan(
4252+
quickwit_proto::search::SearchRequest {
4253+
index_id_patterns: vec!["test-index1".to_string(), "test-index2".to_string()],
4254+
query_ast: qast_json_helper("test-query", &["body"]),
4255+
max_hits: 10,
4256+
ignore_missing_indexes: true,
4257+
..Default::default()
4258+
},
4259+
mock_metastore_service.clone(),
4260+
)
4261+
.await
4262+
.unwrap();
4263+
4264+
// plan with ignore_missing_indexes=false fails
4265+
let search_error = search_plan(
4266+
quickwit_proto::search::SearchRequest {
4267+
index_id_patterns: vec!["test-index1".to_string(), "test-index2".to_string()],
4268+
query_ast: qast_json_helper("test-query", &["body"]),
4269+
max_hits: 10,
4270+
ignore_missing_indexes: false,
4271+
..Default::default()
4272+
},
4273+
mock_metastore_service.clone(),
4274+
)
4275+
.await
4276+
.unwrap_err();
4277+
if let SearchError::IndexesNotFound { index_ids } = search_error {
4278+
assert_eq!(index_ids, vec!["test-index2".to_string()]);
4279+
} else {
4280+
panic!("unexpected error type: {search_error}");
4281+
}
4282+
Ok(())
4283+
}
4284+
41264285
#[test]
41274286
fn test_extract_timestamp_range_from_ast() {
41284287
use std::ops::Bound;

quickwit/quickwit-serve/src/elasticsearch_api/model/multi_search.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@ pub struct MultiSearchQueryParams {
5151
pub ignore_throttled: Option<bool>,
5252
#[serde(default)]
5353
pub ignore_unavailable: Option<bool>,
54+
/// List of indexes to search.
5455
#[serde_as(deserialize_as = "OneOrMany<_, PreferMany>")]
55-
#[serde(default)]
56-
pub index: Vec<String>,
56+
#[serde(default, rename = "index")]
57+
pub indexes: Vec<String>,
5758
#[serde(default)]
5859
pub max_concurrent_searches: Option<u64>,
5960
#[serde(default)]
@@ -94,8 +95,8 @@ pub struct MultiSearchHeader {
9495
#[serde(default)]
9596
pub ignore_unavailable: Option<bool>,
9697
#[serde_as(deserialize_as = "OneOrMany<_, PreferMany>")]
97-
#[serde(default)]
98-
pub index: Vec<String>,
98+
#[serde(default, rename = "index")]
99+
pub indexes: Vec<String>,
99100
#[serde(default)]
100101
pub preference: Option<String>,
101102
#[serde(default)]
@@ -115,8 +116,8 @@ impl MultiSearchHeader {
115116
if self.ignore_unavailable.is_none() {
116117
self.ignore_unavailable = defaults.ignore_unavailable;
117118
}
118-
if self.index.is_empty() {
119-
self.index = defaults.index.clone();
119+
if self.indexes.is_empty() {
120+
self.indexes = defaults.indexes.clone();
120121
}
121122
if self.routing.is_none() {
122123
self.routing = defaults.routing.clone();

quickwit/quickwit-serve/src/elasticsearch_api/rest_handler.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -825,19 +825,19 @@ async fn es_compat_index_multi_search(
825825
))
826826
})?;
827827
request_header.apply_query_param_defaults(&multi_search_params);
828-
if request_header.index.is_empty() {
828+
if request_header.indexes.is_empty() {
829829
return Err(ElasticsearchError::from(SearchError::InvalidArgument(
830830
"`_msearch` request header must define at least one index".to_string(),
831831
)));
832832
}
833-
for index in &request_header.index {
833+
for index in &request_header.indexes {
834834
validate_index_id_pattern(index, true).map_err(|err| {
835835
SearchError::InvalidArgument(format!(
836836
"request header contains an invalid index: {err}"
837837
))
838838
})?;
839839
}
840-
let index_ids_patterns = request_header.index.clone();
840+
let index_ids_patterns = request_header.indexes.clone();
841841
let search_body = payload_lines
842842
.next()
843843
.ok_or_else(|| {

quickwit/rest-api-tests/scenarii/es_compatibility/0025-msearch.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,4 @@ expected:
125125
- hits:
126126
total:
127127
value: 0
128-
status: 200
128+
status: 200

quickwit/rest-api-tests/scenarii/es_compatibility/multi-indices/0004-missing_index_query.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,3 @@ expected:
2525
hits:
2626
total:
2727
value: 4
28-
29-
30-

0 commit comments

Comments
 (0)