public void pruneIndexCandidates()

in asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/am/AbstractIntroduceAccessMethodRule.java [371:590]


    public void pruneIndexCandidates(IAccessMethod accessMethod, AccessMethodAnalysisContext analysisCtx,
            IOptimizationContext context, IVariableTypeEnvironment typeEnvironment, boolean checkApplicableOnly)
            throws AlgebricksException {
        Iterator<Map.Entry<Index, List<Pair<Integer, Integer>>>> indexExprAndVarIt =
                analysisCtx.getIteratorForIndexExprsAndVars();
        boolean hasIndexPreferences = false;
        ArrayList<Integer> matchedExpressions = new ArrayList<>();
        while (indexExprAndVarIt.hasNext()) {
            Map.Entry<Index, List<Pair<Integer, Integer>>> indexExprAndVarEntry = indexExprAndVarIt.next();
            Index index = indexExprAndVarEntry.getKey();
            IndexType indexType = index.getIndexType();
            if (!accessMethod.matchIndexType(indexType)) {
                indexExprAndVarIt.remove();
                continue;
            }
            List<List<String>> keyFieldNames = findKeyFieldNames(index);
            List<IAType> keyFieldTypes = findKeyTypes(index);

            boolean allUsed = true;
            int lastFieldMatched = -1;
            matchedExpressions.clear();
            // Used to keep track of matched expressions (added for prefix search)
            int numMatchedKeys = 0;

            for (int i = 0; i < keyFieldNames.size(); i++) {
                List<String> keyField = keyFieldNames.get(i);
                final IAType keyType = keyFieldTypes.get(i);
                boolean foundKeyField = false;
                Iterator<Pair<Integer, Integer>> exprsAndVarIter = indexExprAndVarEntry.getValue().iterator();
                while (exprsAndVarIter.hasNext()) {
                    final Pair<Integer, Integer> exprAndVarIdx = exprsAndVarIter.next();
                    final IOptimizableFuncExpr optFuncExpr = analysisCtx.getMatchedFuncExpr(exprAndVarIdx.first);
                    // If expr is not optimizable by concrete index then remove
                    // expr and continue.
                    if (!accessMethod.exprIsOptimizable(index, optFuncExpr, checkApplicableOnly)) {
                        exprsAndVarIter.remove();
                        continue;
                    }
                    boolean typeMatch = true;
                    //Prune indexes based on field types
                    List<IAType> matchedTypes = new ArrayList<>();
                    //retrieve types of expressions joined/selected with an indexed field
                    for (int j = 0; j < optFuncExpr.getNumLogicalVars(); j++) {
                        if (j != exprAndVarIdx.second) {
                            matchedTypes.add(optFuncExpr.getFieldType(j));
                        }

                    }

                    if (matchedTypes.size() < 2 && optFuncExpr.getNumLogicalVars() == 1) {
                        matchedTypes
                                .add((IAType) ExpressionTypeComputer.INSTANCE.getType(optFuncExpr.getConstantExpr(0),
                                        context.getMetadataProvider(), typeEnvironment));
                    }

                    //infer type of logicalExpr based on index keyType
                    matchedTypes.add((IAType) ExpressionTypeComputer.INSTANCE.getType(
                            optFuncExpr.getLogicalExpr(exprAndVarIdx.second), null, new IVariableTypeEnvironment() {

                                @Override
                                public Object getVarType(LogicalVariable var) throws AlgebricksException {
                                    if (var.equals(optFuncExpr.getSourceVar(exprAndVarIdx.second))) {
                                        return keyType;
                                    }
                                    throw new IllegalArgumentException();
                                }

                                @Override
                                public Object getVarType(LogicalVariable var,
                                        List<LogicalVariable> nonMissableVariables,
                                        List<List<LogicalVariable>> correlatedMissableVariableLists,
                                        List<LogicalVariable> nonNullableVariables,
                                        List<List<LogicalVariable>> correlatedNullableVariableLists)
                                        throws AlgebricksException {
                                    if (var.equals(optFuncExpr.getSourceVar(exprAndVarIdx.second))) {
                                        return keyType;
                                    }
                                    throw new IllegalArgumentException();
                                }

                                @Override
                                public void setVarType(LogicalVariable var, Object type) {
                                    throw new IllegalArgumentException();
                                }

                                @Override
                                public Object getType(ILogicalExpression expr) throws AlgebricksException {
                                    return ExpressionTypeComputer.INSTANCE.getType(expr, null, this);
                                }

                                @Override
                                public boolean substituteProducedVariable(LogicalVariable v1, LogicalVariable v2)
                                        throws AlgebricksException {
                                    throw new IllegalArgumentException();
                                }
                            }));

                    //for the case when jaccard similarity is measured between ordered & unordered lists
                    boolean jaccardSimilarity = optFuncExpr.getFuncExpr().getFunctionIdentifier().getName()
                            .startsWith("similarity-jaccard-check");

                    // Full-text search consideration: an (un)ordered list of string type can be compatible with string
                    // type. i.e. an (un)ordered list can be provided as arguments to a string type field index.
                    List<IAType> elementTypes = matchedTypes;
                    if (optFuncExpr.getFuncExpr().getFunctionIdentifier() == BuiltinFunctions.FULLTEXT_CONTAINS
                            || optFuncExpr.getFuncExpr()
                                    .getFunctionIdentifier() == BuiltinFunctions.FULLTEXT_CONTAINS_WO_OPTION) {
                        for (int j = 0; j < matchedTypes.size(); j++) {
                            if (matchedTypes.get(j).getTypeTag() == ATypeTag.ARRAY
                                    || matchedTypes.get(j).getTypeTag() == ATypeTag.MULTISET) {
                                elementTypes.set(j, ((AbstractCollectionType) matchedTypes.get(j)).getItemType());
                            }
                        }
                    }

                    for (int j = 0; j < matchedTypes.size(); j++) {
                        for (int k = j + 1; k < matchedTypes.size(); k++) {
                            if (!ATypeTag.ANY.equals(elementTypes.get(k).getTypeTag())) {
                                typeMatch &= isMatched(elementTypes.get(j), elementTypes.get(k), jaccardSimilarity);
                            }
                        }
                    }

                    // Check if any field name in the optFuncExpr matches.
                    if (typeMatch && optFuncExpr.findFieldName(keyField) != -1
                            && optFuncExpr.getOperatorSubTree(exprAndVarIdx.second).hasDataSourceScan()) {
                        foundKeyField = true;
                        matchedExpressions.add(exprAndVarIdx.first);
                        hasIndexPreferences =
                                hasIndexPreferences || accessMethod.getSecondaryIndexAnnotation(optFuncExpr) != null;
                    }
                }
                if (foundKeyField) {
                    numMatchedKeys++;
                    if (lastFieldMatched == i - 1) {
                        lastFieldMatched = i;
                    }
                } else {
                    allUsed = false;
                    // if any expression was matched, remove the non-matched expressions, otherwise the index is unusable
                    if (lastFieldMatched >= 0) {
                        exprsAndVarIter = indexExprAndVarEntry.getValue().iterator();
                        while (exprsAndVarIter.hasNext()) {
                            if (!matchedExpressions.contains(exprsAndVarIter.next().first)) {
                                exprsAndVarIter.remove();
                            }
                        }
                    }
                    break;
                }
            }
            // If the access method requires all exprs to be matched but they
            // are not, remove this candidate.
            if (!allUsed && accessMethod.matchAllIndexExprs(index)) {
                indexExprAndVarIt.remove();
                continue;
            }
            // A prefix of the index exprs may have been matched.
            if (accessMethod.matchPrefixIndexExprs(index)) {
                if (lastFieldMatched < 0) {
                    indexExprAndVarIt.remove();
                    continue;

                } else if (Index.IndexCategory.of(indexType).equals(Index.IndexCategory.ARRAY)) {
                    // For array indexes, we cannot make the decision to apply the prefix until we see a conjunct
                    // conditioning on an array. We should improve using array indexes for queries that don't involve
                    // the array component in the future.
                    Index.ArrayIndexDetails arrayIndexDetails = (Index.ArrayIndexDetails) index.getIndexDetails();
                    int indexOfFirstArrayField = 0;
                    for (Index.ArrayIndexElement e : arrayIndexDetails.getElementList()) {
                        if (!e.getUnnestList().isEmpty()) {
                            break;
                        }
                        for (List<String> ignored : e.getProjectList()) {
                            indexOfFirstArrayField++;
                        }
                    }
                    if (lastFieldMatched < indexOfFirstArrayField) {
                        indexExprAndVarIt.remove();
                        continue;
                    }
                }
            }
            analysisCtx.putNumberOfMatchedKeys(index, numMatchedKeys);
        }

        if (hasIndexPreferences) {
            Map<IOptimizableFuncExpr, Set<Index>> exprAndApplicableIndexes =
                    createExprAndApplicableIndexesMap(analysisCtx);
            // First validate the index preference hints. Warn and remove any inapplicable hints.
            boolean annotationRemoved =
                    warnAndRemoveInapplicableSecondaryIndexHints(exprAndApplicableIndexes, accessMethod, context);
            Collection<String> preferredSecondaryIndexNames =
                    fetchPreferredSecondaryIndexNames(exprAndApplicableIndexes, accessMethod);
            if (preferredSecondaryIndexNames != null) {
                // If we have preferred indexes then remove all non-preferred indexes.
                // Non preferred indexes are (applicableIndexes - preferredIndexes).
                removeNonPreferredSecondaryIndexes(analysisCtx, preferredSecondaryIndexNames);
            } else if (annotationRemoved && (this instanceof IntroduceJoinAccessMethodRule)) {
                // ONE OR MORE INAPPLICABLE ANNOTATIONS HAS BEEN REMOVED AND THERE ARE NO preferredIndexes LEFT.
                // IT IS AS IF NO HINT WAS SPECIFIED BY THE USER.
                // THIS CODE WILL ONLY BE TRIGGERED FOR JOINS AND NOT FOR SCANS
                // TO PRESERVE CURRENT FUNCTIONALITY IN THE ABSENCE OF HINTS.
                //
                // In the case of joins, we want to REMOVE all applicable indexes from consideration,
                // so that the indexnl join will not be applicable.
                // RBO will not pick an indexnl join and default to a hash join.
                // CBO will enumerate all possible join methods and pick the cheapest one.
                //
                // In the case of scans, we DO NOT want to REMOVE any applicable indexes from consideration.
                // RBO will default to an intersection of all applicable indexes.
                // CBO will make a cost based selection and intersection of the chosen indexes.

                // We pass in an empty list for preferredIndexNames,
                // which means that all applicable indexes are non-preferred and will be removed from consideration.
                removeNonPreferredSecondaryIndexes(analysisCtx, Collections.EMPTY_LIST);
            }
        }

    }