std::unique_ptr<QuerySolution> QueryPlannerAnalysis::analyzeDataAccess( const CanonicalQuery& query, const QueryPlannerParams& params, std::unique_ptr<QuerySolutionNode> solnRoot) { auto soln = std::make_unique<QuerySolution>(); soln->filterData = query.getQueryObj(); soln->indexFilterApplied = params.indexFiltersApplied; solnRoot->computeProperties(); analyzeGeo(params, solnRoot.get()); // solnRoot finds all our results. Let's see what transformations we must perform to the // data. // If we're answering a query on a sharded system, we need to drop documents that aren't // logically part of our shard. if (params.options & QueryPlannerParams::INCLUDE_SHARD_FILTER) { if (!solnRoot->fetched()) { // See if we need to fetch information for our shard key. // NOTE: Solution nodes only list ordinary, non-transformed index keys for now bool fetch = false; BSONObjIterator it(params.shardKey); while (it.more()) { BSONElement nextEl = it.next(); if (!solnRoot->hasField(nextEl.fieldName())) { fetch = true; break; } } if (fetch) { FetchNode* fetchNode = new FetchNode(); fetchNode->children.push_back(solnRoot.release()); solnRoot.reset(fetchNode); } } ShardingFilterNode* sfn = new ShardingFilterNode(); sfn->children.push_back(solnRoot.release()); solnRoot.reset(sfn); } bool hasSortStage = false; solnRoot.reset(analyzeSort(query, params, solnRoot.release(), &hasSortStage)); // This can happen if we need to create a blocking sort stage and we're not allowed to. if (!solnRoot) { return nullptr; } // A solution can be blocking if it has a blocking sort stage or // a hashed AND stage. bool hasAndHashStage = hasNode(solnRoot.get(), STAGE_AND_HASH); soln->hasBlockingStage = hasSortStage || hasAndHashStage; const QueryRequest& qr = query.getQueryRequest(); if (qr.getSkip()) { auto skip = std::make_unique<SkipNode>(); skip->skip = *qr.getSkip(); skip->children.push_back(solnRoot.release()); solnRoot = std::move(skip); } // Project the results. if (query.getProj()) { solnRoot = analyzeProjection(query, std::move(solnRoot), hasSortStage); // If we don't have a covered project, and we're not allowed to put an uncovered one in, // bail out. if (solnRoot->fetched() && params.options & QueryPlannerParams::NO_UNCOVERED_PROJECTIONS) return nullptr; } else { // If there's no projection, we must fetch, as the user wants the entire doc. if (!solnRoot->fetched() && !(params.options & QueryPlannerParams::IS_COUNT)) { FetchNode* fetch = new FetchNode(); fetch->children.push_back(solnRoot.release()); solnRoot.reset(fetch); } } // When there is both a blocking sort and a limit, the limit will // be enforced by the blocking sort. // Otherwise, we need to limit the results in the case of a hard limit // (ie. limit in raw query is negative) if (!hasSortStage) { // We don't have a sort stage. This means that, if there is a limit, we will have // to enforce it ourselves since it's not handled inside SORT. if (qr.getLimit()) { LimitNode* limit = new LimitNode(); limit->limit = *qr.getLimit(); limit->children.push_back(solnRoot.release()); solnRoot.reset(limit); } else if (qr.getNToReturn() && !qr.wantMore()) { // We have a "legacy limit", i.e. a negative ntoreturn value from an OP_QUERY style // find. LimitNode* limit = new LimitNode(); limit->limit = *qr.getNToReturn(); limit->children.push_back(solnRoot.release()); solnRoot.reset(limit); } } soln->root = std::move(solnRoot); return soln; }