Status getRunnerAlwaysPlan(Collection* collection, CanonicalQuery* rawCanonicalQuery, const QueryPlannerParams& plannerParams, Runner** out) { invariant(collection); invariant(rawCanonicalQuery); auto_ptr<CanonicalQuery> canonicalQuery(rawCanonicalQuery); vector<QuerySolution*> solutions; Status status = QueryPlanner::plan(*canonicalQuery, plannerParams, &solutions); if (!status.isOK()) { return Status(ErrorCodes::BadValue, "error processing query: " + canonicalQuery->toString() + " planner returned error: " + status.reason()); } // We cannot figure out how to answer the query. Perhaps it requires an index // we do not have? if (0 == solutions.size()) { return Status(ErrorCodes::BadValue, str::stream() << "error processing query: " << canonicalQuery->toString() << " No query solutions"); } // See if one of our solutions is a fast count hack in disguise. if (plannerParams.options & QueryPlannerParams::PRIVATE_IS_COUNT) { for (size_t i = 0; i < solutions.size(); ++i) { if (turnIxscanIntoCount(solutions[i])) { // Great, we can use solutions[i]. Clean up the other QuerySolution(s). for (size_t j = 0; j < solutions.size(); ++j) { if (j != i) { delete solutions[j]; } } LOG(2) << "Using fast count: " << canonicalQuery->toStringShort() << ", planSummary: " << getPlanSummary(*solutions[i]); // We're not going to cache anything that's fast count. WorkingSet* ws; PlanStage* root; verify(StageBuilder::build(collection, *solutions[i], &root, &ws)); *out = new SingleSolutionRunner(collection, canonicalQuery.release(), solutions[i], root, ws); return Status::OK(); } } } if (1 == solutions.size()) { LOG(2) << "Only one plan is available; it will be run but will not be cached. " << canonicalQuery->toStringShort() << ", planSummary: " << getPlanSummary(*solutions[0]); // Only one possible plan. Run it. Build the stages from the solution. WorkingSet* ws; PlanStage* root; verify(StageBuilder::build(collection, *solutions[0], &root, &ws)); // And, run the plan. *out = new SingleSolutionRunner(collection, canonicalQuery.release(), solutions[0], root, ws); return Status::OK(); } else { // Many solutions. Let the MultiPlanRunner pick the best, update the cache, and so on. auto_ptr<MultiPlanRunner> mpr(new MultiPlanRunner(collection,canonicalQuery.release())); for (size_t i = 0; i < solutions.size(); ++i) { WorkingSet* ws; PlanStage* root; if (solutions[i]->cacheData.get()) { solutions[i]->cacheData->indexFilterApplied = plannerParams.indexFiltersApplied; } verify(StageBuilder::build(collection, *solutions[i], &root, &ws)); // Takes ownership of all arguments. mpr->addPlan(solutions[i], root, ws); } *out = mpr.release(); return Status::OK(); } }
Status getRunnerDistinct(Collection* collection, const BSONObj& query, const string& field, Runner** out) { // This should'a been checked by the distinct command. verify(collection); // TODO: check for idhack here? // When can we do a fast distinct hack? // 1. There is a plan with just one leaf and that leaf is an ixscan. // 2. The ixscan indexes the field we're interested in. // 2a: We are correct if the index contains the field but for now we look for prefix. // 3. The query is covered/no fetch. // // We go through normal planning (with limited parameters) to see if we can produce // a soln with the above properties. QueryPlannerParams plannerParams; plannerParams.options = QueryPlannerParams::NO_TABLE_SCAN; IndexCatalog::IndexIterator ii = collection->getIndexCatalog()->getIndexIterator(false); while (ii.more()) { const IndexDescriptor* desc = ii.next(); // The distinct hack can work if any field is in the index but it's not always clear // if it's a win unless it's the first field. if (desc->keyPattern().firstElement().fieldName() == field) { plannerParams.indices.push_back(IndexEntry(desc->keyPattern(), desc->getAccessMethodName(), desc->isMultikey(), desc->isSparse(), desc->indexName(), desc->infoObj())); } } // If there are no suitable indices for the distinct hack bail out now into regular planning // with no projection. if (plannerParams.indices.empty()) { CanonicalQuery* cq; Status status = CanonicalQuery::canonicalize(collection->ns().ns(), query, BSONObj(), BSONObj(), &cq); if (!status.isOK()) { return status; } // Takes ownership of cq. return getRunner(collection, cq, out); } // // If we're here, we have an index prefixed by the field we're distinct-ing over. // // Applying a projection allows the planner to try to give us covered plans that we can turn // into the projection hack. getDistinctProjection deals with .find() projection semantics // (ie _id:1 being implied by default). BSONObj projection = getDistinctProjection(field); // Apply a projection of the key. Empty BSONObj() is for the sort. CanonicalQuery* cq; Status status = CanonicalQuery::canonicalize(collection->ns().ns(), query, BSONObj(), projection, &cq); if (!status.isOK()) { return status; } // If there's no query, we can just distinct-scan one of the indices. // Not every index in plannerParams.indices may be suitable. Refer to // getDistinctNodeIndex(). size_t distinctNodeIndex = 0; if (query.isEmpty() && getDistinctNodeIndex(plannerParams.indices, field, &distinctNodeIndex)) { DistinctNode* dn = new DistinctNode(); dn->indexKeyPattern = plannerParams.indices[distinctNodeIndex].keyPattern; dn->direction = 1; IndexBoundsBuilder::allValuesBounds(dn->indexKeyPattern, &dn->bounds); dn->fieldNo = 0; QueryPlannerParams params; // Takes ownership of 'dn'. QuerySolution* soln = QueryPlannerAnalysis::analyzeDataAccess(*cq, params, dn); verify(soln); LOG(2) << "Using fast distinct: " << cq->toStringShort() << ", planSummary: " << getPlanSummary(*soln); WorkingSet* ws; PlanStage* root; verify(StageBuilder::build(collection, *soln, &root, &ws)); *out = new SingleSolutionRunner(collection, cq, soln, root, ws); return Status::OK(); } // See if we can answer the query in a fast-distinct compatible fashion. vector<QuerySolution*> solutions; status = QueryPlanner::plan(*cq, plannerParams, &solutions); if (!status.isOK()) { return getRunner(collection, cq, out); } // We look for a solution that has an ixscan we can turn into a distinctixscan for (size_t i = 0; i < solutions.size(); ++i) { if (turnIxscanIntoDistinctIxscan(solutions[i], field)) { // Great, we can use solutions[i]. Clean up the other QuerySolution(s). for (size_t j = 0; j < solutions.size(); ++j) { if (j != i) { delete solutions[j]; } } LOG(2) << "Using fast distinct: " << cq->toStringShort() << ", planSummary: " << getPlanSummary(*solutions[i]); // Build and return the SSR over solutions[i]. WorkingSet* ws; PlanStage* root; verify(StageBuilder::build(collection, *solutions[i], &root, &ws)); *out = new SingleSolutionRunner(collection, cq, solutions[i], root, ws); return Status::OK(); } } // If we're here, the planner made a soln with the restricted index set but we couldn't // translate any of them into a distinct-compatible soln. So, delete the solutions and just // go through normal planning. for (size_t i = 0; i < solutions.size(); ++i) { delete solutions[i]; } // We drop the projection from the 'cq'. Unfortunately this is not trivial. delete cq; status = CanonicalQuery::canonicalize(collection->ns().ns(), query, BSONObj(), BSONObj(), &cq); if (!status.isOK()) { return status; } // Takes ownership of cq. return getRunner(collection, cq, out); }
Status getRunnerFromCache(CanonicalQuery* canonicalQuery, Collection* collection, const QueryPlannerParams& plannerParams, Runner** out) { // Skip cache look up for non-cacheable queries. if (!PlanCache::shouldCacheQuery(*canonicalQuery)) { return Status(ErrorCodes::BadValue, "query is not cacheable"); } CachedSolution* rawCS; Status cacheLookupStatus = collection->infoCache()->getPlanCache()->get(*canonicalQuery, &rawCS); if (!cacheLookupStatus.isOK()) { return cacheLookupStatus; } // We have a CachedSolution. Have the planner turn it into a QuerySolution. boost::scoped_ptr<CachedSolution> cs(rawCS); QuerySolution *qs, *backupQs; Status status = QueryPlanner::planFromCache(*canonicalQuery, plannerParams, *cs, &qs, &backupQs); if (!status.isOK()) { return status; } // If our cached solution is a hit for a count query, try to turn it into a fast count // thing. if (plannerParams.options & QueryPlannerParams::PRIVATE_IS_COUNT) { if (turnIxscanIntoCount(qs)) { LOG(2) << "Using fast count: " << canonicalQuery->toStringShort() << ", planSummary: " << getPlanSummary(*qs); WorkingSet* ws; PlanStage* root; verify(StageBuilder::build(collection, *qs, &root, &ws)); *out = new SingleSolutionRunner(collection, canonicalQuery, qs, root, ws); if (NULL != backupQs) { delete backupQs; } return Status::OK(); } } // If we're here, we're going to used the cached plan and things are normal. LOG(2) << "Using cached query plan: " << canonicalQuery->toStringShort() << ", planSummary: " << getPlanSummary(*qs); WorkingSet* ws; PlanStage* root; verify(StageBuilder::build(collection, *qs, &root, &ws)); CachedPlanRunner* cpr = new CachedPlanRunner(collection, canonicalQuery, qs, root, ws); // If there's a backup solution, let the CachedPlanRunner know about it. if (NULL != backupQs) { WorkingSet* backupWs; PlanStage* backupRoot; verify(StageBuilder::build(collection, *backupQs, &backupRoot, &backupWs)); cpr->setBackupPlan(backupQs, backupRoot, backupWs); } *out = cpr; return Status::OK(); }
Status getExecutorAlwaysPlan(OperationContext* txn, Collection* collection, CanonicalQuery* rawCanonicalQuery, const QueryPlannerParams& plannerParams, PlanExecutor** execOut) { invariant(collection); invariant(rawCanonicalQuery); auto_ptr<CanonicalQuery> canonicalQuery(rawCanonicalQuery); *execOut = NULL; vector<QuerySolution*> solutions; Status status = QueryPlanner::plan(*canonicalQuery.get(), plannerParams, &solutions); if (!status.isOK()) { return Status(ErrorCodes::BadValue, "error processing query: " + canonicalQuery->toString() + " planner returned error: " + status.reason()); } // We cannot figure out how to answer the query. Perhaps it requires an index // we do not have? if (0 == solutions.size()) { return Status(ErrorCodes::BadValue, str::stream() << "error processing query: " << canonicalQuery->toString() << " No query solutions"); } // See if one of our solutions is a fast count hack in disguise. if (plannerParams.options & QueryPlannerParams::PRIVATE_IS_COUNT) { for (size_t i = 0; i < solutions.size(); ++i) { if (turnIxscanIntoCount(solutions[i])) { // Great, we can use solutions[i]. Clean up the other QuerySolution(s). for (size_t j = 0; j < solutions.size(); ++j) { if (j != i) { delete solutions[j]; } } LOG(2) << "Using fast count: " << canonicalQuery->toStringShort() << ", planSummary: " << getPlanSummary(*solutions[i]); // We're not going to cache anything that's fast count. WorkingSet* ws = new WorkingSet(); PlanStage* root; verify(StageBuilder::build(txn, collection, *solutions[i], ws, &root)); *execOut = new PlanExecutor(ws, root, solutions[i], canonicalQuery.release(), collection); return Status::OK(); } } } if (1 == solutions.size()) { LOG(2) << "Only one plan is available; it will be run but will not be cached. " << canonicalQuery->toStringShort() << ", planSummary: " << getPlanSummary(*solutions[0]); // Only one possible plan. Run it. Build the stages from the solution. WorkingSet* ws = new WorkingSet(); PlanStage* root; verify(StageBuilder::build(txn, collection, *solutions[0], ws, &root)); *execOut = new PlanExecutor(ws, root, solutions[0], canonicalQuery.release(), collection); return Status::OK(); } else { // Many solutions. Create a MultiPlanStage to pick the best, update the cache, and so on. // The working set will be shared by all candidate plans and owned by the containing runner WorkingSet* sharedWorkingSet = new WorkingSet(); MultiPlanStage* multiPlanStage = new MultiPlanStage(collection, canonicalQuery.get()); for (size_t ix = 0; ix < solutions.size(); ++ix) { if (solutions[ix]->cacheData.get()) { solutions[ix]->cacheData->indexFilterApplied = plannerParams.indexFiltersApplied; } // version of StageBuild::build when WorkingSet is shared PlanStage* nextPlanRoot; verify(StageBuilder::build(txn, collection, *solutions[ix], sharedWorkingSet, &nextPlanRoot)); // Owns none of the arguments multiPlanStage->addPlan(solutions[ix], nextPlanRoot, sharedWorkingSet); } // Do the plan selection up front. multiPlanStage->pickBestPlan(); PlanExecutor* exec = new PlanExecutor(sharedWorkingSet, multiPlanStage, canonicalQuery.release(), collection); *execOut = exec; return Status::OK(); } }
Status getExecutor(OperationContext* txn, Collection* collection, CanonicalQuery* rawCanonicalQuery, PlanExecutor** out, size_t plannerOptions) { invariant(rawCanonicalQuery); auto_ptr<CanonicalQuery> canonicalQuery(rawCanonicalQuery); // This can happen as we're called by internal clients as well. if (NULL == collection) { const string& ns = canonicalQuery->ns(); LOG(2) << "Collection " << ns << " does not exist." << " Using EOF runner: " << canonicalQuery->toStringShort(); EOFStage* eofStage = new EOFStage(); WorkingSet* ws = new WorkingSet(); *out = new PlanExecutor(ws, eofStage, canonicalQuery.release(), collection); return Status::OK(); } // Fill out the planning params. We use these for both cached solutions and non-cached. QueryPlannerParams plannerParams; plannerParams.options = plannerOptions; fillOutPlannerParams(collection, canonicalQuery.get(), &plannerParams); // If we have an _id index we can use the idhack runner. if (IDHackStage::supportsQuery(*canonicalQuery.get()) && collection->getIndexCatalog()->findIdIndex()) { return getExecutorIDHack(txn, collection, canonicalQuery.release(), plannerParams, out); } // Tailable: If the query requests tailable the collection must be capped. if (canonicalQuery->getParsed().hasOption(QueryOption_CursorTailable)) { if (!collection->isCapped()) { return Status(ErrorCodes::BadValue, "error processing query: " + canonicalQuery->toString() + " tailable cursor requested on non capped collection"); } // If a sort is specified it must be equal to expectedSort. const BSONObj expectedSort = BSON("$natural" << 1); const BSONObj& actualSort = canonicalQuery->getParsed().getSort(); if (!actualSort.isEmpty() && !(actualSort == expectedSort)) { return Status(ErrorCodes::BadValue, "error processing query: " + canonicalQuery->toString() + " invalid sort specified for tailable cursor: " + actualSort.toString()); } } // Try to look up a cached solution for the query. CachedSolution* rawCS; if (PlanCache::shouldCacheQuery(*canonicalQuery) && collection->infoCache()->getPlanCache()->get(*canonicalQuery.get(), &rawCS).isOK()) { // We have a CachedSolution. Have the planner turn it into a QuerySolution. boost::scoped_ptr<CachedSolution> cs(rawCS); QuerySolution *qs, *backupQs; QuerySolution*& chosenSolution=qs; // either qs or backupQs Status status = QueryPlanner::planFromCache(*canonicalQuery.get(), plannerParams, *cs, &qs, &backupQs); if (status.isOK()) { // the working set will be shared by the root and backupRoot plans // and owned by the containing single-solution-runner // WorkingSet* sharedWs = new WorkingSet(); PlanStage *root, *backupRoot=NULL; verify(StageBuilder::build(txn, collection, *qs, sharedWs, &root)); if ((plannerParams.options & QueryPlannerParams::PRIVATE_IS_COUNT) && turnIxscanIntoCount(qs)) { LOG(2) << "Using fast count: " << canonicalQuery->toStringShort() << ", planSummary: " << getPlanSummary(*qs); if (NULL != backupQs) { delete backupQs; } } else if (NULL != backupQs) { verify(StageBuilder::build(txn, collection, *backupQs, sharedWs, &backupRoot)); } // add a CachedPlanStage on top of the previous root root = new CachedPlanStage(collection, canonicalQuery.get(), root, backupRoot); *out = new PlanExecutor(sharedWs, root, chosenSolution, canonicalQuery.release(), collection); return Status::OK(); } } if (internalQueryPlanOrChildrenIndependently && SubplanStage::canUseSubplanning(*canonicalQuery)) { QLOG() << "Running query as sub-queries: " << canonicalQuery->toStringShort(); auto_ptr<WorkingSet> ws(new WorkingSet()); SubplanStage* subplan; Status subplanStatus = SubplanStage::make(txn, collection, ws.get(), plannerParams, canonicalQuery.get(), &subplan); if (subplanStatus.isOK()) { LOG(2) << "Running query as sub-queries: " << canonicalQuery->toStringShort(); *out = new PlanExecutor(ws.release(), subplan, canonicalQuery.release(), collection); return Status::OK(); } else { QLOG() << "Subplanner: " << subplanStatus.reason(); } } return getExecutorAlwaysPlan(txn, collection, canonicalQuery.release(), plannerParams, out); }
void MultiPlanStage::pickBestPlan() { // Run each plan some number of times. This number is at least as great as // 'internalQueryPlanEvaluationWorks', but may be larger for big collections. size_t numWorks = internalQueryPlanEvaluationWorks; if (NULL != _collection) { // For large collections, the number of works is set to be this // fraction of the collection size. double fraction = internalQueryPlanEvaluationCollFraction; numWorks = std::max(size_t(internalQueryPlanEvaluationWorks), size_t(fraction * _collection->numRecords())); } // We treat ntoreturn as though it is a limit during plan ranking. // This means that ranking might not be great for sort + batchSize. // But it also means that we don't buffer too much data for sort + limit. // See SERVER-14174 for details. size_t numToReturn = _query->getParsed().getNumToReturn(); // Determine the number of results which we will produce during the plan // ranking phase before stopping. size_t numResults = (size_t)internalQueryPlanEvaluationMaxResults; if (numToReturn > 0) { numResults = std::min(numToReturn, numResults); } // Work the plans, stopping when a plan hits EOF or returns some // fixed number of results. for (size_t ix = 0; ix < numWorks; ++ix) { bool moreToDo = workAllPlans(numResults); if (!moreToDo) { break; } } if (_failure) { return; } // After picking best plan, ranking will own plan stats from // candidate solutions (winner and losers). std::auto_ptr<PlanRankingDecision> ranking(new PlanRankingDecision); _bestPlanIdx = PlanRanker::pickBestPlan(_candidates, ranking.get()); verify(_bestPlanIdx >= 0 && _bestPlanIdx < static_cast<int>(_candidates.size())); // Copy candidate order. We will need this to sort candidate stats for explain // after transferring ownership of 'ranking' to plan cache. std::vector<size_t> candidateOrder = ranking->candidateOrder; CandidatePlan& bestCandidate = _candidates[_bestPlanIdx]; std::list<WorkingSetID>& alreadyProduced = bestCandidate.results; QuerySolution* bestSolution = bestCandidate.solution; QLOG() << "Winning solution:\n" << bestSolution->toString() << endl; LOG(2) << "Winning plan: " << getPlanSummary(*bestSolution); _backupPlanIdx = kNoSuchPlan; if (bestSolution->hasBlockingStage && (0 == alreadyProduced.size())) { QLOG() << "Winner has blocking stage, looking for backup plan...\n"; for (size_t ix = 0; ix < _candidates.size(); ++ix) { if (!_candidates[ix].solution->hasBlockingStage) { QLOG() << "Candidate " << ix << " is backup child\n"; _backupPlanIdx = ix; break; } } } // Store the choice we just made in the cache. In order to do so, // 1) the query must be of a type that is safe to cache, and // 2) two or more plans cannot have tied for the win. Caching in the // case of ties can cause successive queries of the same shape to // use a bad index. if (PlanCache::shouldCacheQuery(*_query) && !ranking->tieForBest) { // Create list of candidate solutions for the cache with // the best solution at the front. std::vector<QuerySolution*> solutions; // Generate solutions and ranking decisions sorted by score. for (size_t orderingIndex = 0; orderingIndex < candidateOrder.size(); ++orderingIndex) { // index into candidates/ranking size_t ix = candidateOrder[orderingIndex]; solutions.push_back(_candidates[ix].solution); } // Check solution cache data. Do not add to cache if // we have any invalid SolutionCacheData data. // XXX: One known example is 2D queries bool validSolutions = true; for (size_t ix = 0; ix < solutions.size(); ++ix) { if (NULL == solutions[ix]->cacheData.get()) { QLOG() << "Not caching query because this solution has no cache data: " << solutions[ix]->toString(); validSolutions = false; break; } } if (validSolutions) { _collection->infoCache()->getPlanCache()->add(*_query, solutions, ranking.release()); } } }
bool MultiPlanRunner::pickBestPlan(size_t* out, BSONObj* objOut) { static const int timesEachPlanIsWorked = 100; // Run each plan some number of times. for (int i = 0; i < timesEachPlanIsWorked; ++i) { bool moreToDo = workAllPlans(objOut); if (!moreToDo) { break; } } if (_failure || _killed) { return false; } // After picking best plan, ranking will own plan stats from // candidate solutions (winner and losers). std::auto_ptr<PlanRankingDecision> ranking(new PlanRankingDecision); size_t bestChild = PlanRanker::pickBestPlan(_candidates, ranking.get()); // Copy candidate order. We will need this to sort candidate stats for explain // after transferring ownership of 'ranking' to plan cache. std::vector<size_t> candidateOrder = ranking->candidateOrder; // Run the best plan. Store it. _bestPlan.reset(new PlanExecutor(_candidates[bestChild].ws, _candidates[bestChild].root)); _bestPlan->setYieldPolicy(_policy); _alreadyProduced = _candidates[bestChild].results; _bestSolution.reset(_candidates[bestChild].solution); QLOG() << "Winning solution:\n" << _bestSolution->toString() << endl; LOG(2) << "Winning plan: " << getPlanSummary(*_bestSolution); size_t backupChild = bestChild; if (_bestSolution->hasBlockingStage && (0 == _alreadyProduced.size())) { QLOG() << "Winner has blocking stage, looking for backup plan.\n"; for (size_t i = 0; i < _candidates.size(); ++i) { if (!_candidates[i].solution->hasBlockingStage) { QLOG() << "Candidate " << i << " is backup child.\n"; backupChild = i; _backupSolution = _candidates[i].solution; _backupAlreadyProduced = _candidates[i].results; _backupPlan = new PlanExecutor(_candidates[i].ws, _candidates[i].root); _backupPlan->setYieldPolicy(_policy); break; } } } // Store the choice we just made in the cache. We do // not cache the query if: // 1) The query is of a type that is not safe to cache, or // 2) the winning plan did not actually produce any results, // without hitting EOF. In this case, we have no information to // suggest that this plan is good. const PlanStageStats* bestStats = ranking->stats.vector()[0]; if (PlanCache::shouldCacheQuery(*_query) && (!_alreadyProduced.empty() || bestStats->common.isEOF)) { Database* db = cc().database(); verify(NULL != db); Collection* collection = db->getCollection(_query->ns()); verify(NULL != collection); PlanCache* cache = collection->infoCache()->getPlanCache(); // Create list of candidate solutions for the cache with // the best solution at the front. std::vector<QuerySolution*> solutions; // Generate solutions and ranking decisions sorted by score. for (size_t orderingIndex = 0; orderingIndex < candidateOrder.size(); ++orderingIndex) { // index into candidates/ranking size_t i = candidateOrder[orderingIndex]; solutions.push_back(_candidates[i].solution); } // Check solution cache data. Do not add to cache if // we have any invalid SolutionCacheData data. // XXX: One known example is 2D queries bool validSolutions = true; for (size_t i = 0; i < solutions.size(); ++i) { if (NULL == solutions[i]->cacheData.get()) { QLOG() << "Not caching query because this solution has no cache data: " << solutions[i]->toString(); validSolutions = false; break; } } if (validSolutions) { cache->add(*_query, solutions, ranking.release()); } } // Clear out the candidate plans, leaving only stats as we're all done w/them. // Traverse candidate plans in order or score for (size_t orderingIndex = 0; orderingIndex < candidateOrder.size(); ++orderingIndex) { // index into candidates/ranking size_t i = candidateOrder[orderingIndex]; if (i == bestChild) { continue; } if (i == backupChild) { continue; } delete _candidates[i].solution; // Remember the stats for the candidate plan because we always show it on an // explain. (The {verbose:false} in explain() is client-side trick; we always // generate a "verbose" explain.) PlanStageStats* stats = _candidates[i].root->getStats(); if (stats) { _candidateStats.push_back(stats); } delete _candidates[i].root; // ws must die after the root. delete _candidates[i].ws; } _candidates.clear(); if (NULL != out) { *out = bestChild; } return true; }