summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mongo/client/parallel.cpp59
-rw-r--r--src/mongo/db/commands/count.cpp1
-rw-r--r--src/mongo/db/exec/index_scan.cpp10
-rw-r--r--src/mongo/db/exec/multi_plan.cpp14
-rw-r--r--src/mongo/db/exec/subplan.cpp8
-rw-r--r--src/mongo/db/pipeline/document_source_cursor.cpp1
-rw-r--r--src/mongo/db/query/SConscript2
-rw-r--r--src/mongo/db/query/explain.cpp90
-rw-r--r--src/mongo/db/query/explain.h15
-rw-r--r--src/mongo/db/query/explain_plan.cpp322
-rw-r--r--src/mongo/db/query/explain_plan.h60
-rw-r--r--src/mongo/db/query/index_bounds_builder_test.cpp50
-rw-r--r--src/mongo/db/query/new_find.cpp221
-rw-r--r--src/mongo/db/query/planner_analysis.cpp8
-rw-r--r--src/mongo/db/query/query_planner_test.cpp90
-rw-r--r--src/mongo/db/query/type_explain.cpp807
-rw-r--r--src/mongo/db/query/type_explain.h292
-rw-r--r--src/mongo/dbtests/querytests.cpp25
-rw-r--r--src/mongo/s/strategy.cpp2
-rw-r--r--src/mongo/shell/shardingtest.js16
20 files changed, 279 insertions, 1814 deletions
diff --git a/src/mongo/client/parallel.cpp b/src/mongo/client/parallel.cpp
index 3b9acd836e1..9d385823081 100644
--- a/src/mongo/client/parallel.cpp
+++ b/src/mongo/client/parallel.cpp
@@ -121,11 +121,13 @@ namespace mongo {
string cursorType;
BSONObj indexBounds;
BSONObj oldPlan;
-
+
long long millis = 0;
double numExplains = 0;
- map<string,long long> counters;
+ long long nReturned = 0;
+ long long keysExamined = 0;
+ long long docsExamined = 0;
map<string,list<BSONObj> > out;
{
@@ -151,31 +153,34 @@ namespace mongo {
y.append( temp );
- BSONObjIterator k( temp );
- while ( k.more() ) {
- BSONElement z = k.next();
- if ( z.fieldName()[0] != 'n' )
- continue;
- long long& c = counters[z.fieldName()];
- c += z.numberLong();
+ if (temp.hasField("executionStats")) {
+ // Here we assume that the shard gave us back explain 2.0 style output.
+ BSONObj execStats = temp["executionStats"].Obj();
+ if (execStats.hasField("nReturned")) {
+ nReturned += execStats["nReturned"].numberLong();
+ }
+ if (execStats.hasField("totalKeysExamined")) {
+ keysExamined += execStats["totalKeysExamined"].numberLong();
+ }
+ if (execStats.hasField("totalDocsExamined")) {
+ docsExamined += execStats["totalDocsExamined"].numberLong();
+ }
}
-
- millis += temp["millis"].numberLong();
- numExplains++;
-
- if ( temp["cursor"].type() == String ) {
- if ( cursorType.size() == 0 )
- cursorType = temp["cursor"].String();
- else if ( cursorType != temp["cursor"].String() )
- cursorType = "multiple";
+ else {
+ // Here we assume that the shard gave us back explain 1.0 style output.
+ if (temp.hasField("n")) {
+ nReturned += temp["n"].numberLong();
+ }
+ if (temp.hasField("nscanned")) {
+ keysExamined += temp["nscanned"].numberLong();
+ }
+ if (temp.hasField("nscannedObjects")) {
+ docsExamined += temp["nscannedObjects"].numberLong();
+ }
}
- if ( temp["indexBounds"].type() == Object )
- indexBounds = temp["indexBounds"].Obj();
-
- if ( temp["oldPlan"].type() == Object )
- oldPlan = temp["oldPlan"].Obj();
-
+ millis += temp["executionStats"]["executionTimeMillis"].numberLong();
+ numExplains++;
}
y.done();
}
@@ -183,8 +188,10 @@ namespace mongo {
}
b.append( "cursor" , cursorType );
- for ( map<string,long long>::iterator i=counters.begin(); i!=counters.end(); ++i )
- b.appendNumber( i->first , i->second );
+
+ b.appendNumber( "nReturned" , nReturned );
+ b.appendNumber( "totalKeysExamined" , keysExamined );
+ b.appendNumber( "totalDocsExamined" , docsExamined );
b.appendNumber( "millisShardTotal" , millis );
b.append( "millisShardAvg" ,
diff --git a/src/mongo/db/commands/count.cpp b/src/mongo/db/commands/count.cpp
index 89c0de4940b..df0ec5fe62c 100644
--- a/src/mongo/db/commands/count.cpp
+++ b/src/mongo/db/commands/count.cpp
@@ -35,7 +35,6 @@
#include "mongo/db/curop.h"
#include "mongo/db/query/get_executor.h"
#include "mongo/db/query/explain.h"
-#include "mongo/db/query/type_explain.h"
#include "mongo/util/log.h"
namespace mongo {
diff --git a/src/mongo/db/exec/index_scan.cpp b/src/mongo/db/exec/index_scan.cpp
index e5a8cfe2019..9bedd8db6c7 100644
--- a/src/mongo/db/exec/index_scan.cpp
+++ b/src/mongo/db/exec/index_scan.cpp
@@ -33,7 +33,6 @@
#include "mongo/db/index/index_access_method.h"
#include "mongo/db/index/index_cursor.h"
#include "mongo/db/index/index_descriptor.h"
-#include "mongo/db/query/explain.h"
#include "mongo/util/log.h"
namespace {
@@ -381,14 +380,7 @@ namespace mongo {
if (_specificStats.indexType.empty()) {
_specificStats.indexType = "BtreeCursor"; // TODO amName;
- // TODO this can be simplified once the new explain format is
- // the default. Probably won't need to include explain.h here either.
- if (enableNewExplain) {
- _specificStats.indexBounds = _params.bounds.toBSON();
- }
- else {
- _specificStats.indexBounds = _params.bounds.toLegacyBSON();
- }
+ _specificStats.indexBounds = _params.bounds.toBSON();
_specificStats.indexBoundsVerbose = _params.bounds.toString();
_specificStats.direction = _params.direction;
diff --git a/src/mongo/db/exec/multi_plan.cpp b/src/mongo/db/exec/multi_plan.cpp
index 2dd765d9553..19c16bbc7d3 100644
--- a/src/mongo/db/exec/multi_plan.cpp
+++ b/src/mongo/db/exec/multi_plan.cpp
@@ -110,6 +110,7 @@ namespace mongo {
if (!bestPlan.results.empty()) {
*out = bestPlan.results.front();
bestPlan.results.pop_front();
+ _commonStats.advanced++;
return PlanStage::ADVANCED;
}
@@ -140,10 +141,23 @@ namespace mongo {
_backupPlanIdx = kNoSuchPlan;
}
+ // Increment stats.
+ if (PlanStage::ADVANCED == state) {
+ _commonStats.advanced++;
+ }
+ else if (PlanStage::NEED_TIME == state) {
+ _commonStats.needTime++;
+ }
+
return state;
}
void MultiPlanStage::pickBestPlan() {
+ // Adds the amount of time taken by pickBestPlan() to executionTimeMillis. There's lots of
+ // execution work that happens here, so this is needed for the time accounting to
+ // make sense.
+ ScopedTimer timer(&_commonStats.executionTimeMillis);
+
// 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;
diff --git a/src/mongo/db/exec/subplan.cpp b/src/mongo/db/exec/subplan.cpp
index 5d4bcbd2dc0..281c0baafae 100644
--- a/src/mongo/db/exec/subplan.cpp
+++ b/src/mongo/db/exec/subplan.cpp
@@ -148,6 +148,10 @@ namespace mongo {
}
Status SubplanStage::planSubqueries() {
+ // Adds the amount of time taken by planSubqueries() to executionTimeMillis. There's lots of
+ // work that happens here, so this is needed for the time accounting to make sense.
+ ScopedTimer timer(&_commonStats.executionTimeMillis);
+
MatchExpression* theOr = _query->root();
for (size_t i = 0; i < _plannerParams.indices.size(); ++i) {
@@ -209,6 +213,10 @@ namespace mongo {
}
Status SubplanStage::pickBestPlan() {
+ // Adds the amount of time taken by pickBestPlan() to executionTimeMillis. There's lots of
+ // work that happens here, so this is needed for the time accounting to make sense.
+ ScopedTimer timer(&_commonStats.executionTimeMillis);
+
// This is what we annotate with the index selections and then turn into a solution.
auto_ptr<OrMatchExpression> theOr(
static_cast<OrMatchExpression*>(_query->root()->shallowClone()));
diff --git a/src/mongo/db/pipeline/document_source_cursor.cpp b/src/mongo/db/pipeline/document_source_cursor.cpp
index bae0e53a680..5e96f5e7cf2 100644
--- a/src/mongo/db/pipeline/document_source_cursor.cpp
+++ b/src/mongo/db/pipeline/document_source_cursor.cpp
@@ -35,7 +35,6 @@
#include "mongo/db/pipeline/document.h"
#include "mongo/db/query/explain.h"
#include "mongo/db/query/find_constants.h"
-#include "mongo/db/query/type_explain.h"
#include "mongo/db/storage_options.h"
#include "mongo/s/d_state.h"
#include "mongo/s/stale_exception.h" // for SendStaleConfigException
diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript
index 96339abc388..bf0a38ee291 100644
--- a/src/mongo/db/query/SConscript
+++ b/src/mongo/db/query/SConscript
@@ -34,13 +34,11 @@ env.Library(
target='query',
source=[
"explain.cpp",
- "explain_plan.cpp",
"get_executor.cpp",
"new_find.cpp",
"plan_executor.cpp",
"plan_ranker.cpp",
"stage_builder.cpp",
- "type_explain.cpp",
],
LIBDEPS=[
"query_planner",
diff --git a/src/mongo/db/query/explain.cpp b/src/mongo/db/query/explain.cpp
index 12ae7b3884d..4a14debc1f6 100644
--- a/src/mongo/db/query/explain.cpp
+++ b/src/mongo/db/query/explain.cpp
@@ -31,7 +31,6 @@
#include "mongo/db/query/explain.h"
#include "mongo/db/exec/multi_plan.h"
-#include "mongo/db/query/explain_plan.h"
#include "mongo/db/query/get_executor.h"
#include "mongo/db/query/plan_executor.h"
#include "mongo/db/query/query_planner.h"
@@ -195,8 +194,6 @@ namespace mongo {
using mongoutils::str::stream;
- MONGO_EXPORT_SERVER_PARAMETER(enableNewExplain, bool, false);
-
// static
void Explain::statsToBSON(const PlanStageStats& stats,
Explain::Verbosity verbosity,
@@ -545,7 +542,7 @@ namespace mongo {
MultiPlanStage* mps = getMultiPlanStage(exec->getRootStage());
// The executionStats verbosity level requires that we run the winning plan
- // until if finishes.
+ // until it finishes.
if (verbosity >= Explain::EXEC_STATS) {
Status s = exec->executePlan();
if (!s.isOK()) {
@@ -672,89 +669,4 @@ namespace mongo {
}
}
- // TODO: This is temporary and should get deleted. There are a few small ways in which
- // this differs from 2.6 explain, but I'm not too worried because this entire format is
- // going away soon:
- // 1) 'indexBounds' field excluded from idhack explain.
- // 2) 'filterSet' field (for index filters) excluded.
- Status Explain::legacyExplain(PlanExecutor* exec, TypeExplain** explain) {
- invariant(exec);
- invariant(explain);
-
- scoped_ptr<PlanStageStats> stats(exec->getStats());
- if (NULL == stats.get()) {
- return Status(ErrorCodes::InternalError, "no stats available to explain plan");
- }
-
- // Special explain format for EOF.
- if (STAGE_EOF == stats->stageType) {
- *explain = new TypeExplain();
-
- // Fill in mandatory fields.
- (*explain)->setN(0);
- (*explain)->setNScannedObjects(0);
- (*explain)->setNScanned(0);
-
- // Fill in all the main fields that don't have a default in the explain data structure.
- (*explain)->setCursor("BasicCursor");
- (*explain)->setScanAndOrder(false);
- (*explain)->setIsMultiKey(false);
- (*explain)->setIndexOnly(false);
- (*explain)->setNYields(0);
- (*explain)->setNChunkSkips(0);
-
- TypeExplain* allPlans = new TypeExplain;
- allPlans->setCursor("BasicCursor");
- (*explain)->addToAllPlans(allPlans); // ownership xfer
-
- (*explain)->setNScannedObjectsAllPlans(0);
- (*explain)->setNScannedAllPlans(0);
-
- return Status::OK();
- }
-
- // Special explain format for idhack.
- vector<PlanStageStats*> statNodes;
- flattenStatsTree(stats.get(), &statNodes);
- PlanStageStats* idhack = NULL;
- for (size_t i = 0; i < statNodes.size(); i++) {
- if (STAGE_IDHACK == statNodes[i]->stageType) {
- idhack = statNodes[i];
- break;
- }
- }
-
- if (NULL != idhack) {
- // Explain format does not match 2.4 and is intended
- // to indicate clearly that the ID hack has been applied.
- *explain = new TypeExplain();
-
- IDHackStats* idhackStats = static_cast<IDHackStats*>(idhack->specific.get());
-
- (*explain)->setCursor("IDCursor");
- (*explain)->setIDHack(true);
- (*explain)->setN(stats->common.advanced);
- (*explain)->setNScanned(idhackStats->keysExamined);
- (*explain)->setNScannedObjects(idhackStats->docsExamined);
-
- return Status::OK();
- }
-
- Status status = explainPlan(*stats, explain, true /* full details */);
- if (!status.isOK()) {
- return status;
- }
-
- // Fill in explain fields that are accounted by on the runner level.
- TypeExplain* chosenPlan = NULL;
- explainPlan(*stats, &chosenPlan, false /* no full details */);
- if (chosenPlan) {
- (*explain)->addToAllPlans(chosenPlan);
- }
- (*explain)->setNScannedObjectsAllPlans((*explain)->getNScannedObjects());
- (*explain)->setNScannedAllPlans((*explain)->getNScanned());
-
- return Status::OK();
- }
-
} // namespace mongo
diff --git a/src/mongo/db/query/explain.h b/src/mongo/db/query/explain.h
index ec018f6af7a..f617809a34c 100644
--- a/src/mongo/db/query/explain.h
+++ b/src/mongo/db/query/explain.h
@@ -34,17 +34,12 @@
#include "mongo/db/query/plan_executor.h"
#include "mongo/db/query/query_planner_params.h"
#include "mongo/db/query/query_solution.h"
-#include "mongo/db/query/type_explain.h"
namespace mongo {
class Collection;
class OperationContext;
- // Temporarily hide the new explain implementation behind a setParameter.
- // TODO: take this out, and make the new implementation the default.
- extern bool enableNewExplain;
-
/**
* A container for the summary statistics that the profiler, slow query log, and
* other non-explain debug mechanisms may want to collect.
@@ -181,16 +176,6 @@ namespace mongo {
*/
static void explainCountEmptyQuery(BSONObjBuilder* out);
- /**
- * Generate the legacy explain format from a PlanExecutor.
- *
- * On success, the caller owns 'explain'.
- *
- * TODO: THIS IS TEMPORARY. Once the legacy explain code is deleted, we won't
- * need this anymore.
- */
- static Status legacyExplain(PlanExecutor* exec, TypeExplain** explain);
-
private:
/**
* Private helper that does the heavy-lifting for the public statsToBSON(...) functions
diff --git a/src/mongo/db/query/explain_plan.cpp b/src/mongo/db/query/explain_plan.cpp
deleted file mode 100644
index 4c3a9fb06f8..00000000000
--- a/src/mongo/db/query/explain_plan.cpp
+++ /dev/null
@@ -1,322 +0,0 @@
-/**
- * Copyright (C) 2013-2014 MongoDB Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-// THIS FILE IS DEPRECATED -- the old explain implementation is being replaced
-
-#include "mongo/db/query/explain_plan.h"
-
-#include "mongo/db/query/explain.h"
-#include "mongo/db/query/stage_types.h"
-#include "mongo/db/query/type_explain.h"
-#include "mongo/util/mongoutils/str.h"
-
-namespace mongo {
-
- using mongoutils::str::stream;
-
- namespace {
-
- bool isOrStage(StageType stageType) {
- return stageType == STAGE_OR || stageType == STAGE_SORT_MERGE;
- }
-
- bool isNearStage(StageType stageType) {
- return stageType == STAGE_GEO_NEAR_2D || stageType == STAGE_GEO_NEAR_2DSPHERE;
- }
-
- bool isIntersectPlan(const PlanStageStats& stats) {
- if (stats.stageType == STAGE_AND_HASH || stats.stageType == STAGE_AND_SORTED) {
- return true;
- }
- for (size_t i = 0; i < stats.children.size(); ++i) {
- if (isIntersectPlan(*stats.children[i])) {
- return true;
- }
- }
- return false;
- }
-
- void getLeafNodes(const PlanStageStats& stats, vector<const PlanStageStats*>* leafNodesOut) {
- if (0 == stats.children.size()) {
- leafNodesOut->push_back(&stats);
- }
- for (size_t i = 0; i < stats.children.size(); ++i) {
- getLeafNodes(*stats.children[i], leafNodesOut);
- }
- }
-
- const PlanStageStats* findNode(const PlanStageStats* root, StageType type) {
- if (root->stageType == type) {
- return root;
- }
- for (size_t i = 0; i < root->children.size(); ++i) {
- const PlanStageStats* ret = findNode(root->children[i], type);
- if (NULL != ret) {
- return ret;
- }
- }
- return NULL;
- }
-
- } // namespace
-
- Status explainIntersectPlan(const PlanStageStats& stats, TypeExplain** explainOut, bool fullDetails) {
- auto_ptr<TypeExplain> res(new TypeExplain);
- res->setCursor("Complex Plan");
- res->setN(stats.common.advanced);
-
- // Sum the various counters at the leaves.
- vector<const PlanStageStats*> leaves;
- getLeafNodes(stats, &leaves);
-
- long long nScanned = 0;
- long long nScannedObjects = 0;
- for (size_t i = 0; i < leaves.size(); ++i) {
- TypeExplain* leafExplain;
- explainPlan(*leaves[i], &leafExplain, false);
- nScanned += leafExplain->getNScanned();
- nScannedObjects += leafExplain->getNScannedObjects();
- delete leafExplain;
- }
-
- res->setNScanned(nScanned);
- // XXX: this isn't exactly "correct" -- for ixscans we have to find out if it's part of a
- // subtree rooted at a fetch, etc. etc. do we want to just add the # of advances of a
- // fetch node minus the number of alreadyHasObj for those nodes?
- res->setNScannedObjects(nScannedObjects);
-
- uint64_t chunkSkips = 0;
- const PlanStageStats* shardFilter = findNode(&stats, STAGE_SHARDING_FILTER);
- if (NULL != shardFilter) {
- const ShardingFilterStats* sfs
- = static_cast<const ShardingFilterStats*>(shardFilter->specific.get());
- chunkSkips = sfs->chunkSkips;
- }
-
- res->setNChunkSkips(chunkSkips);
-
- if (fullDetails) {
- res->setNYields(stats.common.yields);
- BSONObjBuilder bob;
- Explain::statsToBSON(stats, &bob);
- res->stats = bob.obj();
- }
-
- *explainOut = res.release();
- return Status::OK();
- }
-
- namespace {
-
- Status explainPlan(const PlanStageStats& stats, TypeExplain** explainOut,
- bool fullDetails, bool covered) {
- //
- // Temporary explain for index intersection
- //
-
- if (isIntersectPlan(stats)) {
- return explainIntersectPlan(stats, explainOut, fullDetails);
- }
-
- //
- // Legacy explain implementation
- //
-
- // Descend the plan looking for structural properties:
- // + Are there any OR clauses? If so, explain each branch.
- // + What type(s) are the leaf nodes and what are their properties?
- // + Did we need a sort?
-
- bool sortPresent = false;
- size_t chunkSkips = 0;
-
-
- // XXX: TEMPORARY HACK - GEONEAR explains like OR queries (both have children) until the
- // new explain framework makes this file go away.
- const PlanStageStats* orStage = NULL;
- const PlanStageStats* root = &stats;
- const PlanStageStats* leaf = root;
-
- while (leaf->children.size() > 0) {
- // We shouldn't be here if there are any ANDs
- if (leaf->children.size() > 1) {
- verify(isOrStage(leaf->stageType) || isNearStage(leaf->stageType));
- }
-
- if (isOrStage(leaf->stageType) || isNearStage(leaf->stageType)) {
- orStage = leaf;
- break;
- }
-
- if (leaf->stageType == STAGE_FETCH) {
- covered = false;
- }
-
- if (leaf->stageType == STAGE_SORT) {
- sortPresent = true;
- }
-
- if (STAGE_SHARDING_FILTER == leaf->stageType) {
- const ShardingFilterStats* sfs
- = static_cast<const ShardingFilterStats*>(leaf->specific.get());
- chunkSkips = sfs->chunkSkips;
- }
-
- leaf = leaf->children[0];
- }
-
- auto_ptr<TypeExplain> res(new TypeExplain);
-
- // 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 (orStage != NULL) {
- size_t nScanned = 0;
- size_t nScannedObjects = 0;
- const std::vector<PlanStageStats*>& children = orStage->children;
- for (std::vector<PlanStageStats*>::const_iterator it = children.begin();
- it != children.end();
- ++it) {
- TypeExplain* childExplain = NULL;
- explainPlan(**it, &childExplain, false /* no full details */, covered);
- if (childExplain) {
- // Override child's indexOnly value if we have a non-covered
- // query (implied by a FETCH stage).
- //
- // As we run explain on each child, explainPlan() sets indexOnly
- // based only on the information in each child. This does not
- // consider the possibility of a FETCH stage above the OR/MERGE_SORT
- // stage, in which case the child's indexOnly may be erroneously set
- // to true.
- if (!covered && childExplain->isIndexOnlySet()) {
- childExplain->setIndexOnly(false);
- }
-
- // 'res' takes ownership of 'childExplain'.
- res->addToClauses(childExplain);
- nScanned += childExplain->getNScanned();
- nScannedObjects += childExplain->getNScannedObjects();
- }
- }
- // We set the cursor name for backwards compatibility with 2.4.
- if (isOrStage(leaf->stageType)) {
- res->setCursor("QueryOptimizerCursor");
- }
- else {
- if (leaf->stageType == STAGE_GEO_NEAR_2D)
- res->setCursor("GeoSearchCursor");
- else
- res->setCursor("S2NearCursor");
-
- res->setIndexOnly(false);
- res->setIsMultiKey(false);
- }
- 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);
- res->setIndexOnly(false);
- res->setIsMultiKey(false);
- }
- else if (leaf->stageType == STAGE_TEXT) {
- TextStats* tStats = static_cast<TextStats*>(leaf->specific.get());
- res->setCursor("TextCursor");
- res->setNScanned(tStats->keysExamined);
- res->setNScannedObjects(tStats->fetches);
- }
- else if (leaf->stageType == STAGE_IXSCAN) {
- IndexScanStats* indexStats = static_cast<IndexScanStats*>(leaf->specific.get());
- verify(indexStats);
- string direction = indexStats->direction > 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 if (leaf->stageType == STAGE_DISTINCT) {
- DistinctScanStats* dss = static_cast<DistinctScanStats*>(leaf->specific.get());
- verify(dss);
- res->setCursor("DistinctCursor");
- res->setN(dss->keysExamined);
- res->setNScanned(dss->keysExamined);
- // Distinct hack stage is fully covered.
- res->setNScannedObjects(0);
- }
- else {
- return Status(ErrorCodes::InternalError, "cannot interpret execution plan");
- }
-
- // How many documents did the query return?
- res->setN(root->common.advanced);
- res->setScanAndOrder(sortPresent);
- res->setNChunkSkips(chunkSkips);
-
- // 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);
- BSONObjBuilder bob;
- Explain::statsToBSON(*root, &bob);
- res->stats = bob.obj();
- }
-
- *explainOut = res.release();
- return Status::OK();
- }
-
- } // namespace
-
- Status explainPlan(const PlanStageStats& stats, TypeExplain** explainOut, bool fullDetails) {
- // This function merely calls a recursive helper of the same name. The boolean "covered" is
- // used to determine the value of nscannedObjects for subtrees along the way. Recursive
- // calls will pass false for "covered" if a fetch stage has been seen at that point in the
- // traversal.
- const bool covered = true;
- return explainPlan(stats, explainOut, fullDetails, covered);
- }
-
-} // namespace mongo
diff --git a/src/mongo/db/query/explain_plan.h b/src/mongo/db/query/explain_plan.h
deleted file mode 100644
index 4313c2c4a51..00000000000
--- a/src/mongo/db/query/explain_plan.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/**
- * Copyright (C) 2013 10gen Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-// THIS FILE IS DEPRECATED -- the old explain implementation is being replaced
-
-#pragma once
-
-#include "mongo/base/status.h"
-#include "mongo/db/exec/plan_stats.h"
-#include "mongo/db/query/query_solution.h"
-
-namespace mongo {
-
- class TypeExplain;
- struct PlanInfo;
-
- /**
- * Returns OK, allocating and filling in '*explainOut' describing the access paths used in
- * the 'stats' tree of a given query solution. The caller has the ownership of
- * '*explainOut', on success. Otherwise return an error status describing the problem.
- *
- * If 'fullDetails' was requested, the explain will return all available information about
- * the plan, otherwise, just a summary. The fields in the summary are: 'cursor', 'n',
- * 'nscannedObjects', 'nscanned', and 'indexBounds'. The remaining fields are: 'isMultKey',
- * 'nscannedObjectsAllPlans', 'nscannedAllPlans', 'scanAndOrder', 'indexOnly', 'nYields',
- * 'nChunkSkips', 'millis', 'allPlans', and 'oldPlan'.
- *
- * All these fields are documented in type_explain.h
- *
- * TODO: This is temporarily in place to support the legacy explain format. Once legacy
- * explain is removed, this function should be deleted.
- */
- Status explainPlan(const PlanStageStats& stats, TypeExplain** explainOut, bool fullDetails);
-
-} // namespace mongo
diff --git a/src/mongo/db/query/index_bounds_builder_test.cpp b/src/mongo/db/query/index_bounds_builder_test.cpp
index 0a89cef23fb..84b3b96ea51 100644
--- a/src/mongo/db/query/index_bounds_builder_test.cpp
+++ b/src/mongo/db/query/index_bounds_builder_test.cpp
@@ -1119,4 +1119,54 @@ namespace {
ASSERT_EQUALS(tightness, IndexBoundsBuilder::EXACT);
}
+ // Test $type bounds for Code BSON type.
+ TEST(IndexBoundsBuilderTest, CodeTypeBounds) {
+ IndexEntry testIndex = IndexEntry(BSONObj());
+ BSONObj obj = fromjson("{a: {$type: 13}}");
+ auto_ptr<MatchExpression> expr(parseMatchExpression(obj));
+ BSONElement elt = obj.firstElement();
+
+ OrderedIntervalList oil;
+ IndexBoundsBuilder::BoundsTightness tightness;
+ IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness);
+
+ // Build the expected interval.
+ BSONObjBuilder bob;
+ bob.appendCode("", "");
+ bob.appendCodeWScope("", "", BSONObj());
+ BSONObj expectedInterval = bob.obj();
+
+ // Check the output of translate().
+ ASSERT_EQUALS(oil.name, "a");
+ ASSERT_EQUALS(oil.intervals.size(), 1U);
+ ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare(
+ Interval(expectedInterval, true, true)));
+ ASSERT(tightness == IndexBoundsBuilder::INEXACT_FETCH);
+ }
+
+ // Test $type bounds for Code With Scoped BSON type.
+ TEST(IndexBoundsBuilderTest, CodeWithScopeTypeBounds) {
+ IndexEntry testIndex = IndexEntry(BSONObj());
+ BSONObj obj = fromjson("{a: {$type: 15}}");
+ auto_ptr<MatchExpression> expr(parseMatchExpression(obj));
+ BSONElement elt = obj.firstElement();
+
+ OrderedIntervalList oil;
+ IndexBoundsBuilder::BoundsTightness tightness;
+ IndexBoundsBuilder::translate(expr.get(), elt, testIndex, &oil, &tightness);
+
+ // Build the expected interval.
+ BSONObjBuilder bob;
+ bob.appendCodeWScope("", "", BSONObj());
+ bob.appendMaxKey("");
+ BSONObj expectedInterval = bob.obj();
+
+ // Check the output of translate().
+ ASSERT_EQUALS(oil.name, "a");
+ ASSERT_EQUALS(oil.intervals.size(), 1U);
+ ASSERT_EQUALS(Interval::INTERVAL_EQUALS, oil.intervals[0].compare(
+ Interval(expectedInterval, true, true)));
+ ASSERT(tightness == IndexBoundsBuilder::INEXACT_FETCH);
+ }
+
} // namespace
diff --git a/src/mongo/db/query/new_find.cpp b/src/mongo/db/query/new_find.cpp
index 6b7b8af08e1..b29aca68c25 100644
--- a/src/mongo/db/query/new_find.cpp
+++ b/src/mongo/db/query/new_find.cpp
@@ -90,11 +90,6 @@ namespace {
return n >= pq.getNumToReturn();
}
- bool enoughForExplain(const mongo::LiteParsedQuery& pq, long long n) {
- if (pq.wantMore() || 0 == pq.getNumToReturn()) { return false; }
- return n >= pq.getNumToReturn();
- }
-
/**
* Returns true if 'me' is a GTE or GE predicate over the "ts" field.
* Such predicates can be used for the oplog start hack.
@@ -511,60 +506,6 @@ namespace mongo {
// We use this a lot below.
const LiteParsedQuery& pq = cq->getParsed();
- // set this outside loop. we will need to use this both within loop and when deciding
- // to fill in explain information
- const bool isExplain = pq.isExplain();
-
- // New-style explains get diverted through a separate path which calls back into the
- // query planner and query execution mechanisms.
- //
- // TODO temporary until find() becomes a real command.
- if (isExplain && enableNewExplain) {
- size_t options = QueryPlannerParams::DEFAULT;
- if (shardingState.needCollectionMetadata(pq.ns())) {
- options |= QueryPlannerParams::INCLUDE_SHARD_FILTER;
- }
-
- BufBuilder bb;
- bb.skip(sizeof(QueryResult::Value));
-
- PlanExecutor* rawExec;
- // Takes ownership of 'cq'.
- Status execStatus = getExecutor(txn, collection, cq, &rawExec, options);
- if (!execStatus.isOK()) {
- uasserted(17510, "Explain error: " + execStatus.reason());
- }
-
- scoped_ptr<PlanExecutor> exec(rawExec);
- BSONObjBuilder explainBob;
- Status explainStatus = Explain::explainStages(exec.get(), Explain::EXEC_ALL_PLANS,
- &explainBob);
- if (!explainStatus.isOK()) {
- uasserted(18521, "Explain error: " + explainStatus.reason());
- }
-
- // Add the resulting object to the return buffer.
- BSONObj explainObj = explainBob.obj();
- bb.appendBuf((void*)explainObj.objdata(), explainObj.objsize());
-
- curop.debug().iscommand = true;
- // TODO: Does this get overwritten/do we really need to set this twice?
- curop.debug().query = q.query;
-
- // Set query result fields.
- QueryResult::View qr = bb.buf();
- bb.decouple();
- qr.setResultFlagsToOk();
- qr.msgdata().setLen(bb.len());
- curop.debug().responseLength = bb.len();
- qr.msgdata().setOperation(opReply);
- qr.setCursorId(0);
- qr.setStartingFrom(0);
- qr.setNReturned(1);
- result.setData(qr.view2ptr(), true);
- return "";
- }
-
// We'll now try to get the query executor that will execute this query for us. There
// are a few cases in which we know upfront which executor we should get and, therefore,
// we shortcut the selection process here.
@@ -606,6 +547,41 @@ namespace mongo {
verify(NULL != rawExec);
auto_ptr<PlanExecutor> exec(rawExec);
+ // If it's actually an explain, do the explain and return rather than falling through
+ // to the normal query execution loop.
+ if (pq.isExplain()) {
+ BufBuilder bb;
+ bb.skip(sizeof(QueryResult::Value));
+
+ BSONObjBuilder explainBob;
+ Status explainStatus = Explain::explainStages(exec.get(), Explain::EXEC_ALL_PLANS,
+ &explainBob);
+ if (!explainStatus.isOK()) {
+ uasserted(18521, "Explain error: " + explainStatus.reason());
+ }
+
+ // Add the resulting object to the return buffer.
+ BSONObj explainObj = explainBob.obj();
+ bb.appendBuf((void*)explainObj.objdata(), explainObj.objsize());
+
+ curop.debug().iscommand = true;
+ // TODO: Does this get overwritten/do we really need to set this twice?
+ curop.debug().query = q.query;
+
+ // Set query result fields.
+ QueryResult::View qr = bb.buf();
+ bb.decouple();
+ qr.setResultFlagsToOk();
+ qr.msgdata().setLen(bb.len());
+ curop.debug().responseLength = bb.len();
+ qr.msgdata().setOperation(opReply);
+ qr.setCursorId(0);
+ qr.setStartingFrom(0);
+ qr.setNReturned(1);
+ result.setData(qr.view2ptr(), true);
+ return "";
+ }
+
// We freak out later if this changes before we're done with the query.
const ChunkVersion shardingVersionAtStart = shardingState.getVersion(cq->ns());
@@ -662,10 +638,8 @@ namespace mongo {
curop.debug().planSummary = stats.summaryStr.c_str();
while (PlanExecutor::ADVANCED == (state = exec->getNext(&obj, NULL))) {
- // Add result to output buffer. This is unnecessary if explain info is requested
- if (!isExplain) {
- bb.appendBuf((void*)obj.objdata(), obj.objsize());
- }
+ // Add result to output buffer.
+ bb.appendBuf((void*)obj.objdata(), obj.objsize());
// Count the result.
++numResults;
@@ -681,13 +655,8 @@ namespace mongo {
// TODO: only one type of 2d search doesn't support this. We need a way to pull it out
// of CanonicalQuery. :(
const bool supportsGetMore = true;
- if (isExplain) {
- if (enoughForExplain(pq, numResults)) {
- break;
- }
- }
- else if (!supportsGetMore && (enough(pq, numResults)
- || bb.len() >= MaxBytesToReturnToClientAtOnce)) {
+ if (!supportsGetMore && (enough(pq, numResults)
+ || bb.len() >= MaxBytesToReturnToClientAtOnce)) {
break;
}
else if (enoughForFirstBatch(pq, numResults, bb.len())) {
@@ -743,59 +712,33 @@ namespace mongo {
shardingState.getVersion(pq.ns()));
}
- // Used to fill in explain and to determine if the query is slow enough to be logged.
- int elapsedMillis = curop.elapsedMillis();
-
- // Get explain information if:
- // 1) it is needed by an explain query;
- // 2) profiling is enabled; or
- // 3) profiling is disabled but we still need explain details to log a "slow" query.
- // Producing explain information is expensive and should be done only if we are certain
- // the information will be used.
- boost::scoped_ptr<TypeExplain> explain(NULL);
- if (isExplain ||
- ctx.ctx().db()->getProfilingLevel() > 0 ||
- elapsedMillis > serverGlobalParams.slowMS) {
- // Ask the executor to produce explain information.
- TypeExplain* bareExplain;
- Status res = Explain::legacyExplain(exec.get(), &bareExplain);
- if (res.isOK()) {
- explain.reset(bareExplain);
- }
- else if (isExplain) {
- error() << "could not produce explain of query '" << pq.getFilter()
- << "', error: " << res.reason();
- // If numResults and the data in bb don't correspond, we'll crash later when rooting
- // through the reply msg.
- BSONObj emptyObj;
- bb.appendBuf((void*)emptyObj.objdata(), emptyObj.objsize());
- // The explain output is actually a result.
- numResults = 1;
- // TODO: we can fill out millis etc. here just fine even if the plan screwed up.
+ // Set debug information for consumption by the profiler.
+ if (ctx.ctx().db()->getProfilingLevel() > 0 ||
+ curop.elapsedMillis() > serverGlobalParams.slowMS) {
+ PlanSummaryStats newStats;
+ Explain::getSummaryStats(exec.get(), &newStats);
+
+ curop.debug().ntoskip = pq.getSkip();
+ curop.debug().nreturned = numResults;
+ curop.debug().scanAndOrder = newStats.hasSortStage;
+ curop.debug().nscanned = newStats.totalKeysExamined;
+ curop.debug().nscannedObjects = newStats.totalDocsExamined;
+ curop.debug().idhack = newStats.isIdhack;
+
+ // Get BSON stats.
+ scoped_ptr<PlanStageStats> execStats(exec->getStats());
+ BSONObjBuilder statsBob;
+ Explain::statsToBSON(*execStats, &statsBob);
+ curop.debug().execStats.set(statsBob.obj());
+
+ // Replace exec stats with plan summary if stats cannot fit into CachedBSONObj.
+ if (curop.debug().execStats.tooBig() && !curop.debug().planSummary.empty()) {
+ BSONObjBuilder bob;
+ bob.append("summary", curop.debug().planSummary.toString());
+ curop.debug().execStats.set(bob.done());
}
}
- // Fill in the missing run-time fields in explain, starting with propeties of
- // the process running the query.
- if (isExplain && NULL != explain.get()) {
- std::string server = mongoutils::str::stream()
- << getHostNameCached() << ":" << serverGlobalParams.port;
- explain->setServer(server);
-
- // We might have skipped some results due to chunk migration etc. so our count is
- // correct.
- explain->setN(numResults);
-
- // Clock the whole operation.
- explain->setMillis(elapsedMillis);
-
- BSONObj explainObj = explain->toBSON();
- bb.appendBuf((void*)explainObj.objdata(), explainObj.objsize());
-
- // The explain output is actually a result.
- numResults = 1;
- }
-
long long ccId = 0;
if (saveClientCursor) {
// We won't use the executor until it's getMore'd.
@@ -849,44 +792,6 @@ namespace mongo {
qr.setStartingFrom(0);
qr.setNReturned(numResults);
- // Set debug information for consumption by the profiler.
- curop.debug().ntoskip = pq.getSkip();
- curop.debug().nreturned = numResults;
- if (NULL != explain.get()) {
- if (explain->isScanAndOrderSet()) {
- curop.debug().scanAndOrder = explain->getScanAndOrder();
- }
- else {
- curop.debug().scanAndOrder = false;
- }
-
- if (explain->isNScannedSet()) {
- curop.debug().nscanned = explain->getNScanned();
- }
-
- if (explain->isNScannedObjectsSet()) {
- curop.debug().nscannedObjects = explain->getNScannedObjects();
- }
-
- if (explain->isIDHackSet()) {
- curop.debug().idhack = explain->getIDHack();
- }
-
- if (!explain->stats.isEmpty()) {
- // execStats is a CachedBSONObj because it lives in the race-prone
- // curop.
- curop.debug().execStats.set(explain->stats);
-
- // Replace exec stats with plan summary if stats cannot fit into CachedBSONObj.
- if (curop.debug().execStats.tooBig() && !curop.debug().planSummary.empty()) {
- BSONObjBuilder bob;
- bob.append("summary", curop.debug().planSummary.toString());
- curop.debug().execStats.set(bob.done());
- }
-
- }
- }
-
// curop.debug().exhaust is set above.
return curop.debug().exhaust ? pq.ns() : "";
}
diff --git a/src/mongo/db/query/planner_analysis.cpp b/src/mongo/db/query/planner_analysis.cpp
index 233e3b3e4b8..9c723ab46c2 100644
--- a/src/mongo/db/query/planner_analysis.cpp
+++ b/src/mongo/db/query/planner_analysis.cpp
@@ -448,6 +448,11 @@ namespace mongo {
// We have no idea what the client intended. One way to handle the ambiguity
// of a limited OR stage is to use the SPLIT_LIMITED_SORT hack.
//
+ // If wantMore is false (meaning that 'ntoreturn' was initially passed to
+ // the server as a negative value), then we treat numToReturn as a limit.
+ // Since there is no limit-batchSize ambiguity in this case, we do not use the
+ // SPLIT_LIMITED_SORT hack.
+ //
// If numToReturn is really a limit, then we want to add a limit to this
// SORT stage, and hence perform a topK.
//
@@ -458,7 +463,8 @@ namespace mongo {
// with the topK first. If the client wants a limit, they'll get the efficiency
// of topK. If they want a batchSize, the other OR branch will deliver the missing
// results. The OR stage handles deduping.
- if (params.options & QueryPlannerParams::SPLIT_LIMITED_SORT
+ if (query.getParsed().wantMore()
+ && params.options & QueryPlannerParams::SPLIT_LIMITED_SORT
&& !QueryPlannerCommon::hasNode(query.root(), MatchExpression::TEXT)
&& !QueryPlannerCommon::hasNode(query.root(), MatchExpression::GEO)
&& !QueryPlannerCommon::hasNode(query.root(), MatchExpression::GEO_NEAR)) {
diff --git a/src/mongo/db/query/query_planner_test.cpp b/src/mongo/db/query/query_planner_test.cpp
index 4e4c92f930a..19faeb6d1c1 100644
--- a/src/mongo/db/query/query_planner_test.cpp
+++ b/src/mongo/db/query/query_planner_test.cpp
@@ -535,6 +535,68 @@ namespace {
assertSolutionExists("{fetch: {node: {ixscan: {pattern: {x: 1}}}}}");
}
+ TEST_F(QueryPlannerTest, ExistsBounds) {
+ addIndex(BSON("b" << 1));
+
+ runQuery(fromjson("{b: {$exists: true}}"));
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists("{fetch: {filter: {b: {$exists: true}}, node: "
+ "{ixscan: {pattern: {b: 1}, bounds: "
+ "{b: [['MinKey', 'MaxKey', true, true]]}}}}}");
+
+ // This ends up being a double negation, which we currently don't index.
+ runQuery(fromjson("{b: {$not: {$exists: false}}}"));
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+
+ runQuery(fromjson("{b: {$exists: false}}"));
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists("{fetch: {filter: {b: {$exists: false}}, node: "
+ "{ixscan: {pattern: {b: 1}, bounds: "
+ "{b: [[null, null, true, true]]}}}}}");
+
+ runQuery(fromjson("{b: {$not: {$exists: true}}}"));
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists("{fetch: {filter: {b: {$exists: false}}, node: "
+ "{ixscan: {pattern: {b: 1}, bounds: "
+ "{b: [[null, null, true, true]]}}}}}");
+ }
+
+ TEST_F(QueryPlannerTest, ExistsBoundsCompound) {
+ addIndex(BSON("a" << 1 << "b" << 1));
+
+ runQuery(fromjson("{a: 1, b: {$exists: true}}"));
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists("{fetch: {filter: {b: {$exists: true}}, node: "
+ "{ixscan: {pattern: {a: 1, b: 1}, bounds: "
+ "{a: [[1,1,true,true]], b: [['MinKey','MaxKey',true,true]]}}}}}");
+
+ // This ends up being a double negation, which we currently don't index.
+ runQuery(fromjson("{a: 1, b: {$not: {$exists: false}}}"));
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists("{fetch: {node: {ixscan: {pattern: {a: 1, b: 1}, bounds: "
+ "{a: [[1,1,true,true]], b: [['MinKey','MaxKey',true,true]]}}}}}");
+
+ runQuery(fromjson("{a: 1, b: {$exists: false}}"));
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists("{fetch: {filter: {b: {$exists: false}}, node: "
+ "{ixscan: {pattern: {a: 1, b: 1}, bounds: "
+ "{a: [[1,1,true,true]], b: [[null,null,true,true]]}}}}}");
+
+ runQuery(fromjson("{a: 1, b: {$not: {$exists: true}}}"));
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists("{fetch: {filter: {b: {$exists: false}}, node: "
+ "{ixscan: {pattern: {a: 1, b: 1}, bounds: "
+ "{a: [[1,1,true,true]], b: [[null,null,true,true]]}}}}}");
+ }
+
//
// skip and limit
//
@@ -1525,6 +1587,19 @@ namespace {
"node: {ixscan: {filter: null, pattern: {'foo.b': 1}}}}}");*/
}
+ TEST_F(QueryPlannerTest, BasicAllElemMatch2) {
+ // true means multikey
+ addIndex(BSON("a.x" << 1), true);
+
+ runQuery(fromjson("{a: {$all: [{$elemMatch: {x: 3}}, {$elemMatch: {y: 5}}]}}"));
+
+ assertNumSolutions(2U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ assertSolutionExists("{fetch: {filter: {a:{$all:[{$elemMatch:{x:3}},{$elemMatch:{y:5}}]}},"
+ "node: {ixscan: {pattern: {'a.x': 1},"
+ "bounds: {'a.x': [[3,3,true,true]]}}}}}");
+ }
+
// SERVER-13677
TEST_F(QueryPlannerTest, ElemMatchWithAllElemMatchChild) {
addIndex(BSON("a.b.c.d" << 1));
@@ -3535,6 +3610,21 @@ namespace {
"bounds: {a: [['MinKey','MaxKey',true,true]]}}}}}");
}
+ TEST_F(QueryPlannerTest, BoundsTypeMinKeyMaxKey) {
+ params.options = QueryPlannerParams::NO_TABLE_SCAN;
+ addIndex(BSON("a" << 1));
+
+ runQuery(fromjson("{a: {$type: -1}}"));
+ assertNumSolutions(1U);
+ assertSolutionExists("{fetch: {node: {ixscan: {pattern: {a: 1}, bounds:"
+ "{a: [['MinKey','MinKey',true,true]]}}}}}");
+
+ runQuery(fromjson("{a: {$type: 127}}"));
+ assertNumSolutions(1U);
+ assertSolutionExists("{fetch: {node: {ixscan: {pattern: {a: 1}, bounds:"
+ "{a: [['MaxKey','MaxKey',true,true]]}}}}}");
+ }
+
//
// Tests related to building index bounds for multikey
// indices, combined with compound and $elemMatch
diff --git a/src/mongo/db/query/type_explain.cpp b/src/mongo/db/query/type_explain.cpp
deleted file mode 100644
index 329eb1dcb28..00000000000
--- a/src/mongo/db/query/type_explain.cpp
+++ /dev/null
@@ -1,807 +0,0 @@
-/**
- * Copyright (C) 2013 10gen Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects
- * for all of the code used other than as permitted herein. If you modify
- * file(s) with this exception, you may extend this exception to your
- * version of the file(s), but you are not obligated to do so. If you do not
- * wish to do so, delete this exception statement from your version. If you
- * delete this exception statement from all source files in the program,
- * then also delete it in the license file.
- */
-
-// THIS FILE IS DEPRECATED -- the old explain implementation is being replaced
-
-#include "mongo/db/query/type_explain.h"
-
-#include "mongo/db/field_parser.h"
-#include "mongo/util/mongoutils/str.h"
-
-namespace mongo {
-
- // TODO: This doesn't need to be so complicated or serializable. Let's throw this out when we
- // move to explain V2
-
- using mongoutils::str::stream;
-
- const BSONField<std::vector<TypeExplain*> > TypeExplain::clauses("clauses");
- const BSONField<std::string> TypeExplain::cursor("cursor");
- const BSONField<bool> TypeExplain::isMultiKey("isMultiKey");
- const BSONField<long long> TypeExplain::n("n", 0);
- const BSONField<long long> TypeExplain::nScannedObjects("nscannedObjects", 0);
- const BSONField<long long> TypeExplain::nScanned("nscanned", 0);
- const BSONField<long long> TypeExplain::nScannedObjectsAllPlans("nscannedObjectsAllPlans");
- const BSONField<long long> TypeExplain::nScannedAllPlans("nscannedAllPlans");
- const BSONField<bool> TypeExplain::scanAndOrder("scanAndOrder");
- const BSONField<bool> TypeExplain::indexOnly("indexOnly");
- const BSONField<long long> TypeExplain::nYields("nYields");
- const BSONField<long long> TypeExplain::nChunkSkips("nChunkSkips");
- const BSONField<long long> TypeExplain::millis("millis");
- const BSONField<BSONObj> TypeExplain::indexBounds("indexBounds");
- const BSONField<std::vector<TypeExplain*> > TypeExplain::allPlans("allPlans");
- const BSONField<TypeExplain*> TypeExplain::oldPlan("oldPlan");
- const BSONField<bool> TypeExplain::indexFilterApplied("filterSet");
- const BSONField<std::string> TypeExplain::server("server");
-
- TypeExplain::TypeExplain() {
- clear();
- }
-
- TypeExplain::~TypeExplain() {
- unsetClauses();
- unsetAllPlans();
- }
-
- bool TypeExplain::isValid(std::string* errMsg) const {
- std::string dummy;
- if (errMsg == NULL) {
- errMsg = &dummy;
- }
-
- // All the mandatory fields must be present.
- if (!_isNSet) {
- *errMsg = stream() << "missing " << n.name() << " field";
- return false;
- }
-
- if (!_isNScannedObjectsSet) {
- *errMsg = stream() << "missing " << nScannedObjects.name() << " field";
- return false;
- }
-
- if (!_isNScannedSet) {
- *errMsg = stream() << "missing " << nScanned.name() << " field";
- return false;
- }
-
- return true;
- }
-
- BSONObj TypeExplain::toBSON() const {
- BSONObjBuilder builder;
-
- if (_clauses.get()) {
- BSONArrayBuilder clausesBuilder(builder.subarrayStart(clauses()));
- for (std::vector<TypeExplain*>::const_iterator it = _clauses->begin();
- it != _clauses->end();
- ++it) {
- BSONObj clausesDocument = (*it)->toBSON();
- clausesBuilder.append(clausesDocument);
- }
- clausesBuilder.done();
- }
-
- if (_isCursorSet) builder.append(cursor(), _cursor);
-
- if (_isIsMultiKeySet) builder.append(isMultiKey(), _isMultiKey);
-
- if (_isNSet) {
- builder.appendNumber(n(), _n);
- }
- else {
- builder.appendNumber(n(), n.getDefault());
- }
-
- if (_isNScannedObjectsSet) {
- builder.appendNumber(nScannedObjects(), _nScannedObjects);
- }
- else {
- builder.appendNumber(nScannedObjects(), nScannedObjects.getDefault());
- }
-
- if (_isNScannedSet) {
- builder.appendNumber(nScanned(), _nScanned);
- }
- else {
- builder.appendNumber(nScanned(), nScanned.getDefault());
- }
-
- if (_isNScannedObjectsAllPlansSet)
- builder.appendNumber(nScannedObjectsAllPlans(), _nScannedObjectsAllPlans);
-
- if (_isNScannedAllPlansSet) builder.appendNumber(nScannedAllPlans(), _nScannedAllPlans);
-
- if (_isScanAndOrderSet) builder.append(scanAndOrder(), _scanAndOrder);
-
- if (_isIndexOnlySet) builder.append(indexOnly(), _indexOnly);
-
- if (_isNYieldsSet) builder.appendNumber(nYields(), _nYields);
-
- if (_isNChunkSkipsSet) builder.appendNumber(nChunkSkips(), _nChunkSkips);
-
- if (_isMillisSet) builder.appendNumber(millis(), _millis);
-
- if (_isIndexBoundsSet) builder.append(indexBounds(), _indexBounds);
-
- if (_allPlans.get()) {
- BSONArrayBuilder allPlansBuilder(builder.subarrayStart(allPlans()));
- for (std::vector<TypeExplain*>::const_iterator it = _allPlans->begin();
- it != _allPlans->end();
- ++it) {
- BSONObj allPlansObject = (*it)->toBSON();
- allPlansBuilder.append(allPlansObject);
- }
- allPlansBuilder.done();
- }
-
- if (_oldPlan.get()) builder.append(oldPlan(), _oldPlan->toBSON());
-
- if (_isServerSet) builder.append(server(), _server);
-
- if (_isIndexFilterAppliedSet) builder.append(indexFilterApplied(), _indexFilterApplied);
-
- // Add this at the end as it can be huge
- if (!stats.isEmpty()) {
- builder.append("stats", stats);
- }
-
- return builder.obj();
- }
-
- bool TypeExplain::parseBSON(const BSONObj& source, string* errMsg) {
- clear();
-
- std::string dummy;
- if (!errMsg) errMsg = &dummy;
-
- FieldParser::FieldState fieldState;
-
- std::vector<TypeExplain*>* bareClauses = NULL;
- fieldState = FieldParser::extract(source, clauses, &bareClauses, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- if (fieldState == FieldParser::FIELD_SET) _clauses.reset(bareClauses);
-
- fieldState = FieldParser::extract(source, cursor, &_cursor, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isCursorSet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, isMultiKey, &_isMultiKey, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isIsMultiKeySet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, n, &_n, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isNSet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, nScannedObjects, &_nScannedObjects, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isNScannedObjectsSet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, nScanned, &_nScanned, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isNScannedSet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source,
- nScannedObjectsAllPlans,
- &_nScannedObjectsAllPlans,
- errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isNScannedObjectsAllPlansSet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, nScannedAllPlans, &_nScannedAllPlans, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isNScannedAllPlansSet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, scanAndOrder, &_scanAndOrder, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isScanAndOrderSet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, indexOnly, &_indexOnly, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isIndexOnlySet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, nYields, &_nYields, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isNYieldsSet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, nChunkSkips, &_nChunkSkips, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isNChunkSkipsSet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, millis, &_millis, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isMillisSet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, indexBounds, &_indexBounds, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isIndexBoundsSet = fieldState == FieldParser::FIELD_SET;
-
- std::vector<TypeExplain*>* bareAllPlans = NULL;
- fieldState = FieldParser::extract(source, allPlans, &bareAllPlans, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- if (fieldState == FieldParser::FIELD_SET) _allPlans.reset(bareAllPlans);
-
- TypeExplain* bareOldPlan = NULL;
- fieldState = FieldParser::extract(source, oldPlan, &bareOldPlan, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- if (fieldState == FieldParser::FIELD_SET) _oldPlan.reset(bareOldPlan);
-
- fieldState = FieldParser::extract(source, server, &_server, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isServerSet = fieldState == FieldParser::FIELD_SET;
-
- return true;
- }
-
- void TypeExplain::clear() {
- unsetClauses();
-
- _cursor.clear();
- _isCursorSet = false;
-
- _isMultiKey = false;
- _isIsMultiKeySet = false;
-
- _n = 0;
- _isNSet = false;
-
- _nScannedObjects = 0;
- _isNScannedObjectsSet = false;
-
- _nScanned = 0;
- _isNScannedSet = false;
-
- _nScannedObjectsAllPlans = 0;
- _isNScannedObjectsAllPlansSet = false;
-
- _nScannedAllPlans = 0;
- _isNScannedAllPlansSet = false;
-
- _scanAndOrder = false;
- _isScanAndOrderSet = false;
-
- _indexOnly = false;
- _isIndexOnlySet = false;
-
- _idHack = false;
- _isIDHackSet = false;
-
- _indexFilterApplied = false;
- _isIndexFilterAppliedSet = false;
-
- _nYields = 0;
- _isNYieldsSet = false;
-
- _nChunkSkips = 0;
- _isNChunkSkipsSet = false;
-
- _millis = 0;
- _isMillisSet = false;
-
- _indexBounds = BSONObj();
- _isIndexBoundsSet = false;
-
- unsetAllPlans();
-
- unsetOldPlan();
-
- _server.clear();
- _isServerSet = false;
-
- }
-
- void TypeExplain::cloneTo(TypeExplain* other) const {
- other->clear();
-
- other->unsetClauses();
- if (_clauses.get()) {
- for(std::vector<TypeExplain*>::const_iterator it = _clauses->begin();
- it != _clauses->end();
- ++it) {
- TypeExplain* clausesItem = new TypeExplain;
- (*it)->cloneTo(clausesItem);
- other->addToClauses(clausesItem);
- }
- }
-
- other->_cursor = _cursor;
- other->_isCursorSet = _isCursorSet;
-
- other->_isMultiKey = _isMultiKey;
- other->_isIsMultiKeySet = _isIsMultiKeySet;
-
- other->_n = _n;
- other->_isNSet = _isNSet;
-
- other->_nScannedObjects = _nScannedObjects;
- other->_isNScannedObjectsSet = _isNScannedObjectsSet;
-
- other->_nScanned = _nScanned;
- other->_isNScannedSet = _isNScannedSet;
-
- other->_nScannedObjectsAllPlans = _nScannedObjectsAllPlans;
- other->_isNScannedObjectsAllPlansSet = _isNScannedObjectsAllPlansSet;
-
- other->_nScannedAllPlans = _nScannedAllPlans;
- other->_isNScannedAllPlansSet = _isNScannedAllPlansSet;
-
- other->_scanAndOrder = _scanAndOrder;
- other->_isScanAndOrderSet = _isScanAndOrderSet;
-
- other->_indexOnly = _indexOnly;
- other->_isIndexOnlySet = _isIndexOnlySet;
-
- other->_idHack = _idHack;
- other->_isIDHackSet = _isIDHackSet;
-
- other->_indexFilterApplied = _indexFilterApplied;
- other->_isIndexFilterAppliedSet = _isIndexFilterAppliedSet;
-
- other->_nYields = _nYields;
- other->_isNYieldsSet = _isNYieldsSet;
-
- other->_nChunkSkips = _nChunkSkips;
- other->_isNChunkSkipsSet = _isNChunkSkipsSet;
-
- other->_millis = _millis;
- other->_isMillisSet = _isMillisSet;
-
- other->_indexBounds = _indexBounds;
- other->_isIndexBoundsSet = _isIndexBoundsSet;
-
- other->unsetAllPlans();
- if (_allPlans.get()) {
- for(std::vector<TypeExplain*>::const_iterator it = _allPlans->begin();
- it != _allPlans->end();
- ++it) {
- TypeExplain* allPlansItem = new TypeExplain;
- (*it)->cloneTo(allPlansItem);
- other->addToAllPlans(allPlansItem);
- }
- }
-
- other->unsetOldPlan();
- if (_oldPlan.get()) {
- TypeExplain* oldPlanCopy = new TypeExplain;
- _oldPlan->cloneTo(oldPlanCopy);
- other->setOldPlan(oldPlanCopy);
- }
-
- other->_server = _server;
- other->_isServerSet = _isServerSet;
- }
-
- std::string TypeExplain::toString() const {
- return toBSON().toString();
- }
-
- void TypeExplain::setClauses(const std::vector<TypeExplain*>& clauses) {
- unsetClauses();
- for(std::vector<TypeExplain*>::const_iterator it = clauses.begin();
- it != clauses.end();
- ++it) {
- TypeExplain* clausesItem = new TypeExplain;
- (*it)->cloneTo(clausesItem);
- addToClauses(clausesItem);
- }
- }
-
- void TypeExplain::addToClauses(TypeExplain* clauses) {
- if (_clauses.get() == NULL) {
- _clauses.reset(new std::vector<TypeExplain*>);
- }
- _clauses->push_back(clauses);
- }
-
- void TypeExplain::unsetClauses() {
- if (_clauses.get()) {
- for(std::vector<TypeExplain*>::const_iterator it = _clauses->begin();
- it != _clauses->end();
- ++it) {
- delete *it;
- }
- }
- _clauses.reset();
- }
-
- bool TypeExplain::isClausesSet() const {
- return _clauses.get() != NULL;
- }
-
- size_t TypeExplain::sizeClauses() const {
- verify(_clauses.get());
- return _clauses->size();
- }
-
- const std::vector<TypeExplain*>& TypeExplain::getClauses() const {
- verify(_clauses.get());
- return *_clauses;
- }
-
- const TypeExplain* TypeExplain::getClausesAt(size_t pos) const {
- verify(_clauses.get());
- verify(_clauses->size() > pos);
- return _clauses->at(pos);
- }
-
- void TypeExplain::setCursor(const StringData& cursor) {
- _cursor = cursor.toString();
- _isCursorSet = true;
- }
-
- void TypeExplain::unsetCursor() {
- _isCursorSet = false;
- }
-
- bool TypeExplain::isCursorSet() const {
- return _isCursorSet;
- }
-
- const std::string& TypeExplain::getCursor() const {
- verify(_isCursorSet);
- return _cursor;
- }
-
- void TypeExplain::setIsMultiKey(bool isMultiKey) {
- _isMultiKey = isMultiKey;
- _isIsMultiKeySet = true;
- }
-
- void TypeExplain::unsetIsMultiKey() {
- _isIsMultiKeySet = false;
- }
-
- bool TypeExplain::isIsMultiKeySet() const {
- return _isIsMultiKeySet;
- }
-
- bool TypeExplain::getIsMultiKey() const {
- verify(_isIsMultiKeySet);
- return _isMultiKey;
- }
-
- void TypeExplain::setN(long long n) {
- _n = n;
- _isNSet = true;
- }
-
- void TypeExplain::unsetN() {
- _isNSet = false;
- }
-
- bool TypeExplain::isNSet() const {
- return _isNSet;
- }
-
- long long TypeExplain::getN() const {
- verify(_isNSet);
- return _n;
- }
-
- void TypeExplain::setNScannedObjects(long long nScannedObjects) {
- _nScannedObjects = nScannedObjects;
- _isNScannedObjectsSet = true;
- }
-
- void TypeExplain::unsetNScannedObjects() {
- _isNScannedObjectsSet = false;
- }
-
- bool TypeExplain::isNScannedObjectsSet() const {
- return _isNScannedObjectsSet;
- }
-
- long long TypeExplain::getNScannedObjects() const {
- verify(_isNScannedObjectsSet);
- return _nScannedObjects;
- }
-
- void TypeExplain::setNScanned(long long nScanned) {
- _nScanned = nScanned;
- _isNScannedSet = true;
- }
-
- void TypeExplain::unsetNScanned() {
- _isNScannedSet = false;
- }
-
- bool TypeExplain::isNScannedSet() const {
- return _isNScannedSet;
- }
-
- long long TypeExplain::getNScanned() const {
- verify(_isNScannedSet);
- return _nScanned;
- }
-
- void TypeExplain::setNScannedObjectsAllPlans(long long nScannedObjectsAllPlans) {
- _nScannedObjectsAllPlans = nScannedObjectsAllPlans;
- _isNScannedObjectsAllPlansSet = true;
- }
-
- void TypeExplain::unsetNScannedObjectsAllPlans() {
- _isNScannedObjectsAllPlansSet = false;
- }
-
- bool TypeExplain::isNScannedObjectsAllPlansSet() const {
- return _isNScannedObjectsAllPlansSet;
- }
-
- long long TypeExplain::getNScannedObjectsAllPlans() const {
- verify(_isNScannedObjectsAllPlansSet);
- return _nScannedObjectsAllPlans;
- }
-
- void TypeExplain::setNScannedAllPlans(long long nScannedAllPlans) {
- _nScannedAllPlans = nScannedAllPlans;
- _isNScannedAllPlansSet = true;
- }
-
- void TypeExplain::unsetNScannedAllPlans() {
- _isNScannedAllPlansSet = false;
- }
-
- bool TypeExplain::isNScannedAllPlansSet() const {
- return _isNScannedAllPlansSet;
- }
-
- long long TypeExplain::getNScannedAllPlans() const {
- verify(_isNScannedAllPlansSet);
- return _nScannedAllPlans;
- }
-
- void TypeExplain::setScanAndOrder(bool scanAndOrder) {
- _scanAndOrder = scanAndOrder;
- _isScanAndOrderSet = true;
- }
-
- void TypeExplain::unsetScanAndOrder() {
- _isScanAndOrderSet = false;
- }
-
- bool TypeExplain::isScanAndOrderSet() const {
- return _isScanAndOrderSet;
- }
-
- bool TypeExplain::getScanAndOrder() const {
- verify(_isScanAndOrderSet);
- return _scanAndOrder;
- }
-
- void TypeExplain::setIndexOnly(bool indexOnly) {
- _indexOnly = indexOnly;
- _isIndexOnlySet = true;
- }
-
- void TypeExplain::unsetIndexOnly() {
- _isIndexOnlySet = false;
- }
-
- bool TypeExplain::isIndexOnlySet() const {
- return _isIndexOnlySet;
- }
-
- bool TypeExplain::getIndexOnly() const {
- verify(_isIndexOnlySet);
- return _indexOnly;
- }
-
- void TypeExplain::setIDHack(bool idhack) {
- _idHack = idhack;
- _isIDHackSet = true;
- }
-
- void TypeExplain::unsetIDHack() {
- _isIDHackSet = false;
- }
-
- bool TypeExplain::isIDHackSet() const {
- return _isIDHackSet;
- }
-
- bool TypeExplain::getIDHack() const {
- verify(_isIDHackSet);
- return _idHack;
- }
-
- void TypeExplain::setIndexFilterApplied(bool indexFilterApplied) {
- _indexFilterApplied = indexFilterApplied;
- _isIndexFilterAppliedSet = true;
- }
-
- void TypeExplain::unsetIndexFilterApplied() {
- _isIndexFilterAppliedSet = false;
- }
-
- bool TypeExplain::isIndexFilterAppliedSet() const {
- return _isIndexFilterAppliedSet;
- }
-
- bool TypeExplain::getIndexFilterApplied() const {
- verify(_isIndexFilterAppliedSet);
- return _indexFilterApplied;
- }
-
- void TypeExplain::setNYields(long long nYields) {
- _nYields = nYields;
- _isNYieldsSet = true;
- }
-
- void TypeExplain::unsetNYields() {
- _isNYieldsSet = false;
- }
-
- bool TypeExplain::isNYieldsSet() const {
- return _isNYieldsSet;
- }
-
- long long TypeExplain::getNYields() const {
- verify(_isNYieldsSet);
- return _nYields;
- }
-
- void TypeExplain::setNChunkSkips(long long nChunkSkips) {
- _nChunkSkips = nChunkSkips;
- _isNChunkSkipsSet = true;
- }
-
- void TypeExplain::unsetNChunkSkips() {
- _isNChunkSkipsSet = false;
- }
-
- bool TypeExplain::isNChunkSkipsSet() const {
- return _isNChunkSkipsSet;
- }
-
- long long TypeExplain::getNChunkSkips() const {
- verify(_isNChunkSkipsSet);
- return _nChunkSkips;
- }
-
- void TypeExplain::setMillis(long long millis) {
- _millis = millis;
- _isMillisSet = true;
- }
-
- void TypeExplain::unsetMillis() {
- _isMillisSet = false;
- }
-
- bool TypeExplain::isMillisSet() const {
- return _isMillisSet;
- }
-
- long long TypeExplain::getMillis() const {
- verify(_isMillisSet);
- return _millis;
- }
-
- void TypeExplain::setIndexBounds(const BSONObj& indexBounds) {
- _indexBounds = indexBounds.getOwned();
- _isIndexBoundsSet = true;
- }
-
- void TypeExplain::unsetIndexBounds() {
- _isIndexBoundsSet = false;
- }
-
- bool TypeExplain::isIndexBoundsSet() const {
- return _isIndexBoundsSet;
- }
-
- const BSONObj& TypeExplain::getIndexBounds() const {
- verify(_isIndexBoundsSet);
- return _indexBounds;
- }
-
- void TypeExplain::setAllPlans(const std::vector<TypeExplain*>& allPlans) {
- unsetAllPlans();
- for (std::vector<TypeExplain*>::const_iterator it = allPlans.begin();
- it != allPlans.end();
- ++it) {
- TypeExplain* allPlansItem = new TypeExplain;
- (*it)->cloneTo(allPlansItem);
- addToClauses(allPlansItem);
- }
- }
-
- void TypeExplain::addToAllPlans(TypeExplain* allPlans) {
- if (_allPlans.get() == NULL) {
- _allPlans.reset(new std::vector<TypeExplain*>);
- }
- _allPlans->push_back(allPlans);
- }
-
- void TypeExplain::unsetAllPlans() {
- if (_allPlans.get()) {
- for (std::vector<TypeExplain*>::const_iterator it = _allPlans->begin();
- it != _allPlans->end();
- ++it) {
- delete *it;
- }
- _allPlans.reset();
- }
- }
-
- bool TypeExplain::isAllPlansSet() const {
- return _allPlans.get() != NULL;
- }
-
- size_t TypeExplain::sizeAllPlans() const {
- verify(_allPlans.get());
- return _allPlans->size();
- }
-
- const std::vector<TypeExplain*>& TypeExplain::getAllPlans() const {
- verify(_allPlans.get());
- return *_allPlans;
- }
-
- const TypeExplain* TypeExplain::getAllPlansAt(size_t pos) const {
- verify(_allPlans.get());
- verify(_allPlans->size() > pos);
- return _allPlans->at(pos);
- }
-
- void TypeExplain::setOldPlan(TypeExplain* oldPlan) {
- _oldPlan.reset(oldPlan);
- }
-
- void TypeExplain::unsetOldPlan() {
- _oldPlan.reset();
- }
-
- bool TypeExplain::isOldPlanSet() const {
- return _oldPlan.get() != NULL;
- }
-
- const TypeExplain* TypeExplain::getOldPlan() const {
- verify(_oldPlan.get());
- return _oldPlan.get();
- }
-
- void TypeExplain::setServer(const StringData& server) {
- _server = server.toString();
- _isServerSet = true;
- }
-
- void TypeExplain::unsetServer() {
- _isServerSet = false;
- }
-
- bool TypeExplain::isServerSet() const {
- return _isServerSet;
- }
-
- const std::string& TypeExplain::getServer() const {
- verify(_isServerSet);
- return _server;
- }
-
-} // namespace mongo
diff --git a/src/mongo/db/query/type_explain.h b/src/mongo/db/query/type_explain.h
deleted file mode 100644
index 6cca71f2bf0..00000000000
--- a/src/mongo/db/query/type_explain.h
+++ /dev/null
@@ -1,292 +0,0 @@
-/**
- * Copyright (C) 2013 10gen Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the GNU Affero General Public License in all respects
- * for all of the code used other than as permitted herein. If you modify
- * file(s) with this exception, you may extend this exception to your
- * version of the file(s), but you are not obligated to do so. If you do not
- * wish to do so, delete this exception statement from your version. If you
- * delete this exception statement from all source files in the program,
- * then also delete it in the license file.
- */
-
-// THIS FILE IS DEPRECATED -- the old explain implementation is being replaced
-
-#pragma once
-
-#include <string>
-
-#include "mongo/base/string_data.h"
-#include "mongo/bson/bson_field.h"
-#include "mongo/s/bson_serializable.h"
-
-namespace mongo {
-
- /**
- * Contains query debug information that describes the
- * query plan. Generally this information depends only on
- * the planning process that happens without running the
- * query. The exception is the multi plan runner, in which
- * case plan selection depends on actually running the query.
- *
- * Currently, just a summary std::string describing the plan
- * used to run the query.
- */
- struct PlanInfo {
- PlanInfo() : planSummary("") { }
- std::string planSummary;
- };
-
- /**
- * This class represents the layout and content of a TypeExplain runCommand,
- * the response side.
- */
- class TypeExplain : public BSONSerializable {
- MONGO_DISALLOW_COPYING(TypeExplain);
- public:
-
- //
- // schema declarations
- //
-
- static const BSONField<std::vector<TypeExplain*> > clauses;
- static const BSONField<std::string> cursor;
- static const BSONField<bool> isMultiKey;
- static const BSONField<long long> n;
- static const BSONField<long long> nScannedObjects;
- static const BSONField<long long> nScanned;
- static const BSONField<long long> nScannedObjectsAllPlans;
- static const BSONField<long long> nScannedAllPlans;
- static const BSONField<bool> scanAndOrder;
- static const BSONField<bool> indexOnly;
- static const BSONField<long long> nYields;
- static const BSONField<long long> nChunkSkips;
- static const BSONField<long long> millis;
- static const BSONField<BSONObj> indexBounds;
- static const BSONField<std::vector<TypeExplain*> > allPlans;
- static const BSONField<TypeExplain*> oldPlan;
- static const BSONField<bool> indexFilterApplied;
- static const BSONField<std::string> server;
-
- //
- // construction / destruction
- //
-
- TypeExplain();
- virtual ~TypeExplain();
-
- /** Copies all the fields present in 'this' to 'other'. */
- void cloneTo(TypeExplain* other) const;
-
- //
- // bson serializable interface implementation
- //
-
- virtual bool isValid(std::string* errMsg) const;
- virtual BSONObj toBSON() const;
- virtual bool parseBSON(const BSONObj& source, std::string* errMsg);
- virtual void clear();
- virtual std::string toString() const;
-
- //
- // individual field accessors
- //
-
- void setClauses(const std::vector<TypeExplain*>& clauses);
- void addToClauses(TypeExplain* clauses);
- void unsetClauses();
- bool isClausesSet() const;
- size_t sizeClauses() const;
- const std::vector<TypeExplain*>& getClauses() const;
- const TypeExplain* getClausesAt(size_t pos) const;
-
- void setCursor(const StringData& cursor);
- void unsetCursor();
- bool isCursorSet() const;
- const std::string& getCursor() const;
-
- void setIsMultiKey(bool isMultiKey);
- void unsetIsMultiKey();
- bool isIsMultiKeySet() const;
- bool getIsMultiKey() const;
-
- void setN(long long n);
- void unsetN();
- bool isNSet() const;
- long long getN() const;
-
- void setNScannedObjects(long long nScannedObjects);
- void unsetNScannedObjects();
- bool isNScannedObjectsSet() const;
- long long getNScannedObjects() const;
-
- void setNScanned(long long nScanned);
- void unsetNScanned();
- bool isNScannedSet() const;
- long long getNScanned() const;
-
- void setNScannedObjectsAllPlans(long long nScannedObjectsAllPlans);
- void unsetNScannedObjectsAllPlans();
- bool isNScannedObjectsAllPlansSet() const;
- long long getNScannedObjectsAllPlans() const;
-
- void setNScannedAllPlans(long long nScannedAllPlans);
- void unsetNScannedAllPlans();
- bool isNScannedAllPlansSet() const;
- long long getNScannedAllPlans() const;
-
- void setScanAndOrder(bool scanAndOrder);
- void unsetScanAndOrder();
- bool isScanAndOrderSet() const;
- bool getScanAndOrder() const;
-
- void setIndexOnly(bool indexOnly);
- void unsetIndexOnly();
- bool isIndexOnlySet() const;
- bool getIndexOnly() const;
-
- void setIDHack(bool idhack);
- void unsetIDHack();
- bool isIDHackSet() const;
- bool getIDHack() const;
-
- void setIndexFilterApplied(bool indexFilterApplied);
- void unsetIndexFilterApplied();
- bool isIndexFilterAppliedSet() const;
- bool getIndexFilterApplied() const;
-
- void setNYields(long long nYields);
- void unsetNYields();
- bool isNYieldsSet() const;
- long long getNYields() const;
-
- void setNChunkSkips(long long nChunkSkips);
- void unsetNChunkSkips();
- bool isNChunkSkipsSet() const;
- long long getNChunkSkips() const;
-
- void setMillis(long long millis);
- void unsetMillis();
- bool isMillisSet() const;
- long long getMillis() const;
-
- void setIndexBounds(const BSONObj& indexBounds);
- void unsetIndexBounds();
- bool isIndexBoundsSet() const;
- const BSONObj& getIndexBounds() const;
-
- void setAllPlans(const std::vector<TypeExplain*>& allPlans);
- void addToAllPlans(TypeExplain* allPlans);
- void unsetAllPlans();
- bool isAllPlansSet() const;
- size_t sizeAllPlans() const;
- const std::vector<TypeExplain*>& getAllPlans() const;
- const TypeExplain* getAllPlansAt(size_t pos) const;
-
- void setOldPlan(TypeExplain* oldPlan);
- void unsetOldPlan();
- bool isOldPlanSet() const;
- const TypeExplain* getOldPlan() const;
-
- void setServer(const StringData& server);
- void unsetServer();
- bool isServerSet() const;
- const std::string& getServer() const;
-
- // Opaque stats object
- BSONObj stats;
-
- private:
- // Convention: (M)andatory, (O)ptional
-
- // (O) explain for branches on a $or query
- boost::scoped_ptr<std::vector<TypeExplain*> >_clauses;
-
- // (O) type and name of the cursor used on the leaf stage
- std::string _cursor;
- bool _isCursorSet;
-
- // (O) type and name of the cursor used on the leaf stage
- bool _isMultiKey;
- bool _isIsMultiKeySet;
-
- // (M) number of documents returned by the query
- long long _n;
- bool _isNSet;
-
- // (M) number of documents fetched entirely from the disk
- long long _nScannedObjects;
- bool _isNScannedObjectsSet;
-
- // (M) number of entries retrieved either from an index or collection
- long long _nScanned;
- bool _isNScannedSet;
-
- // (O) number of documents fetched entirely from the disk across all plans
- long long _nScannedObjectsAllPlans;
- bool _isNScannedObjectsAllPlansSet;
-
- // (O) number of entries retrieved either from an index or collection across all plans
- long long _nScannedAllPlans;
- bool _isNScannedAllPlansSet;
-
- // (O) whether this plan involved sorting
- bool _scanAndOrder;
- bool _isScanAndOrderSet;
-
- // (O) number of entries retrieved either from an index or collection across all plans
- bool _indexOnly;
- bool _isIndexOnlySet;
-
- // (O) whether the idhack was used to answer this query
- bool _idHack;
- bool _isIDHackSet;
-
- // (O) whether index filters were used in planning this query
- bool _indexFilterApplied;
- bool _isIndexFilterAppliedSet;
-
- // (O) number times this plan released and reacquired its lock
- long long _nYields;
- bool _isNYieldsSet;
-
- // (O) number times this plan skipped over migrated data
- long long _nChunkSkips;
- bool _isNChunkSkipsSet;
-
- // (O) elapsed time this plan took running, in milliseconds
- long long _millis;
- bool _isMillisSet;
-
- // (O) keys used to seek in and out of an index
- BSONObj _indexBounds;
- bool _isIndexBoundsSet;
-
- // (O) alternative plans considered
- boost::scoped_ptr<std::vector<TypeExplain*> > _allPlans;
-
- // (O) cached plan for this query
- boost::scoped_ptr<TypeExplain> _oldPlan;
-
- // (O) server's host:port against which the query ran
- std::string _server;
- bool _isServerSet;
- };
-
-} // namespace mongo
diff --git a/src/mongo/dbtests/querytests.cpp b/src/mongo/dbtests/querytests.cpp
index 48686d3c621..a14d3e620b3 100644
--- a/src/mongo/dbtests/querytests.cpp
+++ b/src/mongo/dbtests/querytests.cpp
@@ -632,8 +632,9 @@ namespace QueryTests {
// Check number of results and filterSet flag in explain.
// filterSet is not available in oplog replay mode.
BSONObj explainObj = c->next();
- ASSERT_EQUALS( 1, explainObj.getIntField( "n" ) );
- ASSERT_FALSE( explainObj.hasField( "filterSet" ) );
+ ASSERT( explainObj.hasField("executionStats") );
+ BSONObj execStats = explainObj["executionStats"].Obj();
+ ASSERT_EQUALS( 1, execStats.getIntField( "nReturned" ) );
ASSERT( !c->more() );
}
@@ -973,26 +974,6 @@ namespace QueryTests {
checkMatch();
_client.ensureIndex( _ns, BSON( "a" << 1 ) );
checkMatch();
- // Use explain queries to check index bounds.
- {
- BSONObj explain = _client.findOne( _ns, QUERY( "a" << BSON( "$type" << (int)Code ) ).explain() );
- BSONObjBuilder lower;
- lower.appendCode( "", "" );
- BSONObjBuilder upper;
- upper.appendCodeWScope( "", "", BSONObj() );
- ASSERT( lower.done().firstElement().valuesEqual( explain[ "indexBounds" ].Obj()[ "a" ].Array()[ 0 ].Array()[ 0 ] ) );
- ASSERT( upper.done().firstElement().valuesEqual( explain[ "indexBounds" ].Obj()[ "a" ].Array()[ 0 ].Array()[ 1 ] ) );
- }
- {
- BSONObj explain = _client.findOne( _ns, QUERY( "a" << BSON( "$type" << (int)CodeWScope ) ).explain() );
- BSONObjBuilder lower;
- lower.appendCodeWScope( "", "", BSONObj() );
- // This upper bound may change if a new bson type is added.
- BSONObjBuilder upper;
- upper << "" << BSON( "$maxElement" << 1 );
- ASSERT( lower.done().firstElement().valuesEqual( explain[ "indexBounds" ].Obj()[ "a" ].Array()[ 0 ].Array()[ 0 ] ) );
- ASSERT( upper.done().firstElement().valuesEqual( explain[ "indexBounds" ].Obj()[ "a" ].Array()[ 0 ].Array()[ 1 ] ) );
- }
}
private:
void checkMatch() {
diff --git a/src/mongo/s/strategy.cpp b/src/mongo/s/strategy.cpp
index 7ef2cf0c40b..34d05825598 100644
--- a/src/mongo/s/strategy.cpp
+++ b/src/mongo/s/strategy.cpp
@@ -171,7 +171,7 @@ namespace mongo {
if ( qSpec.isExplain() ) {
BSONObjBuilder explain_builder;
cursor->explain( explain_builder );
- explain_builder.appendNumber( "millis",
+ explain_builder.appendNumber( "executionTimeMillis",
static_cast<long long>(queryTimer.millis()) );
BSONObj b = explain_builder.obj();
diff --git a/src/mongo/shell/shardingtest.js b/src/mongo/shell/shardingtest.js
index b3c0a1171ca..1c8241ec392 100644
--- a/src/mongo/shell/shardingtest.js
+++ b/src/mongo/shell/shardingtest.js
@@ -874,21 +874,21 @@ ShardingTest.prototype.getShard = function( coll, query, includeEmpty ){
ShardingTest.prototype.getShards = function( coll, query, includeEmpty ){
if( ! coll.getDB )
coll = this.s.getCollection( coll )
-
+
var explain = coll.find( query ).explain()
var shards = []
-
+
if( explain.shards ){
-
- for( var shardName in explain.shards ){
+ for( var shardName in explain.shards ){
for( var i = 0; i < explain.shards[shardName].length; i++ ){
- if( includeEmpty || ( explain.shards[shardName][i].n && explain.shards[shardName][i].n > 0 ) )
+ var hasResults = explain.shards[shardName][i].executionStats.nReturned &&
+ explain.shards[shardName][i].executionStats.nReturned > 0;
+ if( includeEmpty || hasResults )
shards.push( shardName )
}
}
-
}
-
+
for( var i = 0; i < shards.length; i++ ){
for( var j = 0; j < this._connections.length; j++ ){
if ( connectionURLTheSame( this._connections[j] , shards[i] ) ){
@@ -897,7 +897,7 @@ ShardingTest.prototype.getShards = function( coll, query, includeEmpty ){
}
}
}
-
+
return shards
}