in pinot-controller/src/main/java/org/apache/pinot/controller/recommender/rules/utils/QueryInvertedSortedIndexRecommender.java [339:515]
private PredicateParseResult parsePredicateList(FilterContext predicateList, int depth) {
LOGGER.debug("parsePredicateList: Parsing predicate list: {}", predicateList.toString());
FilterContext.Type type = predicateList.getType();
// case:AND connected predicates
if (type == FilterContext.Type.AND) {
LOGGER.trace("parsePredicateList: Parsing AND list {}", predicateList.toString());
FixedLenBitset candidateDims = mutableEmptySet();
double nESI = NESI_ZERO;
double percentSelected = PERCENT_SELECT_ALL;
List<PredicateParseResult> childResults = new ArrayList<>();
boolean isPureBitmap = false;
// Recursively eval child predicates
for (int i = 0; i < predicateList.getChildren().size(); i++) {
PredicateParseResult childResult = parsePredicateList(predicateList.getChildren().get(i), depth + 1);
if (childResult != null) {
childResults.add(childResult);
}
}
// Sort child candidates based on the priority
childResults = reorderAndPredicateList(childResults);
LOGGER.debug("parsePredicateList: AND: Reordered child results: {}", childResults);
if (childResults.get(childResults.size() - 1).getIteratorEvalPriority()
.compareTo(IteratorEvalPriorityEnum.INDEXED) <= 0) {
isPureBitmap = true;
}
// Calculate the estimated columns scanned with no indices applied
for (PredicateParseResult childResult : childResults) {
nESI += childResult.getnESI() * percentSelected;
percentSelected *= childResult.getPercentSelected();
}
// Recommend nothing if all child predicates already have indices
if (isPureBitmap) {
return PredicateParseResult.PredicateParseResultBuilder.aPredicateParseResult()
.setCandidateDims(FixedLenBitset.IMMUTABLE_EMPTY_SET)
.setIteratorEvalPriorityEnum(IteratorEvalPriorityEnum.INDEXED)
.setRecommendationPriorityEnum(RecommendationPriorityEnum.BITMAP).setnESI(nESI)
.setPercentSelected(percentSelected).setnESIWithIdx(nESI).build();
}
Map<RecommendationPriorityEnum, List<PredicateParseResult>> groupedPredicates = childResults.stream()
.collect(Collectors.groupingBy(PredicateParseResult::getRecommendationPriorityEnum, Collectors.toList()));
// Recommend nothing if any nested predicate list cannot be converted to a bitmap by applying sorted/inverted
// indices
if (groupedPredicates.containsKey(RecommendationPriorityEnum.NESTED) || (
!groupedPredicates.containsKey(RecommendationPriorityEnum.BITMAP) && !groupedPredicates
.containsKey(RecommendationPriorityEnum.CANDIDATE_SCAN))) {
return PredicateParseResult.PredicateParseResultBuilder.aPredicateParseResult()
.setCandidateDims(FixedLenBitset.IMMUTABLE_EMPTY_SET)
.setIteratorEvalPriorityEnum(IteratorEvalPriorityEnum.AND)
.setRecommendationPriorityEnum(RecommendationPriorityEnum.NESTED).setnESI(nESI)
.setPercentSelected(percentSelected).setnESIWithIdx(nESI).build();
}
// Recommend one predicate having min percent docs selected to apply indices on,
// from the scanning based predicates
Optional<PredicateParseResult> newCandidateOptional =
groupedPredicates.getOrDefault(RecommendationPriorityEnum.CANDIDATE_SCAN, Collections.emptyList()).stream()
.filter(
PredicateParseResult::hasCandidateDim) // The predicate should be index applicable to be recommended
.min(Comparator.comparing(PredicateParseResult::getPercentSelected));
double bitmapPercentSelected = PERCENT_SELECT_ALL;
double bitmapNESIWithIdx = NESI_ZERO;
for (PredicateParseResult predicateParseResult : groupedPredicates
.getOrDefault(RecommendationPriorityEnum.BITMAP, Collections.emptyList())) {
bitmapPercentSelected *= predicateParseResult.getPercentSelected();
bitmapNESIWithIdx += predicateParseResult.getnESIWithIdx();
candidateDims.union(predicateParseResult.getCandidateDims());
}
if (depth > NESTED_SECOND_LEVEL) {
if (groupedPredicates.containsKey(RecommendationPriorityEnum.BITMAP)) {
newCandidateOptional = Optional.empty();
}
} else {
if (newCandidateOptional.isPresent()
&& newCandidateOptional.get().getPercentSelected() > bitmapPercentSelected) {
newCandidateOptional = Optional.empty();
}
}
newCandidateOptional
.ifPresent(predicateParseResult -> candidateDims.union(predicateParseResult.getCandidateDims()));
// Calculate the estimated columns scanned with recommended indices applied
double percentSelectedWithIdx =
newCandidateOptional.map(PredicateParseResult::getPercentSelected).orElse(PERCENT_SELECT_ALL);
double nESIWithIdx = newCandidateOptional.map(PredicateParseResult::getnESIWithIdx).orElse(NESI_ZERO);
percentSelectedWithIdx *= bitmapPercentSelected;
nESIWithIdx += bitmapNESIWithIdx;
for (PredicateParseResult predicateParseResult : groupedPredicates
.getOrDefault(RecommendationPriorityEnum.CANDIDATE_SCAN, Collections.emptyList())) {
if (predicateParseResult != newCandidateOptional.orElse(null)) {
nESIWithIdx += predicateParseResult.getnESI() * percentSelectedWithIdx;
percentSelectedWithIdx *= predicateParseResult.getPercentSelected();
}
}
for (PredicateParseResult predicateParseResult : groupedPredicates
.getOrDefault(RecommendationPriorityEnum.NON_CANDIDATE_SCAN, Collections.emptyList())) {
nESIWithIdx += predicateParseResult.getnESI() * percentSelectedWithIdx;
percentSelectedWithIdx *= predicateParseResult.getPercentSelected();
}
return PredicateParseResult.PredicateParseResultBuilder.aPredicateParseResult().setCandidateDims(candidateDims)
.setIteratorEvalPriorityEnum(IteratorEvalPriorityEnum.AND)
.setRecommendationPriorityEnum(RecommendationPriorityEnum.BITMAP).setnESI(nESI)
.setPercentSelected(percentSelected).setnESIWithIdx(nESIWithIdx).build();
} else if (type == FilterContext.Type.OR) {
// case:OR connected predicates
LOGGER.trace("parsePredicateList: OR Parsing list: {}", predicateList.toString());
List<PredicateParseResult> childResults = new ArrayList<>();
FixedLenBitset candidateDims = mutableEmptySet();
double nESI = NESI_ZERO;
double nESIWithIdx = NESI_ZERO;
double percentSelected = PERCENT_SELECT_ZERO;
boolean isPureBitmap = true;
boolean isBitmapConvertable = true;
// Recursively eval child predicates
for (int i = 0; i < predicateList.getChildren().size(); i++) {
PredicateParseResult childResult = parsePredicateList(predicateList.getChildren().get(i), depth + 1);
if (childResult != null) {
childResults.add(childResult);
}
}
LOGGER.debug("parsePredicateList: OR: childResults {}", childResults);
// Recommend all child candidates if by doing this the or predicate can be reduced to a bitmap iterator
for (PredicateParseResult childResult : childResults) {
nESI += childResult.getnESI();
percentSelected += childResult.getPercentSelected();
nESIWithIdx += childResult.getnESIWithIdx();
candidateDims.union(childResult.getCandidateDims());
if (!childResult.isBitmapConvertable()) { //Contains column that can not be converted to a Bitmap iterator
isBitmapConvertable = false;
}
if (childResult.getIteratorEvalPriority().compareTo(IteratorEvalPriorityEnum.INDEXED) > 0) {
isPureBitmap = false;
}
}
LOGGER.trace("parsePredicateList: OR: parsePredicateList(): isPureBitmap {} hasCandidateDim {}", isPureBitmap,
isBitmapConvertable);
percentSelected = Math.min(percentSelected, PERCENT_SELECT_ALL);
if (isPureBitmap) {
return PredicateParseResult.PredicateParseResultBuilder.aPredicateParseResult().setCandidateDims(candidateDims)
.setIteratorEvalPriorityEnum(IteratorEvalPriorityEnum.INDEXED)
.setRecommendationPriorityEnum(RecommendationPriorityEnum.BITMAP).setnESI(nESI)
.setPercentSelected(percentSelected).setnESIWithIdx(nESIWithIdx).build();
} else if (isBitmapConvertable) { // The current and predicate cannot be converted to a bitmap by applying indices
return PredicateParseResult.PredicateParseResultBuilder.aPredicateParseResult().setCandidateDims(candidateDims)
.setIteratorEvalPriorityEnum(IteratorEvalPriorityEnum.OR)
.setRecommendationPriorityEnum(RecommendationPriorityEnum.BITMAP).setnESI(nESI)
.setPercentSelected(percentSelected).setnESIWithIdx(nESIWithIdx).build();
} else {
return PredicateParseResult.PredicateParseResultBuilder.aPredicateParseResult()
.setCandidateDims(FixedLenBitset.IMMUTABLE_EMPTY_SET)
.setIteratorEvalPriorityEnum(IteratorEvalPriorityEnum.OR)
.setRecommendationPriorityEnum(RecommendationPriorityEnum.NESTED).setnESI(nESI)
.setPercentSelected(percentSelected).setnESIWithIdx(nESI).build();
}
} else if (type == FilterContext.Type.NOT) {
assert predicateList.getChildren().size() == 1;
return parsePredicateList(predicateList.getChildren().get(0), depth);
} else {
// case:Leaf predicate
PredicateParseResult predicateParseResult = parseLeafPredicate(predicateList, depth);
LOGGER.debug("parsePredicateList: Parsed a leaf predicate: {}", predicateParseResult);
return predicateParseResult;
}
}