diff options
-rw-r--r-- | jstests/core/profile_agg.js | 2 | ||||
-rw-r--r-- | jstests/core/profile_count.js | 2 | ||||
-rw-r--r-- | jstests/core/profile_delete.js | 2 | ||||
-rw-r--r-- | jstests/core/profile_distinct.js | 2 | ||||
-rw-r--r-- | jstests/core/profile_find.js | 2 | ||||
-rw-r--r-- | jstests/core/profile_getmore.js | 4 | ||||
-rw-r--r-- | jstests/core/profile_group.js | 4 | ||||
-rw-r--r-- | jstests/core/profile_mapreduce.js | 2 | ||||
-rw-r--r-- | jstests/core/profile_update.js | 4 | ||||
-rw-r--r-- | src/mongo/db/keypattern.cpp | 30 | ||||
-rw-r--r-- | src/mongo/db/keypattern.h | 8 | ||||
-rw-r--r-- | src/mongo/db/query/explain.cpp | 34 |
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 |