1919package org .apache .asterix .optimizer .rules .am ;
2020
2121import java .util .ArrayList ;
22+ import java .util .Collections ;
2223import java .util .HashMap ;
2324import java .util .List ;
2425import java .util .Map ;
2526import java .util .Optional ;
2627import java .util .TreeMap ;
28+ import java .util .stream .IntStream ;
2729
2830import org .apache .asterix .algebra .operators .CommitOperator ;
2931import org .apache .asterix .common .cluster .PartitioningProperties ;
5052import org .apache .hyracks .algebricks .core .algebra .expressions .AbstractFunctionCallExpression ;
5153import org .apache .hyracks .algebricks .core .algebra .expressions .IVariableTypeEnvironment ;
5254import org .apache .hyracks .algebricks .core .algebra .expressions .VariableReferenceExpression ;
55+ import org .apache .hyracks .algebricks .core .algebra .functions .AlgebricksBuiltinFunctions ;
5356import org .apache .hyracks .algebricks .core .algebra .functions .FunctionIdentifier ;
5457import org .apache .hyracks .algebricks .core .algebra .operators .logical .AbstractLogicalOperator ;
5558import org .apache .hyracks .algebricks .core .algebra .operators .logical .AbstractLogicalOperator .ExecutionMode ;
@@ -383,7 +386,8 @@ protected boolean prefix(List<List<String>> list1, List<List<String>> list2) {
383386 return true ;
384387 }
385388
386- protected void removeSmallerPrefixIndexes (List <IndexAccessInfo > indexes ) throws CompilationException {
389+ protected void keepBestPrefixIndexes (List <IndexAccessInfo > indexes , List <Integer > numberOfMatchedKeys )
390+ throws CompilationException {
387391 int len = indexes .size ();
388392 int i , j ;
389393 Index indexI , indexJ ;
@@ -412,10 +416,19 @@ protected void removeSmallerPrefixIndexes(List<IndexAccessInfo> indexes) throws
412416 fieldNamesJ = findKeyFieldNames (indexJ );
413417 if (fieldNamesI .size () <= fieldNamesJ .size ()) {
414418 if (prefix (fieldNamesI , fieldNamesJ )) {
415- include [i ] = false ;
419+ if (numberOfMatchedKeys .get (j ) > numberOfMatchedKeys .get (i )) {
420+ include [i ] = false ;
421+ } else {
422+ include [j ] = false ;
423+
424+ }
416425 }
417426 } else if (prefix (fieldNamesJ , fieldNamesI )) {
418- include [j ] = false ;
427+ if (numberOfMatchedKeys .get (i ) > numberOfMatchedKeys .get (j )) {
428+ include [j ] = false ;
429+ } else {
430+ include [i ] = false ;
431+ }
419432 }
420433 }
421434 }
@@ -548,13 +561,16 @@ protected boolean checkAndApplyTheSelectTransformation(Mutable<ILogicalOperator>
548561
549562 // Choose all indexes that will be applied.
550563 chooseAllIndexes (analyzedAMs , chosenIndexes );
551- removeSmallerPrefixIndexes (chosenIndexes );
552-
553564 if (chosenIndexes == null || chosenIndexes .isEmpty ()) {
554565 // We can't apply any index for this SELECT operator
555566 context .addToDontApplySet (this , selectRef .getValue ());
556567 return false ;
557568 }
569+ List <Integer > matchedKeyCountsNonCovering = new ArrayList <>();
570+ List <Integer > matchedKeyCountsCovering = new ArrayList <>();
571+ List <IndexAccessInfo > nonCoverigIndexes = new ArrayList <>();
572+ List <IndexAccessInfo > coverigIndexes = new ArrayList <>();
573+ fillFieldNamesInTheSubTree (subTree , context );
558574
559575 for (IndexAccessInfo indexAccessInfo : chosenIndexes ) {
560576 AccessMethodAnalysisContext analysisCtx = analyzedAMs .get (indexAccessInfo .getAccessMethod ());
@@ -565,7 +581,15 @@ protected boolean checkAndApplyTheSelectTransformation(Mutable<ILogicalOperator>
565581 AccessMethodUtils .indexOnlyPlanCheck (afterSelectRefs , selectRef , subTree , null ,
566582 indexAccessInfo .getIndex (), analysisCtx , context , indexOnlyPlanInfo , false );
567583 indexAccessInfo .setIsIndexOnlyPlan (indexOnlyPlanInfo .getFirst ());
584+ if (indexAccessInfo .isIndexOnlyPlan ) {
585+ matchedKeyCountsCovering .add (analysisCtx .getNumberOfMatchedKeys (indexAccessInfo .getIndex ()));
586+ coverigIndexes .add (indexAccessInfo );
587+ } else {
588+ matchedKeyCountsNonCovering .add (analysisCtx .getNumberOfMatchedKeys (indexAccessInfo .getIndex ()));
589+ nonCoverigIndexes .add (indexAccessInfo );
590+ }
568591 }
592+ keepBestPrefixIndexes (nonCoverigIndexes , matchedKeyCountsNonCovering );
569593
570594 if (checkApplicableOnly ) {
571595 return true ;
@@ -574,28 +598,36 @@ protected boolean checkAndApplyTheSelectTransformation(Mutable<ILogicalOperator>
574598 // Apply plan transformation using chosen index.
575599 boolean res ;
576600 // Primary index applicable?
601+ // We should look in the list including both covering and non covering.
577602 IndexAccessInfo chosenPrimaryIndex = fetchPrimaryIndexAmongChosenIndexes (chosenIndexes );
578603 if (chosenPrimaryIndex != null ) {
604+ subTree .getVarsToFieldNameMap ().clear ();
579605 AccessMethodAnalysisContext analysisCtx = analyzedAMs .get (chosenPrimaryIndex .getAccessMethod ());
580606 res = chosenPrimaryIndex .getAccessMethod ().applySelectPlanTransformation (afterSelectRefs , selectRef ,
581607 subTree , chosenPrimaryIndex .getIndex (), analysisCtx , context );
582608 context .addToDontApplySet (this , selectRef .getValue ());
583- } else if (chosenIndexes .size () == 1 ) {
584- // Index-only plan possible?
609+ } else if (coverigIndexes .isEmpty () && nonCoverigIndexes .size () == 1 ) {
585610 // Gets the analysis context for the given index.
586- AccessMethodAnalysisContext analysisCtx = analyzedAMs .get (chosenIndexes .get (0 ).getAccessMethod ());
587-
588- // Finds the field name of each variable in the sub-tree.
589- fillFieldNamesInTheSubTree (subTree , context );
611+ AccessMethodAnalysisContext analysisCtx =
612+ analyzedAMs .get (nonCoverigIndexes .get (0 ).getAccessMethod ());
590613
591614 // Finally, try to apply plan transformation using chosen index.
592- res = chosenIndexes .get (0 ).getAccessMethod ().applySelectPlanTransformation (afterSelectRefs ,
593- selectRef , subTree , chosenIndexes .get (0 ).getIndex (), analysisCtx , context );
615+ res = nonCoverigIndexes .get (0 ).getAccessMethod ().applySelectPlanTransformation (afterSelectRefs ,
616+ selectRef , subTree , nonCoverigIndexes .get (0 ).getIndex (), analysisCtx , context );
594617 context .addToDontApplySet (this , selectRef .getValue ());
595618 } else {
596- // Multiple secondary indexes applicable?
597- res = intersectAllSecondaryIndexes (chosenIndexes , analyzedAMs , context );
598- context .addToDontApplySet (this , selectRef .getValue ());
619+ if (!coverigIndexes .isEmpty ()) {
620+ IndexAccessInfo bestCoveringIndex =
621+ chooseBestCoveringIndex (coverigIndexes , matchedKeyCountsCovering , analyzedAMs );
622+ AccessMethodAnalysisContext analysisCtx = analyzedAMs .get (bestCoveringIndex .getAccessMethod ());
623+ res = bestCoveringIndex .getAccessMethod ().applySelectPlanTransformation (afterSelectRefs ,
624+ selectRef , subTree , bestCoveringIndex .getIndex (), analysisCtx , context );
625+ context .addToDontApplySet (this , selectRef .getValue ());
626+ } else {
627+ subTree .getVarsToFieldNameMap ().clear ();
628+ res = intersectAllSecondaryIndexes (nonCoverigIndexes , analyzedAMs , context );
629+ context .addToDontApplySet (this , selectRef .getValue ());
630+ }
599631 }
600632
601633 // If the plan transformation is successful, we don't need to traverse
@@ -619,6 +651,68 @@ protected boolean checkAndApplyTheSelectTransformation(Mutable<ILogicalOperator>
619651
620652 }
621653
654+ private IndexAccessInfo chooseBestCoveringIndex (List <IndexAccessInfo > indexes ,
655+ List <Integer > numberOfMatchedKeysForCoveringindexes ,
656+ Map <IAccessMethod , AccessMethodAnalysisContext > analyzedAMs ) throws CompilationException {
657+
658+ if (indexes .size () == 1 ) {
659+ return indexes .get (0 );
660+ }
661+
662+ // 1) Keep only indexes with max matched keys
663+ int max = numberOfMatchedKeysForCoveringindexes .stream ().mapToInt (Integer ::intValue ).max ().orElse (0 );
664+ List <IndexAccessInfo > indexesWithMaxMatchedKeys = IntStream .range (0 , indexes .size ())
665+ .filter (i -> numberOfMatchedKeysForCoveringindexes .get (i ) == max ).mapToObj (indexes ::get ).toList ();
666+ if (indexesWithMaxMatchedKeys .size () == 1 ) {
667+ return indexesWithMaxMatchedKeys .get (0 );
668+ }
669+
670+ // 2) Find earliest equality positions
671+ List <IndexInfo > indexesWithMaxMatchedKeysInfo = new ArrayList <>(indexesWithMaxMatchedKeys .size ());
672+ for (IndexAccessInfo indexAccessInfo : indexesWithMaxMatchedKeys ) {
673+ List <List <String >> indexFields = findKeyFieldNames (indexAccessInfo .getIndex ());
674+ List <IOptimizableFuncExpr > funcExprs =
675+ analyzedAMs .get (indexAccessInfo .getAccessMethod ()).getMatchedFuncExprs ();
676+ IndexInfo indexInfo = new IndexInfo ();
677+ indexInfo .indexExprs = new ArrayList <>(Collections .nCopies (funcExprs .size (), null ));
678+ indexInfo .numKeys = indexFields .size ();
679+ indexInfo .indexaccessinfo = indexAccessInfo ;
680+ for (IOptimizableFuncExpr expr : funcExprs ) {
681+ for (int i = 0 ; i < expr .getNumLogicalVars (); i ++) {
682+ int i1 = indexFields .indexOf (expr .getFieldName (i ));
683+ indexInfo .indexExprs .set (i1 , expr .getFuncExpr ().getFunctionIdentifier ());
684+ }
685+ }
686+ indexesWithMaxMatchedKeysInfo .add (indexInfo );
687+ }
688+ return indexesWithMaxMatchedKeysInfo .stream ().sorted ().toList ().getFirst ().indexaccessinfo ;
689+ }
690+
691+ class IndexInfo implements Comparable <IndexInfo > {
692+ List <FunctionIdentifier > indexExprs = new ArrayList <>();
693+ int numKeys ;
694+ IndexAccessInfo indexaccessinfo ;
695+
696+ @ Override
697+ public int compareTo (IndexInfo other ) {
698+ for (int i = 0 ; i < this .indexExprs .size () && i < other .indexExprs .size (); i ++) {
699+ if (this .indexExprs .get (i ) == AlgebricksBuiltinFunctions .EQ
700+ && other .indexExprs .get (i ) != AlgebricksBuiltinFunctions .EQ ) {
701+ return -1 ;
702+ } else if (this .indexExprs .get (i ) != AlgebricksBuiltinFunctions .EQ
703+ && other .indexExprs .get (i ) == AlgebricksBuiltinFunctions .EQ ) {
704+ return 1 ;
705+ }
706+ }
707+
708+ if (this .indexExprs .size () != other .indexExprs .size ()) {
709+ return 0 ;
710+ }
711+
712+ return Integer .compare (this .numKeys , other .numKeys );
713+ }
714+ }
715+
622716 @ Override
623717 public Map <FunctionIdentifier , List <IAccessMethod >> getAccessMethods () {
624718 return accessMethods ;
0 commit comments