public void getCandidateIndexes()

in exec/java-exec/src/main/java/org/apache/drill/exec/planner/index/IndexSelector.java [221:362]


  public void getCandidateIndexes(IndexConditionInfo.Builder infoBuilder, List<IndexGroup> coveringIndexes,
      List<IndexGroup> nonCoveringIndexes, List<IndexGroup> intersectIndexes) {

    RelOptPlanner planner = indexContext.getCall().getPlanner();
    PlannerSettings settings = PrelUtil.getPlannerSettings(planner);
    List<IndexGroup> candidateIndexes = Lists.newArrayList();

    logger.info("index_plan_info: Analyzing {} indexes for prefix matches: {}",
        indexPropList.size(), indexPropList);
    // analysis phase
    for (IndexProperties p : indexPropList) {
      analyzePrefixMatches(p);

      // only consider indexes that either have some leading prefix of the filter condition or
      // can satisfy required collation
      if (p.numLeadingFilters() > 0 || p.satisfiesCollation()) {
        double selThreshold = p.isCovering() ? settings.getIndexCoveringSelThreshold() :
          settings.getIndexNonCoveringSelThreshold();
        // only consider indexes whose selectivity is <= the configured threshold OR consider
        // all when full table scan is disable to avoid a CannotPlanException
        if (settings.isDisableFullTableScan() || p.getLeadingSelectivity() <= selThreshold) {
          IndexGroup index = new IndexGroup();
          index.addIndexProp(p);
          candidateIndexes.add(index);
        }
        else {
          if (p.getLeadingSelectivity() > selThreshold) {
            logger.debug("Skipping index {}. The leading selectivity {} is larger than threshold {}",
                p.getIndexDesc().getIndexName(), p.getLeadingSelectivity(), selThreshold);
          }
        }
      }
    }

    if (candidateIndexes.size() == 0) {
      logger.info("index_plan_info: No suitable indexes found !");
      return;
    }

    int max_candidate_indexes = (int)PrelUtil.getPlannerSettings(planner).getIndexMaxChosenIndexesPerTable();

    // Ranking phase. Technically, we don't need to rank if there are fewer than max_candidate_indexes
    // but we do it anyways for couple of reasons: the log output will show the indexes in a properly ranked
    // order which helps diagnosing problems and secondly for internal unit/functional testing we want this code
    // to be exercised even for few indexes
    if (candidateIndexes.size() > 1) {
      Collections.sort(candidateIndexes, new IndexComparator(planner, builder));
    }

    // Generate index intersections for ranking
    addIndexIntersections(candidateIndexes, infoBuilder, settings.getMaxIndexesToIntersect());

    // Sort again after intersect plan is added to the list
    if (candidateIndexes.size() > 1) {
      Collections.sort(candidateIndexes, new IndexComparator(planner, builder));
    }

    logger.info("index_plan_info: The top ranked indexes are: ");

    int count = 0;
    boolean foundCovering = false;
    boolean foundCoveringCollation = false;
    boolean foundNonCoveringCollation = false;

    // pick the best N indexes
    for (int i=0; i < candidateIndexes.size(); i++) {
      IndexGroup index = candidateIndexes.get(i);
      if (index.numIndexes() == 1
          && index.getIndexProps().get(0).isCovering()) {
        IndexProperties indexProps = index.getIndexProps().get(0);
        if (foundCoveringCollation) {
          // if previously we already found a higher ranked covering index that satisfies collation,
          // then skip this one (note that selectivity and cost considerations were already handled
          // by the ranking phase)
          logger.debug("index_plan_info: Skipping covering index {} because a higher ranked covering index with collation already exists.", indexProps.getIndexDesc().getIndexName());
          continue;
        }
        coveringIndexes.add(index);
        logger.info("index_plan_info: name: {}, covering, collation: {}, leadingSelectivity: {}, cost: {}",
            indexProps.getIndexDesc().getIndexName(),
            indexProps.satisfiesCollation(),
            indexProps.getLeadingSelectivity(),
            indexProps.getSelfCost(planner));
        count++;
        foundCovering = true;
        if (indexProps.satisfiesCollation()) {
          foundCoveringCollation = true;
        }
      } else if (index.numIndexes() == 1) {  // non-covering
        IndexProperties indexProps = index.getIndexProps().get(0);
        // skip this non-covering index if (a) there was a higher ranked covering index
        // with collation or (b) there was a higher ranked covering index and this
        // non-covering index does not have collation
        if (foundCoveringCollation ||
            (foundCovering && !indexProps.satisfiesCollation())) {
          logger.debug("index_plan_info: Skipping non-covering index {} because it does not have collation and a higher ranked covering index already exists.",
              indexProps.getIndexDesc().getIndexName());
          continue;
        }
        if (indexProps.satisfiesCollation()) {
          foundNonCoveringCollation = true;
        }
        // all other non-covering indexes can be added to the list because 2 or more non-covering index could
        // be considered for intersection later; currently the index selector is not costing the index intersection
        nonCoveringIndexes.add(index);
        logger.info("index_plan_info: name: {}, non-covering, collation: {}, leadingSelectivity: {}, cost: {}",
            indexProps.getIndexDesc().getIndexName(),
            indexProps.satisfiesCollation(),
            indexProps.getLeadingSelectivity(),
            indexProps.getSelfCost(planner));
        count++;
      } else {  // intersect indexes
        if (foundCoveringCollation ||
            (foundCovering && !index.getIndexProps().get(index.numIndexes()-1).satisfiesCollation()) ||
            foundNonCoveringCollation) {
          continue;
        }
        IndexGroup intersectIndex = new IndexGroup();
        double isectLeadingSel = 1.0;
        String isectName = "Intersect-"+count;
        for (IndexProperties indexProps : index.getIndexProps()) {
          intersectIndex.addIndexProp(indexProps);
          isectLeadingSel *= indexProps.getLeadingSelectivity();
          logger.info("name: {}, {}, collation: {}, leadingSelectivity: {}, cost: {}",
              indexProps.getIndexDesc().getIndexName(),
              isectName,
              indexProps.satisfiesCollation(),
              indexProps.getLeadingSelectivity(),
              indexProps.getSelfCost(planner));
        }
        logger.info("name: {}, intersect-idx, collation: {}, leadingSelectivity: {}, cost: {}",
            isectName,
            index.getIndexProps().get(index.numIndexes()-1).satisfiesCollation(),
            isectLeadingSel,
            index.getIndexProps().get(0).getIntersectCost(index, builder, planner));
        intersectIndexes.add(intersectIndex);
      }
      if (count == max_candidate_indexes) {
        break;
      }
    }
  }