Skip to content

Instantly share code, notes, and snippets.

@evan-stripe
Created August 19, 2020 19:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save evan-stripe/df24064abc5d61d665d0f4daf2cdbe5c to your computer and use it in GitHub Desktop.
Save evan-stripe/df24064abc5d61d665d0f4daf2cdbe5c to your computer and use it in GitHub Desktop.
> db.test.save({a: 1})
WriteResult({ "nInserted" : 1 })
> db.test.save({a: 2})
WriteResult({ "nInserted" : 1 })
> db.test.save({a: 3})
WriteResult({ "nInserted" : 1 })
> db.test.runCommand({find: 'test', filter: {a: {$gte: 2}}, includeExecutionStats: true})
{
"cursor" : {
"firstBatch" : [
{
"_id" : ObjectId("5f35bb945edc03d8581bd9c4"),
"a" : 2
},
{
"_id" : ObjectId("5f35bb955edc03d8581bd9c5"),
"a" : 3
}
],
"id" : NumberLong(0),
"ns" : "test.test"
},
"executionStats" : {
"keysExamined" : 0,
"docsExamined" : 3,
"hasSortStage" : false,
"planSummary" : "COLLSCAN"
},
"ok" : 1
}
> db.test.ensureIndex({a: 1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.test.runCommand({find: 'test', filter: {a: {$gte: 2}}, includeExecutionStats: true})
{
"cursor" : {
"firstBatch" : [
{
"_id" : ObjectId("5f35bb945edc03d8581bd9c4"),
"a" : 2
},
{
"_id" : ObjectId("5f35bb955edc03d8581bd9c5"),
"a" : 3
}
],
"id" : NumberLong(0),
"ns" : "test.test"
},
"executionStats" : {
"keysExamined" : 2,
"docsExamined" : 2,
"hasSortStage" : false,
"planSummary" : "IXSCAN { a: 1 }"
},
"ok" : 1
}
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index cba4f1d224..22c7caaff7 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -483,6 +483,15 @@ public:
throw;
}
+ // Need to grab the stats now before we capture the cursor
+ PlanSummaryStats summaryStats;
+ std::string planSummary;
+ bool includeExecutionStats(originalQR.getIncludeExecutionStats());
+ if (includeExecutionStats) {
+ exec->getSummaryStats(&summaryStats);
+ planSummary = exec->getPlanSummary();
+ }
+
// Set up the cursor for getMore.
CursorId cursorId = 0;
if (shouldSaveCursor(opCtx, collection, state, exec.get())) {
@@ -523,6 +532,17 @@ public:
// Generate the response object to send to the client.
firstBatch.done(cursorId, nss.ns());
+
+ // Now that the cursor response is closed, we can add the execution stats we captured
+ // above
+ if (includeExecutionStats) {
+ auto bodyBuilder = result->getBodyBuilder();
+ BSONObjBuilder executionStatsBuilder(bodyBuilder.subobjStart("executionStats"));
+ executionStatsBuilder.appendNumber("keysExamined", summaryStats.totalKeysExamined);
+ executionStatsBuilder.appendNumber("docsExamined", summaryStats.totalDocsExamined);
+ executionStatsBuilder.appendBool("hasSortStage", summaryStats.hasSortStage);
+ executionStatsBuilder.append("planSummary", planSummary);
+ }
}
void appendMirrorableRequest(BSONObjBuilder* bob) const override {
diff --git a/src/mongo/db/query/query_request.cpp b/src/mongo/db/query/query_request.cpp
index b9601a5a1f..2f82416b89 100644
--- a/src/mongo/db/query/query_request.cpp
+++ b/src/mongo/db/query/query_request.cpp
@@ -251,6 +251,13 @@ StatusWith<std::unique_ptr<QueryRequest>> QueryRequest::parseFromFindCommand(
}
qr->_showRecordId = el.boolean();
+ } else if (fieldName == kIncludeExecutionStatsField) {
+ Status status = checkFieldType(el, Bool);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ qr->_includeExecutionStats = el.boolean();
} else if (fieldName == kTailableField) {
Status status = checkFieldType(el, Bool);
if (!status.isOK()) {
@@ -499,6 +506,10 @@ void QueryRequest::asFindCommandInternal(BSONObjBuilder* cmdBuilder) const {
cmdBuilder->append(kShowRecordIdField, true);
}
+ if (_includeExecutionStats) {
+ cmdBuilder->append(kIncludeExecutionStatsField, true);
+ }
+
switch (_tailableMode) {
case TailableModeEnum::kTailable: {
cmdBuilder->append(kTailableField, true);
diff --git a/src/mongo/db/query/query_request.h b/src/mongo/db/query/query_request.h
index bfb9615dbc..1666e761ae 100644
--- a/src/mongo/db/query/query_request.h
+++ b/src/mongo/db/query/query_request.h
@@ -82,6 +82,7 @@ public:
static constexpr auto kResumeAfterField = "$_resumeAfter";
static constexpr auto kUse44SortKeys = "_use44SortKeys";
static constexpr auto kMaxTimeMSOpOnlyField = "maxTimeMSOpOnly";
+ static constexpr auto kIncludeExecutionStatsField = "includeExecutionStats";
// Field names for sorting options.
static constexpr auto kNaturalSortField = "$natural";
@@ -449,6 +450,14 @@ public:
_resumeAfter = resumeAfter;
}
+ bool getIncludeExecutionStats() const {
+ return _includeExecutionStats;
+ }
+
+ void setIncludeExecutionStats(bool includeExecutionStats) {
+ _includeExecutionStats = includeExecutionStats;
+ }
+
/**
* Return options as a bit vector.
*/
@@ -575,6 +584,10 @@ private:
// inside $expr.
boost::optional<BSONObj> _letParameters;
+ // If specified, include explain-style query execution stats as part of the
+ // find return value
+ bool _includeExecutionStats = false;
+
// Options that can be specified in the OP_QUERY 'flags' header.
TailableModeEnum _tailableMode = TailableModeEnum::kNormal;
bool _slaveOk = false;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment