// static
    Status PlanCacheListPlans::list(const PlanCache& planCache, const std::string& ns,
                                    const BSONObj& cmdObj, BSONObjBuilder* bob) {
        CanonicalQuery* cqRaw;
        Status status = canonicalize(ns, cmdObj, &cqRaw);
        if (!status.isOK()) {
            return status;
        }

        scoped_ptr<CanonicalQuery> cq(cqRaw);
        CachedSolution* crRaw;
        Status result = planCache.get(*cq, &crRaw);
        if (!result.isOK()) {
            return result;
        }
        scoped_ptr<CachedSolution> cr(crRaw);

        BSONArrayBuilder plansBuilder(bob->subarrayStart("plans"));
        size_t numPlans = cr->plannerData.size();
        for (size_t i = 0; i < numPlans; ++i) {
            BSONObjBuilder planBob(plansBuilder.subobjStart());

            // Create plan details field.
            // Currently, simple string representationg of
            // SolutionCacheData. Need to revisit format when we
            // need to parse user-provided plan details for planCacheAddPlan.
            SolutionCacheData* scd = cr->plannerData[i];
            BSONObjBuilder detailsBob(planBob.subobjStart("details"));
            detailsBob.append("solution", scd->toString());
            detailsBob.doneFast();

            // XXX: Fix these field values once we have fleshed out cache entries.
            //      reason should contain initial plan stats and score from ranking process.
            //      feedback should contain execution stats from running the query to completion.
            planBob.append("reason", BSONObj());
            planBob.append("feedback", BSONObj());
            planBob.append("hint", scd->adminHintApplied);
        }
        plansBuilder.doneFast();

        return Status::OK();
    }
    // static
    Status PlanCacheListPlans::list(OperationContext* txn,
                                    const PlanCache& planCache,
                                    const std::string& ns,
                                    const BSONObj& cmdObj,
                                    BSONObjBuilder* bob) {
        CanonicalQuery* cqRaw;
        Status status = canonicalize(txn, ns, cmdObj, &cqRaw);
        if (!status.isOK()) {
            return status;
        }

        scoped_ptr<CanonicalQuery> cq(cqRaw);

        if (!planCache.contains(*cq)) {
            // Return empty plans in results if query shape does not
            // exist in plan cache.
            BSONArrayBuilder plansBuilder(bob->subarrayStart("plans"));
            plansBuilder.doneFast();
            return Status::OK();
        }

        PlanCacheEntry* entryRaw;
        Status result = planCache.getEntry(*cq, &entryRaw);
        if (!result.isOK()) {
            return result;
        }
        scoped_ptr<PlanCacheEntry> entry(entryRaw);

        BSONArrayBuilder plansBuilder(bob->subarrayStart("plans"));
        size_t numPlans = entry->plannerData.size();
        invariant(numPlans == entry->decision->stats.size());
        invariant(numPlans == entry->decision->scores.size());
        for (size_t i = 0; i < numPlans; ++i) {
            BSONObjBuilder planBob(plansBuilder.subobjStart());

            // Create plan details field.
            // Currently, simple string representationg of
            // SolutionCacheData. Need to revisit format when we
            // need to parse user-provided plan details for planCacheAddPlan.
            SolutionCacheData* scd = entry->plannerData[i];
            BSONObjBuilder detailsBob(planBob.subobjStart("details"));
            detailsBob.append("solution", scd->toString());
            detailsBob.doneFast();

            // reason is comprised of score and initial stats provided by
            // multi plan runner.
            BSONObjBuilder reasonBob(planBob.subobjStart("reason"));
            reasonBob.append("score", entry->decision->scores[i]);
            BSONObjBuilder statsBob(reasonBob.subobjStart("stats"));
            PlanStageStats* stats = entry->decision->stats.vector()[i];
            if (stats) {
                Explain::statsToBSON(*stats, &statsBob);
            }
            statsBob.doneFast();
            reasonBob.doneFast();

            // BSON object for 'feedback' field is created from query executions
            // and shows number of executions since this cached solution was
            // created as well as score data (average and standard deviation).
            BSONObjBuilder feedbackBob(planBob.subobjStart("feedback"));
            if (i == 0U) {
                feedbackBob.append("nfeedback", int(entry->feedback.size()));
                feedbackBob.append("averageScore", entry->averageScore.get_value_or(0));
                feedbackBob.append("stdDevScore",entry->stddevScore.get_value_or(0));
                BSONArrayBuilder scoresBob(feedbackBob.subarrayStart("scores"));
                for (size_t i = 0; i < entry->feedback.size(); ++i) {
                    BSONObjBuilder scoreBob(scoresBob.subobjStart());
                    scoreBob.append("score", entry->feedback[i]->score);
                }
                scoresBob.doneFast();
            }
            feedbackBob.doneFast();

            planBob.append("filterSet", scd->indexFilterApplied);
        }
        plansBuilder.doneFast();

        return Status::OK();
    }
