Ejemplo n.º 1
0
    Status explainPlan(const PlanStageStats& stats, TypeExplain** explain, bool fullDetails) {
        auto_ptr<TypeExplain> res(new TypeExplain);

        // Descend the plan looking for structural properties:
        // + is there any 'or's (TODO ands)? if so, prepare to explain each branch recursively
        // + is is a collection scan or a an index scan?
        // + if the latter, was it covered?
        // + was a sort necessary?
        //
        // TODO: For now, we assume that at most one index is used in a plan
        bool covered = true;
        bool sortPresent = false;
        const PlanStageStats* logicalStage = NULL;
        const PlanStageStats* root = &stats;
        const PlanStageStats* leaf = root;
        while (leaf->children.size() > 0) {

            // We're failing a plan with multiple children other than OR.
            // TODO: explain richer plans.
            if (leaf->children.size() > 1 && !isLogicalStage(leaf->stageType)) {
                res->setCursor("Complex Plan");
                res->setNScanned(0);
                res->setNScannedObjects(0);
                *explain = res.release();
                return Status::OK();
            }

            if (isLogicalStage(leaf->stageType)) {
                logicalStage = leaf;
                break;
            }
            if (leaf->stageType == STAGE_FETCH) {
                covered = false;
            }
            if (leaf->stageType == STAGE_SORT) {
                sortPresent = true;
            }
            leaf = leaf->children[0];
        }

        // How many documents did the query return?
        res->setN(root->common.advanced);

        // Accounting for 'nscanned' and 'nscannedObjects' is specific to the kind of leaf:
        //
        // + on collection scan, both are the same; all the documents retrieved were
        //   fetched in practice. To get how many documents were retrieved, one simply
        //   looks at the number of 'advanced' in the stats.
        //
        // + on an index scan, we'd neeed to look into the index scan cursor to extract the
        //   number of keys that cursor retrieved, and into the stage's stats 'advanced' for
        //   nscannedObjects', which would be the number of keys that survived the IXSCAN
        //   filter. Those keys would have been FETCH-ed, if a fetch is present.

        if (logicalStage != NULL) {
            uint64_t nScanned = 0;
            uint64_t nScannedObjects = 0;
            bool isMultiKey = false;
            bool isIndexOnly = covered;
            const std::vector<PlanStageStats*>& children = logicalStage->children;
            for (std::vector<PlanStageStats*>::const_iterator it = children.begin();
                 it != children.end();
                 ++it) {
                TypeExplain* childExplain = NULL;
                explainPlan(**it, &childExplain, false /* no full details */);
                if (childExplain) {
                    res->addToClauses(childExplain);
                    nScanned += childExplain->getNScanned();

                    // We don't necessarilly fetch on a branch, but the old query framework
                    // did. We're still emulating the number it would have produced.
                    nScannedObjects += childExplain->getNScanned();

                    isMultiKey |= childExplain->getIsMultiKey();
                    isIndexOnly &= childExplain->getIndexOnly();
                }
            }
            res->setNScanned(nScanned);
            res->setNScannedObjects(nScannedObjects);
        }
        else if (leaf->stageType == STAGE_COLLSCAN) {
            CollectionScanStats* csStats = static_cast<CollectionScanStats*>(leaf->specific.get());
            res->setCursor("BasicCursor");
            res->setNScanned(csStats->docsTested);
            res->setNScannedObjects(csStats->docsTested);
        }
        else if (leaf->stageType == STAGE_GEO_NEAR_2DSPHERE) {
            // TODO: This is kind of a lie for STAGE_GEO_NEAR_2DSPHERE.
            res->setCursor("S2NearCursor");
            // The first work() is an init.  Every subsequent work examines a document.
            res->setNScanned(leaf->common.works);
            res->setNScannedObjects(leaf->common.works);
            // TODO: Could be multikey.
            res->setIsMultiKey(false);
            res->setIndexOnly(false);
        }
        else if (leaf->stageType == STAGE_GEO_NEAR_2D) {
            // TODO: This is kind of a lie.
            res->setCursor("GeoSearchCursor");
            // The first work() is an init.  Every subsequent work examines a document.
            res->setNScanned(leaf->common.works);
            res->setNScannedObjects(leaf->common.works);
            // TODO: Could be multikey.
            res->setIsMultiKey(false);
            res->setIndexOnly(false);
        }
        else if (leaf->stageType == STAGE_IXSCAN) {
            IndexScanStats* indexStats = static_cast<IndexScanStats*>(leaf->specific.get());
            dassert(indexStats);
            string direction = indexStats > 0 ? "" : " reverse";
            res->setCursor(indexStats->indexType + " " + indexStats->indexName + direction);
            res->setNScanned(indexStats->keysExamined);

            // If we're covered, that is, no FETCH is present, then, by definition,
            // nScannedObject would be zero because no full document would have been fetched
            // from disk.
            res->setNScannedObjects(covered ? 0 : leaf->common.advanced);

            res->setIndexBounds(indexStats->indexBounds);
            res->setIsMultiKey(indexStats->isMultiKey);
            res->setIndexOnly(covered);
        }
        else {
            return Status(ErrorCodes::InternalError, "cannot interpret execution plan");
        }

        res->setScanAndOrder(sortPresent);

        // Statistics for the plan (appear only in a detailed mode)
        // TODO: if we can get this from the runner, we can kill "detailed mode"
        if (fullDetails) {
            res->setNYields(root->common.yields);
        }

        *explain = res.release();
        return Status::OK();
    }