diff options
Diffstat (limited to 'src/mongo/db/query/query_planner.cpp')
| -rw-r--r-- | src/mongo/db/query/query_planner.cpp | 136 |
1 files changed, 83 insertions, 53 deletions
diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp index c258c9e6867..e04516a6330 100644 --- a/src/mongo/db/query/query_planner.cpp +++ b/src/mongo/db/query/query_planner.cpp @@ -252,6 +252,11 @@ void tryToAddColumnScan(const QueryPlannerParams& params, // collection scan. Add that solution. out.push_back(QueryPlannerAnalysis::analyzeDataAccess(query, params, std::move(columnScan))); } + +bool collscanIsBounded(const CollectionScanNode* collscan) { + return collscan->minRecord || collscan->maxRecord; +} + } // namespace using std::numeric_limits; @@ -469,13 +474,57 @@ static BSONObj finishMaxObj(const IndexEntry& indexEntry, } } +bool providesSort(const CanonicalQuery& query, const BSONObj& kp) { + return query.getFindCommandRequest().getSort().isPrefixOf( + kp, SimpleBSONElementComparator::kInstance); +} + +/** + * Determine whether this query has a sort that can be provided by the clustered index, if so, which + * direction the scan should be. If the collection is not clustered, or the sort cannot be provided, + * returns 'boost::none'. + */ +boost::optional<int> determineClusteredScanDirection(const CanonicalQuery& query, + const QueryPlannerParams& params) { + if (params.clusteredInfo && query.getSortPattern() && + CollatorInterface::collatorsMatch(params.clusteredCollectionCollator, + query.getCollator())) { + auto kp = clustered_util::getSortPattern(params.clusteredInfo->getIndexSpec()); + if (providesSort(query, kp)) { + return 1; + } else if (providesSort(query, QueryPlannerCommon::reverseSortObj(kp))) { + return -1; + } + } + + return boost::none; +} + +/** + * Determine the direction of the scan needed for the query. Defaults to 1 unless this is a + * clustered collection and we have a sort that can be provided by the clustered index. + */ +int determineCollscanDirection(const CanonicalQuery& query, const QueryPlannerParams& params) { + return determineClusteredScanDirection(query, params).value_or(1); +} + +std::pair<std::unique_ptr<QuerySolution>, const CollectionScanNode*> buildCollscanSolnWithNode( + const CanonicalQuery& query, + bool tailable, + const QueryPlannerParams& params, + boost::optional<int> direction = boost::none) { + std::unique_ptr<QuerySolutionNode> solnRoot(QueryPlannerAccess::makeCollectionScan( + query, tailable, params, direction.value_or(determineCollscanDirection(query, params)))); + const auto* collscanNode = checked_cast<const CollectionScanNode*>(solnRoot.get()); + return std::make_pair( + QueryPlannerAnalysis::analyzeDataAccess(query, params, std::move(solnRoot)), collscanNode); +} + std::unique_ptr<QuerySolution> buildCollscanSoln(const CanonicalQuery& query, bool tailable, const QueryPlannerParams& params, - int direction = 1) { - std::unique_ptr<QuerySolutionNode> solnRoot( - QueryPlannerAccess::makeCollectionScan(query, tailable, params, direction)); - return QueryPlannerAnalysis::analyzeDataAccess(query, params, std::move(solnRoot)); + boost::optional<int> direction = boost::none) { + return buildCollscanSolnWithNode(query, tailable, params, direction).first; } std::unique_ptr<QuerySolution> buildWholeIXSoln( @@ -491,11 +540,6 @@ std::unique_ptr<QuerySolution> buildWholeIXSoln( return QueryPlannerAnalysis::analyzeDataAccess(query, params, std::move(solnRoot)); } -bool providesSort(const CanonicalQuery& query, const BSONObj& kp) { - return query.getFindCommandRequest().getSort().isPrefixOf( - kp, SimpleBSONElementComparator::kInstance); -} - StatusWith<std::unique_ptr<PlanCacheIndexTree>> QueryPlanner::cacheDataFromTaggedTree( const MatchExpression* const taggedTree, const vector<IndexEntry>& relevantIndices) { if (!taggedTree) { @@ -661,7 +705,7 @@ StatusWith<std::unique_ptr<QuerySolution>> QueryPlanner::planFromCache( } else if (SolutionCacheData::COLLSCAN_SOLN == winnerCacheData.solnType) { // The cached solution is a collection scan. We don't cache collscans // with tailable==true, hence the false below. - auto soln = buildCollscanSoln(query, false, params); + auto soln = buildCollscanSoln(query, false, params, winnerCacheData.wholeIXSolnDir); if (!soln) { return Status(ErrorCodes::NoQueryExecutionPlans, "plan cache error: collection scan soln"); @@ -683,10 +727,11 @@ StatusWith<std::unique_ptr<QuerySolution>> QueryPlanner::planFromCache( "filter"_attr = redact(clone->debugString()), "cacheData"_attr = redact(winnerCacheData.toString())); - stdx::unordered_set<string> fields; + RelevantFieldIndexMap fields; QueryPlannerIXSelect::getFields(query.root(), &fields); + // We will not cache queries with 'hint'. std::vector<IndexEntry> expandedIndexes = - QueryPlannerIXSelect::expandIndexes(fields, params.indices); + QueryPlannerIXSelect::expandIndexes(fields, params.indices, false /* indexHinted */); // Map from index name to index number. map<IndexEntry::Identifier, size_t> indexMap; @@ -888,13 +933,14 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan( } // Figure out what fields we care about. - stdx::unordered_set<string> fields; + RelevantFieldIndexMap fields; QueryPlannerIXSelect::getFields(query.root(), &fields); for (auto&& field : fields) { - LOGV2_DEBUG(20970, 5, "Predicate over field", "field"_attr = field); + LOGV2_DEBUG(20970, 5, "Predicate over field", "field"_attr = field.first); } - fullIndexList = QueryPlannerIXSelect::expandIndexes(fields, std::move(fullIndexList)); + fullIndexList = QueryPlannerIXSelect::expandIndexes( + fields, std::move(fullIndexList), !hintedIndex.isEmpty()); std::vector<IndexEntry> relevantIndices; if (!hintedIndexEntry) { @@ -1257,37 +1303,6 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan( } } } - - // The base index is sorted on some key, so it's possible we might want to use - // a collection scan to provide the sort requested - if (params.clusteredInfo) { - if (CollatorInterface::collatorsMatch(params.clusteredCollectionCollator, - query.getCollator())) { - auto kp = clustered_util::getSortPattern(params.clusteredInfo->getIndexSpec()); - int direction = 0; - if (providesSort(query, kp)) { - direction = 1; - } else if (providesSort(query, QueryPlannerCommon::reverseSortObj(kp))) { - direction = -1; - } - - if (direction != 0) { - auto soln = buildCollscanSoln(query, isTailable, params, direction); - if (soln) { - LOGV2_DEBUG(6082401, - 5, - "Planner: outputting soln that uses clustered index to " - "provide sort"); - SolutionCacheData* scd = new SolutionCacheData(); - scd->solnType = SolutionCacheData::COLLSCAN_SOLN; - scd->wholeIXSolnDir = direction; - - soln->cacheData.reset(scd); - out.push_back(std::move(soln)); - } - } - } - } } // If a projection exists, there may be an index that allows for a covered plan, even if @@ -1339,6 +1354,8 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan( "No indexed plans available, and running with 'notablescan'"); } + bool clusteredCollection = params.clusteredInfo.has_value(); + // geoNear and text queries *require* an index. // Also, if a hint is specified it indicates that we MUST use it. bool possibleToCollscan = @@ -1348,21 +1365,34 @@ StatusWith<std::vector<std::unique_ptr<QuerySolution>>> QueryPlanner::plan( return Status(ErrorCodes::NoQueryExecutionPlans, "No query solutions"); } - if (possibleToCollscan && (collscanRequested || collScanRequired)) { - auto collscan = buildCollscanSoln(query, isTailable, params); - if (!collscan && collScanRequired) { + if (possibleToCollscan && (collscanRequested || collScanRequired || clusteredCollection)) { + auto clusteredScanDirection = determineClusteredScanDirection(query, params); + auto direction = clusteredScanDirection.value_or(1); + auto [collscanSoln, collscanNode] = + buildCollscanSolnWithNode(query, isTailable, params, direction); + if (!collscanSoln && collScanRequired) { return Status(ErrorCodes::NoQueryExecutionPlans, "Failed to build collection scan soln"); } - if (collscan) { + + // We consider collection scan in the following cases: + // 1. collScanRequested - specifically requested by caller. + // 2. collScanRequired - there are no other possible plans, so we fallback to full scan. + // 3. collscanIsBounded - collection is clustered and clustered index is used. + // 4. clusteredScanDirection - collection is clustered and sort, provided by clustered + // index, is used + if (collscanSoln && + (collscanRequested || collScanRequired || collscanIsBounded(collscanNode) || + clusteredScanDirection)) { LOGV2_DEBUG(20984, 5, "Planner: outputting a collection scan", - "collectionScan"_attr = redact(collscan->toString())); + "collectionScan"_attr = redact(collscanSoln->toString())); SolutionCacheData* scd = new SolutionCacheData(); scd->solnType = SolutionCacheData::COLLSCAN_SOLN; - collscan->cacheData.reset(scd); - out.push_back(std::move(collscan)); + scd->wholeIXSolnDir = direction; + collscanSoln->cacheData.reset(scd); + out.push_back(std::move(collscanSoln)); } } |
