shared_ptr<PlanExecutor> PipelineD::addCursorSource(const intrusive_ptr<Pipeline>& pipeline, const intrusive_ptr<ExpressionContext>& expCtx, shared_ptr<PlanExecutor> exec, DepsTracker deps, const BSONObj& queryObj, const BSONObj& sortObj, const BSONObj& projectionObj) { // Get the full "namespace" name. const string& fullName = expCtx->ns.ns(); // Put the PlanExecutor into a DocumentSourceCursor and add it to the front of the pipeline. intrusive_ptr<DocumentSourceCursor> pSource = DocumentSourceCursor::create(fullName, exec, expCtx); // Note the query, sort, and projection for explain. pSource->setQuery(queryObj); pSource->setSort(sortObj); if (deps.hasNoRequirements()) { pSource->shouldProduceEmptyDocs(); } if (!projectionObj.isEmpty()) { pSource->setProjection(projectionObj, boost::none); } else { // There may be fewer dependencies now if the sort was covered. if (!sortObj.isEmpty()) { deps = pipeline->getDependencies(queryObj); } pSource->setProjection(deps.toProjection(), deps.toParsedDeps()); } // Add the initial DocumentSourceCursor to the front of the pipeline. Then optimize again in // case the new stage can be absorbed with the first stages of the pipeline. pipeline->addInitialSource(pSource); pipeline->optimizePipeline(); // DocumentSourceCursor expects a yielding PlanExecutor that has had its state saved. We // deregister the PlanExecutor so that it can be registered with ClientCursor. exec->deregisterExec(); exec->saveState(); return exec; }
std::shared_ptr<PlanExecutor> PipelineD::prepareExecutor( OperationContext* txn, Collection* collection, const NamespaceString& nss, const intrusive_ptr<Pipeline>& pipeline, const intrusive_ptr<ExpressionContext>& expCtx, const intrusive_ptr<DocumentSourceSort>& sortStage, const DepsTracker& deps, const BSONObj& queryObj, BSONObj* sortObj, BSONObj* projectionObj) { // The query system has the potential to use an index to provide a non-blocking sort and/or to // use the projection to generate a covered plan. If this is possible, it is more efficient to // let the query system handle those parts of the pipeline. If not, it is more efficient to use // a $sort and/or a ParsedDeps object. Thus, we will determine whether the query system can // provide a non-blocking sort or a covered projection before we commit to a PlanExecutor. // // To determine if the query system can provide a non-blocking sort, we pass the // NO_BLOCKING_SORT planning option, meaning 'getExecutor' will not produce a PlanExecutor if // the query system would use a blocking sort stage. // // To determine if the query system can provide a covered projection, we pass the // NO_UNCOVERED_PROJECTS planning option, meaning 'getExecutor' will not produce a PlanExecutor // if the query system would need to fetch the document to do the projection. The following // logic uses the above strategies, with multiple calls to 'attemptToGetExecutor' to determine // the most efficient way to handle the $sort and $project stages. // // LATER - We should attempt to determine if the results from the query are returned in some // order so we can then apply other optimizations there are tickets for, such as SERVER-4507. size_t plannerOpts = QueryPlannerParams::DEFAULT | QueryPlannerParams::NO_BLOCKING_SORT; // If we are connecting directly to the shard rather than through a mongos, don't filter out // orphaned documents. if (ShardingState::get(txn)->needCollectionMetadata(txn, nss.ns())) { plannerOpts |= QueryPlannerParams::INCLUDE_SHARD_FILTER; } if (deps.hasNoRequirements()) { // If we don't need any fields from the input document, performing a count is faster, and // will output empty documents, which is okay. plannerOpts |= QueryPlannerParams::IS_COUNT; } // The only way to get a text score is to let the query system handle the projection. In all // other cases, unless the query system can do an index-covered projection and avoid going to // the raw record at all, it is faster to have ParsedDeps filter the fields we need. if (!deps.needTextScore) { plannerOpts |= QueryPlannerParams::NO_UNCOVERED_PROJECTIONS; } std::shared_ptr<PlanExecutor> exec; BSONObj emptyProjection; if (sortStage) { // See if the query system can provide a non-blocking sort. auto swExecutorSort = attemptToGetExecutor( txn, collection, expCtx, queryObj, emptyProjection, *sortObj, plannerOpts); if (swExecutorSort.isOK()) { // Success! Now see if the query system can also cover the projection. auto swExecutorSortAndProj = attemptToGetExecutor( txn, collection, expCtx, queryObj, *projectionObj, *sortObj, plannerOpts); if (swExecutorSortAndProj.isOK()) { // Success! We have a non-blocking sort and a covered projection. exec = std::move(swExecutorSortAndProj.getValue()); } else { // The query system couldn't cover the projection. *projectionObj = BSONObj(); exec = std::move(swExecutorSort.getValue()); } // We know the sort is being handled by the query system, so remove the $sort stage. pipeline->sources.pop_front(); if (sortStage->getLimitSrc()) { // We need to reinsert the coalesced $limit after removing the $sort. pipeline->sources.push_front(sortStage->getLimitSrc()); } return exec; } // The query system can't provide a non-blocking sort. *sortObj = BSONObj(); } // Either there was no $sort stage, or the query system could not provide a non-blocking // sort. dassert(sortObj->isEmpty()); // See if the query system can cover the projection. auto swExecutorProj = attemptToGetExecutor( txn, collection, expCtx, queryObj, *projectionObj, *sortObj, plannerOpts); if (swExecutorProj.isOK()) { // Success! We have a covered projection. return std::move(swExecutorProj.getValue()); } // The query system couldn't provide a covered projection. *projectionObj = BSONObj(); // If this doesn't work, nothing will. return uassertStatusOK(attemptToGetExecutor( txn, collection, expCtx, queryObj, *projectionObj, *sortObj, plannerOpts)); }