Exemple #3
0
    // static
    Status QueryPlanner::planFromCache(const CanonicalQuery& query,
                                       const QueryPlannerParams& params,
                                       const SolutionCacheData& cacheData,
                                       QuerySolution** out) {
        if (SolutionCacheData::WHOLE_IXSCAN_SOLN == cacheData.solnType) {
            // The solution can be constructed by a scan over the entire index.
            QuerySolution* soln = buildWholeIXSoln(*cacheData.tree->entry,
                                                   query,
                                                   params,
                                                   cacheData.wholeIXSolnDir);
            if (soln == NULL) {
                return Status(ErrorCodes::BadValue,
                              "plan cache error: soln that uses index to provide sort");
            }
            else {
                *out = soln;
                return Status::OK();
            }
        }
        else if (SolutionCacheData::COLLSCAN_SOLN == cacheData.solnType) {
            // The cached solution is a collection scan. We don't cache collscans
            // with tailable==true, hence the false below.
            QuerySolution* soln = buildCollscanSoln(query, false, params);
            if (soln == NULL) {
                return Status(ErrorCodes::BadValue, "plan cache error: collection scan soln");
            }
            else {
                *out = soln;
                return Status::OK();
            }
        }

        // SolutionCacheData::USE_TAGS_SOLN == cacheData->solnType
        // If we're here then this is neither the whole index scan or collection scan
        // cases, and we proceed by using the PlanCacheIndexTree to tag the query tree.

        // Create a copy of the expression tree.  We use cachedSoln to annotate this with indices.
        MatchExpression* clone = query.root()->shallowClone();

        QLOG() << "Tagging the match expression according to cache data: " << endl
               << "Filter:" << endl << clone->toString()
               << "Cache data:" << endl << cacheData.toString();

        // Map from index name to index number.
        // TODO: can we assume that the index numbering has the same lifetime
        // as the cache state?
        map<BSONObj, size_t> indexMap;
        for (size_t i = 0; i < params.indices.size(); ++i) {
            const IndexEntry& ie = params.indices[i];
            indexMap[ie.keyPattern] = i;
            QLOG() << "Index " << i << ": " << ie.keyPattern.toString() << endl;
        }

        Status s = tagAccordingToCache(clone, cacheData.tree.get(), indexMap);
        if (!s.isOK()) {
            return s;
        }

        // The planner requires a defined sort order.
        sortUsingTags(clone);

        QLOG() << "Tagged tree:" << endl << clone->toString();

        // Use the cached index assignments to build solnRoot.  Takes ownership of clone.
        QuerySolutionNode* solnRoot =
            QueryPlannerAccess::buildIndexedDataAccess(query, clone, false, params.indices);

        if (NULL != solnRoot) {
            // Takes ownership of 'solnRoot'.
            QuerySolution* soln = QueryPlannerAnalysis::analyzeDataAccess(query,
                                                                          params,
                                                                          solnRoot);
            if (NULL != soln) {
                QLOG() << "Planner: solution constructed from the cache:\n" << soln->toString() << endl;
                *out = soln;
                return Status::OK();
            }
        }

        return Status(ErrorCodes::BadValue, "couldn't plan from cache");
    }