summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/core/profile_agg.js2
-rw-r--r--jstests/core/profile_count.js2
-rw-r--r--jstests/core/profile_delete.js2
-rw-r--r--jstests/core/profile_distinct.js2
-rw-r--r--jstests/core/profile_find.js2
-rw-r--r--jstests/core/profile_getmore.js4
-rw-r--r--jstests/core/profile_group.js4
-rw-r--r--jstests/core/profile_mapreduce.js2
-rw-r--r--jstests/core/profile_update.js4
-rw-r--r--src/mongo/db/keypattern.cpp30
-rw-r--r--src/mongo/db/keypattern.h8
-rw-r--r--src/mongo/db/query/explain.cpp34
12 files changed, 69 insertions, 27 deletions
diff --git a/jstests/core/profile_agg.js b/jstests/core/profile_agg.js
index 209d0ce8b22..3422d171335 100644
--- a/jstests/core/profile_agg.js
+++ b/jstests/core/profile_agg.js
@@ -29,7 +29,7 @@
assert.eq(profileObj.nreturned, 8, tojson(profileObj));
assert.eq(profileObj.keysExamined, 8, tojson(profileObj));
assert.eq(profileObj.docsExamined, 8, tojson(profileObj));
- assert.eq(profileObj.planSummary, "IXSCAN { a: 1.0 }", tojson(profileObj));
+ assert.eq(profileObj.planSummary, "IXSCAN { a: 1 }", tojson(profileObj));
assert.eq(profileObj.protocol,
getProfilerProtocolStringForCommand(testDB.getMongo()),
tojson(profileObj));
diff --git a/jstests/core/profile_count.js b/jstests/core/profile_count.js
index 5e448201811..2fb9063a25b 100644
--- a/jstests/core/profile_count.js
+++ b/jstests/core/profile_count.js
@@ -68,7 +68,7 @@
assert.eq(profileObj.command.query, query, tojson(profileObj));
assert.eq(profileObj.keysExamined, 6, tojson(profileObj));
- assert.eq(profileObj.planSummary, "COUNT_SCAN { a: 1.0 }", tojson(profileObj));
+ assert.eq(profileObj.planSummary, "COUNT_SCAN { a: 1 }", tojson(profileObj));
assert(profileObj.execStats.hasOwnProperty("stage"), tojson(profileObj));
assert.eq(profileObj.appName, "MongoDB Shell", tojson(profileObj));
diff --git a/jstests/core/profile_delete.js b/jstests/core/profile_delete.js
index 937c6327e45..85f180fc18a 100644
--- a/jstests/core/profile_delete.js
+++ b/jstests/core/profile_delete.js
@@ -37,7 +37,7 @@
assert.eq(profileObj.keysExamined, 1, tojson(profileObj));
assert.eq(profileObj.docsExamined, 1, tojson(profileObj));
assert.eq(profileObj.keysDeleted, 2, tojson(profileObj));
- assert.eq(profileObj.planSummary, "IXSCAN { a: 1.0 }", tojson(profileObj));
+ assert.eq(profileObj.planSummary, "IXSCAN { a: 1 }", tojson(profileObj));
assert(profileObj.execStats.hasOwnProperty("stage"), tojson(profileObj));
assert(profileObj.hasOwnProperty("millis"), tojson(profileObj));
assert(profileObj.hasOwnProperty("numYield"), tojson(profileObj));
diff --git a/jstests/core/profile_distinct.js b/jstests/core/profile_distinct.js
index aea24f14333..432deffc542 100644
--- a/jstests/core/profile_distinct.js
+++ b/jstests/core/profile_distinct.js
@@ -29,7 +29,7 @@
assert.eq(profileObj.op, "command", tojson(profileObj));
assert.eq(profileObj.keysExamined, 5, tojson(profileObj));
assert.eq(profileObj.docsExamined, 5, tojson(profileObj));
- assert.eq(profileObj.planSummary, "IXSCAN { b: 1.0 }", tojson(profileObj));
+ assert.eq(profileObj.planSummary, "IXSCAN { b: 1 }", tojson(profileObj));
assert(profileObj.execStats.hasOwnProperty("stage"), tojson(profileObj));
assert.eq(profileObj.protocol, getProfilerProtocolStringForCommand(conn), tojson(profileObj));
assert.eq(coll.getName(), profileObj.command.distinct, tojson(profileObj));
diff --git a/jstests/core/profile_find.js b/jstests/core/profile_find.js
index ed7dc62638c..36116c5a6fa 100644
--- a/jstests/core/profile_find.js
+++ b/jstests/core/profile_find.js
@@ -35,7 +35,7 @@
assert.eq(profileObj.keysExamined, 1, tojson(profileObj));
assert.eq(profileObj.docsExamined, 1, tojson(profileObj));
assert.eq(profileObj.nreturned, 1, tojson(profileObj));
- assert.eq(profileObj.planSummary, "IXSCAN { a: 1.0 }", tojson(profileObj));
+ assert.eq(profileObj.planSummary, "IXSCAN { a: 1 }", tojson(profileObj));
assert(profileObj.execStats.hasOwnProperty("stage"), tojson(profileObj));
assert.eq(profileObj.query.filter, {a: 1}, tojson(profileObj));
if (isLegacyReadMode) {
diff --git a/jstests/core/profile_getmore.js b/jstests/core/profile_getmore.js
index f9e606fa748..a9272567b1a 100644
--- a/jstests/core/profile_getmore.js
+++ b/jstests/core/profile_getmore.js
@@ -42,7 +42,7 @@
assert.eq(profileObj.originatingCommand.filter, {a: {$gt: 0}});
assert.eq(profileObj.originatingCommand.sort, {a: 1});
}
- assert.eq(profileObj.planSummary, "IXSCAN { a: 1.0 }", tojson(profileObj));
+ assert.eq(profileObj.planSummary, "IXSCAN { a: 1 }", tojson(profileObj));
assert(profileObj.execStats.hasOwnProperty("stage"), tojson(profileObj));
assert(profileObj.hasOwnProperty("responseLength"), tojson(profileObj));
assert(profileObj.hasOwnProperty("numYield"), tojson(profileObj));
@@ -117,7 +117,7 @@
}
assert.eq(profileObj.cursorid, cursorId, tojson(profileObj));
assert.eq(profileObj.nreturned, 20, tojson(profileObj));
- assert.eq(profileObj.planSummary, "IXSCAN { a: 1.0 }", tojson(profileObj));
+ assert.eq(profileObj.planSummary, "IXSCAN { a: 1 }", tojson(profileObj));
assert.eq(profileObj.cursorExhausted, true, tojson(profileObj));
assert.eq(profileObj.keysExamined, 20, tojson(profileObj));
assert.eq(profileObj.docsExamined, 20, tojson(profileObj));
diff --git a/jstests/core/profile_group.js b/jstests/core/profile_group.js
index f1a2e3b48b3..cfcec3dfc1b 100644
--- a/jstests/core/profile_group.js
+++ b/jstests/core/profile_group.js
@@ -20,7 +20,7 @@
for (i = 0; i < 10; ++i) {
assert.writeOK(coll.insert({a: i, b: i % 5}));
}
- assert.commandWorked(coll.createIndex({b: 1}));
+ assert.commandWorked(coll.createIndex({b: -1}));
coll.group({
key: {a: 1, b: 1},
@@ -35,7 +35,7 @@
assert.eq(profileObj.op, "command", tojson(profileObj));
assert.eq(profileObj.keysExamined, 2, tojson(profileObj));
assert.eq(profileObj.docsExamined, 2, tojson(profileObj));
- assert.eq(profileObj.planSummary, "IXSCAN { b: 1.0 }", tojson(profileObj));
+ assert.eq(profileObj.planSummary, "IXSCAN { b: -1 }", tojson(profileObj));
assert(profileObj.execStats.hasOwnProperty("stage"), tojson(profileObj));
assert.eq(profileObj.protocol, getProfilerProtocolStringForCommand(conn), tojson(profileObj));
assert.eq(profileObj.command.group.key, {a: 1, b: 1}, tojson(profileObj));
diff --git a/jstests/core/profile_mapreduce.js b/jstests/core/profile_mapreduce.js
index 181e8684501..d579091aff7 100644
--- a/jstests/core/profile_mapreduce.js
+++ b/jstests/core/profile_mapreduce.js
@@ -40,7 +40,7 @@
assert.eq(profileObj.op, "command", tojson(profileObj));
assert.eq(profileObj.keysExamined, 3, tojson(profileObj));
assert.eq(profileObj.docsExamined, 3, tojson(profileObj));
- assert.eq(profileObj.planSummary, "IXSCAN { a: 1.0 }", tojson(profileObj));
+ assert.eq(profileObj.planSummary, "IXSCAN { a: 1 }", tojson(profileObj));
assert(profileObj.execStats.hasOwnProperty("stage"), tojson(profileObj));
assert.eq(profileObj.protocol, getProfilerProtocolStringForCommand(conn), tojson(profileObj));
assert.eq(coll.getName(), profileObj.command.mapreduce, tojson(profileObj));
diff --git a/jstests/core/profile_update.js b/jstests/core/profile_update.js
index 41727b89580..f4e04179a53 100644
--- a/jstests/core/profile_update.js
+++ b/jstests/core/profile_update.js
@@ -39,7 +39,7 @@
assert.eq(profileObj.keysDeleted, 1, tojson(profileObj));
assert.eq(profileObj.nMatched, 1, tojson(profileObj));
assert.eq(profileObj.nModified, 1, tojson(profileObj));
- assert.eq(profileObj.planSummary, "IXSCAN { a: 1.0 }", tojson(profileObj));
+ assert.eq(profileObj.planSummary, "IXSCAN { a: 1 }", tojson(profileObj));
assert(profileObj.execStats.hasOwnProperty("stage"), tojson(profileObj));
assert(profileObj.hasOwnProperty("millis"), tojson(profileObj));
assert(profileObj.hasOwnProperty("numYield"), tojson(profileObj));
@@ -64,7 +64,7 @@
assert.eq(profileObj.keysDeleted, 5, tojson(profileObj));
assert.eq(profileObj.nMatched, 5, tojson(profileObj));
assert.eq(profileObj.nModified, 5, tojson(profileObj));
- assert.eq(profileObj.planSummary, "IXSCAN { a: 1.0 }", tojson(profileObj));
+ assert.eq(profileObj.planSummary, "IXSCAN { a: 1 }", tojson(profileObj));
assert(profileObj.execStats.hasOwnProperty("stage"), tojson(profileObj));
assert.eq(profileObj.appName, "MongoDB Shell", tojson(profileObj));
diff --git a/src/mongo/db/keypattern.cpp b/src/mongo/db/keypattern.cpp
index 2c1bf09497d..ffc2a1f8e71 100644
--- a/src/mongo/db/keypattern.cpp
+++ b/src/mongo/db/keypattern.cpp
@@ -31,7 +31,6 @@
#include "mongo/db/keypattern.h"
#include "mongo/db/index_names.h"
-#include "mongo/util/mongoutils/str.h"
namespace mongo {
@@ -55,6 +54,35 @@ bool KeyPattern::isHashedKeyPattern(const BSONObj& pattern) {
return IndexNames::HASHED == IndexNames::findPluginName(pattern);
}
+StringBuilder& operator<<(StringBuilder& sb, const KeyPattern& keyPattern) {
+ // Rather than return BSONObj::toString() we construct a keyPattern string manually. This allows
+ // us to avoid the cost of writing numeric direction to the str::stream which will then undergo
+ // expensive number to string conversion.
+ sb << "{ ";
+
+ bool first = true;
+ for (auto&& elem : keyPattern._pattern) {
+ if (first) {
+ first = false;
+ } else {
+ sb << ", ";
+ }
+
+ if (mongo::String == elem.type()) {
+ sb << elem;
+ } else if (elem.number() >= 0) {
+ // The canonical check as to whether a key pattern element is "ascending" or
+ // "descending" is (elem.number() >= 0). This is defined by the Ordering class.
+ sb << elem.fieldNameStringData() << ": 1";
+ } else {
+ sb << elem.fieldNameStringData() << ": -1";
+ }
+ }
+
+ sb << " }";
+ return sb;
+}
+
BSONObj KeyPattern::extendRangeBound(const BSONObj& bound, bool makeUpperInclusive) const {
BSONObjBuilder newBound(bound.objsize());
diff --git a/src/mongo/db/keypattern.h b/src/mongo/db/keypattern.h
index f9ac83983e3..cfa24da5df2 100644
--- a/src/mongo/db/keypattern.h
+++ b/src/mongo/db/keypattern.h
@@ -29,7 +29,9 @@
#pragma once
#include "mongo/base/string_data.h"
+#include "mongo/bson/util/builder.h"
#include "mongo/db/jsobj.h"
+#include "mongo/util/mongoutils/str.h"
namespace mongo {
@@ -80,9 +82,13 @@ public:
* Returns a string representation of this KeyPattern
*/
std::string toString() const {
- return toBSON().toString();
+ return str::stream() << *this;
}
+ /**
+ * Writes to 'sb' a string representation of this KeyPattern.
+ */
+ friend StringBuilder& operator<<(StringBuilder& sb, const KeyPattern& keyPattern);
/* Takes a BSONObj whose field names are a prefix of the fields in this keyPattern, and
* outputs a new bound with MinKey values appended to match the fields in this keyPattern
diff --git a/src/mongo/db/query/explain.cpp b/src/mongo/db/query/explain.cpp
index 929950dc38a..7d83021dbd5 100644
--- a/src/mongo/db/query/explain.cpp
+++ b/src/mongo/db/query/explain.cpp
@@ -31,6 +31,7 @@
#include "mongo/db/query/explain.h"
#include "mongo/base/owned_pointer_vector.h"
+#include "mongo/bson/util/builder.h"
#include "mongo/db/exec/cached_plan.h"
#include "mongo/db/exec/count_scan.h"
#include "mongo/db/exec/distinct_scan.h"
@@ -41,6 +42,7 @@
#include "mongo/db/exec/pipeline_proxy.h"
#include "mongo/db/exec/text.h"
#include "mongo/db/exec/working_set_common.h"
+#include "mongo/db/keypattern.h"
#include "mongo/db/query/get_executor.h"
#include "mongo/db/query/plan_executor.h"
#include "mongo/db/query/plan_summary_stats.h"
@@ -166,33 +168,39 @@ size_t getDocsExamined(StageType type, const SpecificStats* specific) {
}
/**
- * Adds to the plan summary string being built by 'ss' for the execution stage 'stage'.
+ * Adds to the plan summary string being built by 'sb' for the execution stage 'stage'.
*/
-void addStageSummaryStr(const PlanStage* stage, mongoutils::str::stream& ss) {
+void addStageSummaryStr(const PlanStage* stage, StringBuilder& sb) {
// First add the stage type string.
const CommonStats* common = stage->getCommonStats();
- ss << common->stageTypeStr;
+ sb << common->stageTypeStr;
// Some leaf nodes also provide info about the index they used.
const SpecificStats* specific = stage->getSpecificStats();
if (STAGE_COUNT_SCAN == stage->stageType()) {
const CountScanStats* spec = static_cast<const CountScanStats*>(specific);
- ss << " " << spec->keyPattern;
+ const KeyPattern keyPattern{spec->keyPattern};
+ sb << " " << keyPattern;
} else if (STAGE_DISTINCT_SCAN == stage->stageType()) {
const DistinctScanStats* spec = static_cast<const DistinctScanStats*>(specific);
- ss << " " << spec->keyPattern;
+ const KeyPattern keyPattern{spec->keyPattern};
+ sb << " " << keyPattern;
} else if (STAGE_GEO_NEAR_2D == stage->stageType()) {
const NearStats* spec = static_cast<const NearStats*>(specific);
- ss << " " << spec->keyPattern;
+ const KeyPattern keyPattern{spec->keyPattern};
+ sb << " " << keyPattern;
} else if (STAGE_GEO_NEAR_2DSPHERE == stage->stageType()) {
const NearStats* spec = static_cast<const NearStats*>(specific);
- ss << " " << spec->keyPattern;
+ const KeyPattern keyPattern{spec->keyPattern};
+ sb << " " << keyPattern;
} else if (STAGE_IXSCAN == stage->stageType()) {
const IndexScanStats* spec = static_cast<const IndexScanStats*>(specific);
- ss << " " << spec->keyPattern;
+ const KeyPattern keyPattern{spec->keyPattern};
+ sb << " " << keyPattern;
} else if (STAGE_TEXT == stage->stageType()) {
const TextStats* spec = static_cast<const TextStats*>(specific);
- ss << " " << spec->indexPrefix;
+ const KeyPattern keyPattern{spec->indexPrefix};
+ sb << " " << keyPattern;
}
}
@@ -801,7 +809,7 @@ std::string Explain::getPlanSummary(const PlanStage* root) {
flattenExecTree(root, &stages);
// Use this stream to build the plan summary string.
- mongoutils::str::stream ss;
+ StringBuilder sb;
bool seenLeaf = false;
for (size_t i = 0; i < stages.size(); i++) {
@@ -809,15 +817,15 @@ std::string Explain::getPlanSummary(const PlanStage* root) {
// This is a leaf node. Add to the plan summary string accordingly. Unless
// this is the first leaf we've seen, add a delimiting string first.
if (seenLeaf) {
- ss << ", ";
+ sb << ", ";
} else {
seenLeaf = true;
}
- addStageSummaryStr(stages[i], ss);
+ addStageSummaryStr(stages[i], sb);
}
}
- return ss;
+ return sb.str();
}
// static