5151create (Db , Selector0 , Opts , Kind ) ->
5252 Selector = mango_selector :normalize (Selector0 ),
5353 {UsableIndexes , Trace } = mango_idx :get_usable_indexes (Db , Selector , Opts , Kind ),
54+ UseIndex = use_index (Opts ),
5455 case maybe_filter_indexes_by_ddoc (UsableIndexes , Opts ) of
5556 [] ->
56- % use_index doesn't match a valid index - fall back to a valid one
57- create_cursor (Db , {UsableIndexes , Trace }, Selector , Opts );
57+ % use_index doesn't match a valid index - determine how
58+ % this shall be handled by the further settings
59+ case allow_fallback (Opts ) orelse (UseIndex == [] andalso length (UsableIndexes ) > 1 ) of
60+ true ->
61+ % fall back to a valid index
62+ create_cursor (Db , {UsableIndexes , Trace }, Selector , Opts );
63+ false ->
64+ % no usable index but all_docs, no fallback allowed: return an error
65+ Details =
66+ case UseIndex of
67+ [] -> [];
68+ [DesignId ] -> [ddoc_name (DesignId )];
69+ [DesignId , ViewName ] -> [ddoc_name (DesignId ), ViewName ]
70+ end ,
71+ ? MANGO_ERROR ({invalid_index , Details })
72+ end ;
5873 UserSpecifiedIndex ->
5974 create_cursor (Db , {UserSpecifiedIndex , Trace }, Selector , Opts )
6075 end .
@@ -366,13 +381,21 @@ execute(#cursor{index = Idx} = Cursor, UserFun, UserAcc) ->
366381 Mod = mango_idx :cursor_mod (Idx ),
367382 Mod :execute (Cursor , UserFun , UserAcc ).
368383
384+ use_index (Opts ) ->
385+ {use_index , UseIndex } = lists :keyfind (use_index , 1 , Opts ),
386+ UseIndex .
387+
388+ allow_fallback (Opts ) ->
389+ {allow_fallback , AllowFallback } = lists :keyfind (allow_fallback , 1 , Opts ),
390+ AllowFallback .
391+
369392maybe_filter_indexes_by_ddoc (Indexes , Opts ) ->
370- case lists : keyfind ( use_index , 1 , Opts ) of
371- { use_index , []} ->
393+ case use_index ( Opts ) of
394+ [] ->
372395 [];
373- { use_index , [DesignId ]} ->
396+ [DesignId ] ->
374397 filter_indexes (Indexes , DesignId );
375- { use_index , [DesignId , ViewName ]} ->
398+ [DesignId , ViewName ] ->
376399 filter_indexes (Indexes , DesignId , ViewName )
377400 end .
378401
@@ -575,7 +598,9 @@ create_test_() ->
575598 [
576599 ? TDEF_FE (t_create_regular , 10 ),
577600 ? TDEF_FE (t_create_user_specified_index , 10 ),
578- ? TDEF_FE (t_create_invalid_user_specified_index , 10 )
601+ ? TDEF_FE (t_create_invalid_user_specified_index , 10 ),
602+ ? TDEF_FE (t_create_invalid_user_specified_index_no_fallback , 10 ),
603+ ? TDEF_FE (t_create_no_suitable_index_no_fallback , 10 )
579604 ]
580605 }.
581606
@@ -591,7 +616,7 @@ t_create_regular(_) ->
591616 filtered_indexes => sets :from_list (FilteredIndexes ),
592617 indexes_of_type => sets :from_list (IndexesOfType )
593618 },
594- Options = [{use_index , []}],
619+ Options = [{use_index , []}, { allow_fallback , true } ],
595620 meck :expect (mango_selector , normalize , [selector ], meck :val (normalized_selector )),
596621 meck :expect (
597622 mango_idx ,
@@ -650,7 +675,7 @@ t_create_invalid_user_specified_index(_) ->
650675 filtered_indexes => sets :from_list (UsableIndexes ),
651676 indexes_of_type => sets :from_list (IndexesOfType )
652677 },
653- Options = [{use_index , [<<" foobar" >>]}],
678+ Options = [{use_index , [<<" foobar" >>]}, { allow_fallback , true } ],
654679 meck :expect (mango_selector , normalize , [selector ], meck :val (normalized_selector )),
655680 meck :expect (
656681 mango_idx ,
@@ -666,6 +691,64 @@ t_create_invalid_user_specified_index(_) ->
666691 ),
667692 ? assertEqual (view_cursor , create (db , selector , Options , target )).
668693
694+ t_create_invalid_user_specified_index_no_fallback (_ ) ->
695+ IndexSpecial = # idx {type = <<" special" >>, def = all_docs },
696+ IndexView1 = # idx {type = <<" json" >>, ddoc = <<" _design/view_idx1" >>},
697+ IndexView2 = # idx {type = <<" json" >>, ddoc = <<" _design/view_idx2" >>},
698+ IndexView3 = # idx {type = <<" json" >>, ddoc = <<" _design/view_idx3" >>},
699+ UsableIndexes = [IndexSpecial , IndexView1 , IndexView2 , IndexView3 ],
700+ IndexesOfType = [IndexView1 , IndexView2 , IndexView3 ],
701+ Trace1 = #{},
702+ Trace2 =
703+ #{
704+ filtered_indexes => sets :from_list (UsableIndexes ),
705+ indexes_of_type => sets :from_list (IndexesOfType )
706+ },
707+ UseIndex = [<<" design" >>, <<" foobar" >>],
708+ Options = [{use_index , UseIndex }, {allow_fallback , false }],
709+ meck :expect (mango_selector , normalize , [selector ], meck :val (normalized_selector )),
710+ meck :expect (
711+ mango_idx ,
712+ get_usable_indexes ,
713+ [db , normalized_selector , Options , target ],
714+ meck :val ({UsableIndexes , Trace1 })
715+ ),
716+ meck :expect (
717+ mango_cursor_view ,
718+ create ,
719+ [db , {IndexesOfType , Trace2 }, normalized_selector , Options ],
720+ meck :val (view_cursor )
721+ ),
722+ Exception = {mango_error , mango_cursor , {invalid_index , UseIndex }},
723+ ? assertThrow (Exception , create (db , selector , Options , target )).
724+
725+ t_create_no_suitable_index_no_fallback (_ ) ->
726+ IndexSpecial = # idx {type = <<" special" >>, def = all_docs },
727+ UsableIndexes = [IndexSpecial ],
728+ IndexesOfType = [],
729+ Trace1 = #{},
730+ Trace2 =
731+ #{
732+ filtered_indexes => sets :from_list (UsableIndexes ),
733+ indexes_of_type => sets :from_list (IndexesOfType )
734+ },
735+ Options = [{use_index , []}, {allow_fallback , false }],
736+ meck :expect (mango_selector , normalize , [selector ], meck :val (normalized_selector )),
737+ meck :expect (
738+ mango_idx ,
739+ get_usable_indexes ,
740+ [db , normalized_selector , Options , target ],
741+ meck :val ({UsableIndexes , Trace1 })
742+ ),
743+ meck :expect (
744+ mango_cursor_view ,
745+ create ,
746+ [db , {IndexesOfType , Trace2 }, normalized_selector , Options ],
747+ meck :val (view_cursor )
748+ ),
749+ Exception = {mango_error , mango_cursor , {invalid_index , []}},
750+ ? assertThrow (Exception , create (db , selector , Options , target )).
751+
669752enhance_candidates_test () ->
670753 Candidates1 = #{index => #{reason => [], usable => true }},
671754 Candidates2 = #{index => #{reason => [reason1 ], usable => true }},
0 commit comments