@@ -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 ;
0 commit comments