bool run(OperationContext* opCtx,
             const std::string& dbname,
             const BSONObj& cmdObj,
             BSONObjBuilder& result) override {
        // Although it is a command, a find command gets counted as a query.
        globalOpCounters.gotQuery();

        // Parse the command BSON to a QueryRequest.
        const bool isExplain = false;
        // Pass parseNs to makeFromFindCommand in case cmdObj does not have a UUID.
        auto qrStatus = QueryRequest::makeFromFindCommand(
            NamespaceString(parseNs(dbname, cmdObj)), cmdObj, isExplain);
        if (!qrStatus.isOK()) {
            return appendCommandStatus(result, qrStatus.getStatus());
        }

        auto& qr = qrStatus.getValue();

        // Validate term before acquiring locks, if provided.
        if (auto term = qr->getReplicationTerm()) {
            auto replCoord = repl::ReplicationCoordinator::get(opCtx);
            Status status = replCoord->updateTerm(opCtx, *term);
            // Note: updateTerm returns ok if term stayed the same.
            if (!status.isOK()) {
                return appendCommandStatus(result, status);
            }
        }

        // Acquire locks. If the query is on a view, we release our locks and convert the query
        // request into an aggregation command.
        Lock::DBLock dbSLock(opCtx, dbname, MODE_IS);
        const NamespaceString nss(parseNsOrUUID(opCtx, dbname, cmdObj));
        qr->refreshNSS(opCtx);

        // Fill out curop information.
        //
        // We pass negative values for 'ntoreturn' and 'ntoskip' to indicate that these values
        // should be omitted from the log line. Limit and skip information is already present in the
        // find command parameters, so these fields are redundant.
        const int ntoreturn = -1;
        const int ntoskip = -1;
        beginQueryOp(opCtx, nss, cmdObj, ntoreturn, ntoskip);

        // Finish the parsing step by using the QueryRequest to create a CanonicalQuery.
        ExtensionsCallbackReal extensionsCallback(opCtx, &nss);
        const boost::intrusive_ptr<ExpressionContext> expCtx;
        auto statusWithCQ =
            CanonicalQuery::canonicalize(opCtx,
                                         std::move(qr),
                                         expCtx,
                                         extensionsCallback,
                                         MatchExpressionParser::kAllowAllSpecialFeatures &
                                             ~MatchExpressionParser::AllowedFeatures::kIsolated);
        if (!statusWithCQ.isOK()) {
            return appendCommandStatus(result, statusWithCQ.getStatus());
        }
        std::unique_ptr<CanonicalQuery> cq = std::move(statusWithCQ.getValue());

        AutoGetCollectionOrViewForReadCommand ctx(opCtx, nss, std::move(dbSLock));
        Collection* collection = ctx.getCollection();
        if (ctx.getView()) {
            // Relinquish locks. The aggregation command will re-acquire them.
            ctx.releaseLocksForView();

            // Convert the find command into an aggregation using $match (and other stages, as
            // necessary), if possible.
            const auto& qr = cq->getQueryRequest();
            auto viewAggregationCommand = qr.asAggregationCommand();
            if (!viewAggregationCommand.isOK())
                return appendCommandStatus(result, viewAggregationCommand.getStatus());

            BSONObj aggResult = Command::runCommandDirectly(
                opCtx,
                OpMsgRequest::fromDBAndBody(dbname, std::move(viewAggregationCommand.getValue())));
            auto status = getStatusFromCommandResult(aggResult);
            if (status.code() == ErrorCodes::InvalidPipelineOperator) {
                return appendCommandStatus(
                    result,
                    {ErrorCodes::InvalidPipelineOperator,
                     str::stream() << "Unsupported in view pipeline: " << status.reason()});
            }
            result.resetToEmpty();
            result.appendElements(aggResult);
            return status.isOK();
        }

        // Get the execution plan for the query.
        auto statusWithPlanExecutor =
            getExecutorFind(opCtx, collection, nss, std::move(cq), PlanExecutor::YIELD_AUTO);
        if (!statusWithPlanExecutor.isOK()) {
            return appendCommandStatus(result, statusWithPlanExecutor.getStatus());
        }

        auto exec = std::move(statusWithPlanExecutor.getValue());

        {
            stdx::lock_guard<Client> lk(*opCtx->getClient());
            CurOp::get(opCtx)->setPlanSummary_inlock(Explain::getPlanSummary(exec.get()));
        }

        if (!collection) {
            // No collection. Just fill out curop indicating that there were zero results and
            // there is no ClientCursor id, and then return.
            const long long numResults = 0;
            const CursorId cursorId = 0;
            endQueryOp(opCtx, collection, *exec, numResults, cursorId);
            appendCursorResponseObject(cursorId, nss.ns(), BSONArray(), &result);
            return true;
        }

        const QueryRequest& originalQR = exec->getCanonicalQuery()->getQueryRequest();

        // Stream query results, adding them to a BSONArray as we go.
        CursorResponseBuilder firstBatch(/*isInitialResponse*/ true, &result);
        BSONObj obj;
        PlanExecutor::ExecState state = PlanExecutor::ADVANCED;
        long long numResults = 0;
        while (!FindCommon::enoughForFirstBatch(originalQR, numResults) &&
               PlanExecutor::ADVANCED == (state = exec->getNext(&obj, NULL))) {
            // If we can't fit this result inside the current batch, then we stash it for later.
            if (!FindCommon::haveSpaceForNext(obj, numResults, firstBatch.bytesUsed())) {
                exec->enqueue(obj);
                break;
            }

            // Add result to output buffer.
            firstBatch.append(obj);
            numResults++;
        }

        // Throw an assertion if query execution fails for any reason.
        if (PlanExecutor::FAILURE == state || PlanExecutor::DEAD == state) {
            firstBatch.abandon();
            error() << "Plan executor error during find command: " << PlanExecutor::statestr(state)
                    << ", stats: " << redact(Explain::getWinningPlanStats(exec.get()));

            return appendCommandStatus(result,
                                       WorkingSetCommon::getMemberObjectStatus(obj).withContext(
                                           "Executor error during find command"));
        }

        // Before saving the cursor, ensure that whatever plan we established happened with the
        // expected collection version
        auto css = CollectionShardingState::get(opCtx, nss);
        css->checkShardVersionOrThrow(opCtx);

        // Set up the cursor for getMore.
        CursorId cursorId = 0;
        if (shouldSaveCursor(opCtx, collection, state, exec.get())) {
            // Create a ClientCursor containing this plan executor and register it with the cursor
            // manager.
            ClientCursorPin pinnedCursor = collection->getCursorManager()->registerCursor(
                opCtx,
                {std::move(exec),
                 nss,
                 AuthorizationSession::get(opCtx->getClient())->getAuthenticatedUserNames(),
                 opCtx->recoveryUnit()->isReadingFromMajorityCommittedSnapshot(),
                 cmdObj});
            cursorId = pinnedCursor.getCursor()->cursorid();

            invariant(!exec);
            PlanExecutor* cursorExec = pinnedCursor.getCursor()->getExecutor();

            // State will be restored on getMore.
            cursorExec->saveState();
            cursorExec->detachFromOperationContext();

            // We assume that cursors created through a DBDirectClient are always used from their
            // original OperationContext, so we do not need to move time to and from the cursor.
            if (!opCtx->getClient()->isInDirectClient()) {
                pinnedCursor.getCursor()->setLeftoverMaxTimeMicros(
                    opCtx->getRemainingMaxTimeMicros());
            }
            pinnedCursor.getCursor()->setPos(numResults);

            // Fill out curop based on the results.
            endQueryOp(opCtx, collection, *cursorExec, numResults, cursorId);
        } else {
            endQueryOp(opCtx, collection, *exec, numResults, cursorId);
        }

        // Generate the response object to send to the client.
        firstBatch.done(cursorId, nss.ns());
        return true;
    }