summaryrefslogtreecommitdiff
path: root/src/mongo/db/query/plan_ranker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/query/plan_ranker.cpp')
-rw-r--r--src/mongo/db/query/plan_ranker.cpp302
1 files changed, 0 insertions, 302 deletions
diff --git a/src/mongo/db/query/plan_ranker.cpp b/src/mongo/db/query/plan_ranker.cpp
index 42fb5645ff8..87f189e89f7 100644
--- a/src/mongo/db/query/plan_ranker.cpp
+++ b/src/mongo/db/query/plan_ranker.cpp
@@ -93,21 +93,6 @@ void logFailedPlan(std::function<std::string()> planSummary) {
LOGV2_DEBUG(
20960, 2, "Not scoring a plan because the plan failed", "planSummary"_attr = planSummary());
}
-
-void logTieBreaking(double score,
- double docsExaminedBonus,
- double indexPrefixBonus,
- bool isPlanTied) {
- LOGV2_DEBUG(8027500, 2, "Tie breaking heuristics", "formula"_attr = [&]() {
- StringBuilder sb;
- sb << "isPlanTied: " << isPlanTied << ". finalScore("
- << str::convertDoubleToString(score + docsExaminedBonus + indexPrefixBonus)
- << ") = score(" << str::convertDoubleToString(score) << ") + docsExaminedBonus("
- << str::convertDoubleToString(docsExaminedBonus) << ") + indexPrefixBonus("
- << str::convertDoubleToString(indexPrefixBonus) << ")";
- return sb.str();
- }());
-}
} // namespace log_detail
namespace {
@@ -153,296 +138,9 @@ protected:
return false;
}
};
-
-/**
- * Return true if the nodes have the same type and the same number of children.
- */
-bool areNodesCompatible(const std::vector<const QuerySolutionNode*>& nodes) {
- for (size_t i = 1; i < nodes.size(); ++i) {
- if (nodes[i - 1]->getType() != nodes[i]->getType()) {
- return false;
- }
-
- if (nodes[i - 1]->children.size() != nodes[i]->children.size()) {
- return false;
- }
- }
-
- return true;
-}
-
-/**
- * Returns true if the value can serve as a type lower bound for the purposes of type bracketing.
- * The function is designed to work with the 'interesting' for index prefix heuristic types only:
- * Number, String, Date, Timestamp, Boolean, Object, Array, ObjectId. For other types it may return
- * false positive results. The code of the function is based on index bounds build logic from
- * 'index_bounds_builder.cpp'.
- */
-bool isLowerBound(const BSONElement& value, bool isInclusive) {
- switch (value.type()) {
- case NumberInt:
- case NumberDouble:
- case NumberLong:
- case NumberDecimal:
- // Lower bound value for numbers.
- return (std::isinf(value.numberDouble()) || std::isnan(value.numberDouble())) &&
- isInclusive == true;
- case String:
- // Lower bound value for strings.
- return value.str().empty() && isInclusive == true;
- case Date:
- // Lower bound value for dates.
- return value.date() == Date_t::min() && isInclusive == true;
- case bsonTimestamp:
- // Lower bound value for timestamps.
- return value.timestamp() == Timestamp::min() && isInclusive == true;
- case jstOID:
- // Lower bound value for ObjectID.
- return value.OID() == OID() && isInclusive == true;
- case Object:
- case Array:
- // Lower bound value for Object and Array.
- return value.Obj().isEmpty() && isInclusive == true;
- case BinData:
- case EOO:
- case MinKey:
- case MaxKey:
- case Bool: // Boolean bounds are considered always open since they are non-selective.
- case jstNULL:
- case Undefined:
- case Symbol:
- case RegEx:
- case DBRef:
- case Code:
- case CodeWScope:
- return true;
- }
-
- MONGO_UNREACHABLE_TASSERT(8102100);
-}
-
-/**
- * Returns true if the value can serve as a type upper bound for the purposes of type bracketing.
- * The function is designed to work with the 'interesting' for index prefix heuristic types only:
- * Number, String, Date, Timestamp, Boolean, Object, Array, ObjectId. For other types it may return
- * false positive results. The code of the function is based on index bounds build logic from
- * 'index_bounds_builder.cpp'.
- */
-bool isUpperBound(const BSONElement& value, bool isInclusive) {
- switch (value.type()) {
- case NumberInt:
- case NumberDouble:
- case NumberLong:
- case NumberDecimal:
- // Upper bound value for numbers.
- return std::isinf(value.numberDouble()) && isInclusive == true;
- case String:
- // A string value cannot be an upper bound value.
- return false;
- case Date:
- // Upper bound value for Date.
- return value.date() == Date_t::max() && isInclusive == true;
- case bsonTimestamp:
- // Upper bound value for Timestamp.
- return value.timestamp() == Timestamp::max() && isInclusive == true;
- case jstOID:
- // Upper bound value for ObjectID.
- return value.OID() == OID::max() && isInclusive == true;
- case Object:
- // Upper bound value for String.
- return value.Obj().isEmpty() && isInclusive == false;
- case Array:
- // Upper bound value for Object.
- return value.Obj().isEmpty() && isInclusive == false;
- case BinData:
- // Upper bound value for Array.
- return value.valuesize() == 0 && isInclusive == false;
- case EOO:
- case MinKey:
- case MaxKey:
- case Bool: // Boolean bounds are considered always open since they are non-selective.
- case jstNULL:
- case Undefined:
- case Symbol:
- case RegEx:
- case DBRef:
- case Code:
- case CodeWScope:
- return true;
- }
-
- MONGO_UNREACHABLE_TASSERT(8102101);
-}
-
-/**
- * The function tries to detect if the interval is closed on both ends. Can return false
- * positive results for the types not mentioned in the comment to 'isMinMaxValue' function.
- */
-bool isClosedInterval(const Interval& interval) {
- // If the bound types are different the interval is considered to be open.
- if (interval.start.type() != interval.end.type()) {
- return false;
- }
-
- switch (interval.getDirection()) {
- // Point intervals, empty intervals, and null intervals have no direction.
- case Interval::Direction::kDirectionNone:
- return true;
- case Interval::Direction::kDirectionAscending:
- return !isLowerBound(interval.start, interval.startInclusive) &&
- !isUpperBound(interval.end, interval.endInclusive);
- case Interval::Direction::kDirectionDescending:
- return !isUpperBound(interval.start, interval.startInclusive) &&
- !isLowerBound(interval.end, interval.endInclusive);
- }
-
- MONGO_UNREACHABLE_TASSERT(8102102);
-}
-
-/**
- * Returns true if this OIL contains only closed intervals.
- */
-bool containsOnlyClosedIntervals(const OrderedIntervalList& oil) {
- for (const auto& interval : oil.intervals) {
- if (!isClosedInterval(interval)) {
- return false;
- }
- }
-
- return true;
-}
-
-/**
- * Calculates score for the given index bounds. The score reflects the following rules:
- * - IndexBounds that has longest single point interval prefix wins,
- * - if winner is not defined on the previous step then IndexBounds with the longest point
- * interval prefix wins,
- * - if winner is not defined on the previous step then IndexBounds with the longest closed
- * interval prefix wins,
- * - if winner is not defined, then IndexBounds with longest interval prefix wins
- * - if winner is not defined, them IndexBounds with shortest index key pattern wins.
- */
-uint64_t getIndexBoundsScore(const IndexBounds& bounds) {
- const uint64_t indexKeyLength = static_cast<uint64_t>(bounds.fields.size());
- uint64_t singlePointIntervalPrefix = 0;
- uint64_t pointsIntervalPrefix = 0;
- uint64_t closedIntervalPrefix = 0;
- uint64_t intervalLength = 0;
-
- for (const auto& field : bounds.fields) {
- // Skip the $** index virtual field, as it's not part of the actual index key.
- if (field.name == "$_path") {
- continue;
- }
-
- // Stop scoring index bounds as soon as we see an all-values interval.
- if (field.isMinToMax() || field.isMaxToMin()) {
- break;
- }
-
- if (intervalLength == singlePointIntervalPrefix && field.isPoint()) {
- ++singlePointIntervalPrefix;
- }
-
- if (intervalLength == pointsIntervalPrefix && field.containsOnlyPointIntervals()) {
- ++pointsIntervalPrefix;
- }
-
- if (intervalLength == closedIntervalPrefix && containsOnlyClosedIntervals(field)) {
- ++closedIntervalPrefix;
- }
-
- ++intervalLength;
- }
-
- // We pack calculated stats into one value to make their comparison simplier. For every
- // prefix length we allocate 12 bits (4096 values) which is more then enough since an index
- // can have no more than 32 fields (see "MongoDB Limits and Thresholds" reference).
- // 'indexKeyLength' is treated differently because, unlike others, we prefer shorter index
- // key prefix length (see the comment to the function for details).
- uint64_t result = (singlePointIntervalPrefix << 52) | (pointsIntervalPrefix << 40) |
- (closedIntervalPrefix << 28) | (intervalLength << 16) |
- (std::numeric_limits<uint16_t>::max() - indexKeyLength);
-
- return result;
-}
-
-/**
- * Calculates scores for the given IndexBounds and add 1 to every winner's resultScores. i-th
- * position in resultScores corresponds to i-th field in IndexBound.
- */
-void scoreIndexBounds(const std::vector<const IndexBounds*>& bounds,
- std::vector<size_t>& resultScores) {
- const size_t nfields = bounds.size();
-
- std::vector<uint64_t> scores{};
- scores.reserve(nfields);
- for (size_t i = 0; i < bounds.size(); ++i) {
- scores.emplace_back(getIndexBoundsScore(*bounds[i]));
- }
-
- auto topScore = max_element(scores.begin(), scores.end());
- for (size_t i = 0; i < nfields; ++i) {
- if (*topScore == scores[i]) {
- resultScores[i] += 1;
- }
- }
-}
} // namespace
std::unique_ptr<PlanScorer<PlanStageStats>> makePlanScorer() {
return std::make_unique<DefaultPlanScorer>();
}
-
-std::vector<size_t> applyIndexPrefixHeuristic(const std::vector<const QuerySolution*>& solutions) {
- std::vector<size_t> solutionScores(solutions.size(), 0);
-
- std::vector<std::vector<const QuerySolutionNode*>> stack{};
- stack.emplace_back();
- stack.back().reserve(solutions.size());
- for (auto solution : solutions) {
- stack.back().emplace_back(solution->root());
- }
-
- while (!stack.empty()) {
- auto top = std::move(stack.back());
- stack.pop_back();
-
- if (!areNodesCompatible(top)) {
- return {};
- }
-
- // Compatible nodes have the same number of children, see comment to 'areNodesCompatible'
- // function.
- for (size_t childIndex = 0; childIndex < top.front()->children.size(); ++childIndex) {
- stack.emplace_back();
- stack.back().reserve(solutions.size());
- for (auto node : top) {
- stack.back().emplace_back(node->children[childIndex]);
- }
- }
-
- if (top.front()->getType() == STAGE_IXSCAN) {
- std::vector<const IndexBounds*> bounds{};
- bounds.reserve(solutions.size());
-
- for (auto node : top) {
- bounds.emplace_back(&static_cast<const IndexScanNode*>(node)->bounds);
- }
-
- scoreIndexBounds(bounds, solutionScores);
- }
- }
-
- std::vector<size_t> winningSolutionIndices{};
- winningSolutionIndices.reserve(solutions.size());
- const auto topScore = max_element(solutionScores.begin(), solutionScores.end());
- for (size_t index = 0; index < solutionScores.size(); ++index) {
- if (solutionScores[index] == *topScore) {
- winningSolutionIndices.emplace_back(index);
- }
- }
-
- return winningSolutionIndices;
-}
} // namespace mongo::plan_ranker