summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuoxin Xu <ruoxin.xu@mongodb.com>2020-10-08 14:30:18 +0100
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-11-10 16:30:30 +0000
commitafbc28ca7d7e5aae4eaff1609a55af8e79ef275f (patch)
treebf10914fa035de048d69d6e7f75f41081199f22a
parent1761282aff97de315fe1f232c780e0a9fb998799 (diff)
downloadmongo-afbc28ca7d7e5aae4eaff1609a55af8e79ef275f.tar.gz
SERVER-35069 Explain should include command being explained
-rw-r--r--jstests/core/explain_includes_command.js53
-rw-r--r--src/mongo/db/commands/count_cmd.cpp2
-rw-r--r--src/mongo/db/commands/distinct.cpp3
-rw-r--r--src/mongo/db/commands/find_and_modify.cpp4
-rw-r--r--src/mongo/db/commands/find_cmd.cpp3
-rw-r--r--src/mongo/db/commands/map_reduce_agg.cpp2
-rw-r--r--src/mongo/db/commands/run_aggregate.cpp3
-rw-r--r--src/mongo/db/commands/write_commands/write_commands.cpp23
-rw-r--r--src/mongo/db/pipeline/document_source_cursor.cpp1
-rw-r--r--src/mongo/db/query/explain.cpp18
-rw-r--r--src/mongo/db/query/explain.h8
-rw-r--r--src/mongo/db/query/explain_common.cpp20
-rw-r--r--src/mongo/db/query/explain_common.h6
-rw-r--r--src/mongo/db/query/find.cpp8
-rw-r--r--src/mongo/dbtests/query_stage_multiplan.cpp8
-rw-r--r--src/mongo/s/commands/cluster_count_cmd.cpp2
-rw-r--r--src/mongo/s/commands/cluster_distinct_cmd.cpp2
-rw-r--r--src/mongo/s/commands/cluster_explain.cpp2
-rw-r--r--src/mongo/s/commands/cluster_explain.h1
-rw-r--r--src/mongo/s/commands/cluster_find_and_modify_cmd.cpp8
-rw-r--r--src/mongo/s/commands/cluster_find_cmd.cpp8
-rw-r--r--src/mongo/s/commands/cluster_write_cmd.cpp8
-rw-r--r--src/mongo/s/commands/strategy.cpp2
-rw-r--r--src/mongo/s/query/cluster_aggregate.cpp6
24 files changed, 173 insertions, 28 deletions
diff --git a/jstests/core/explain_includes_command.js b/jstests/core/explain_includes_command.js
new file mode 100644
index 00000000000..633770118db
--- /dev/null
+++ b/jstests/core/explain_includes_command.js
@@ -0,0 +1,53 @@
+/*
+ * Confirms that the explain command includes the command object that was run.
+ *
+ * @tags: [requires_fcv_49, sbe_incompatible]
+ */
+(function() {
+"use strict";
+const collName = "explain_includes_command";
+
+db[collName].drop();
+assert.commandWorked(db[collName].insert({_id: 0}));
+assert.commandWorked(db[collName].insert({_id: 1}));
+assert.commandWorked(db[collName].insert({_id: 2}));
+
+/*
+ * Runs explain on 'cmdToExplain' and ensures that the explain output contains the expected
+ * 'command' field.
+ */
+function testExplainContainsCommand(cmdToExplain) {
+ const verbosity = ["queryPlanner", "executionStats", "allPlansExecution"];
+ for (const option of verbosity) {
+ const explainOutput = db.runCommand({explain: cmdToExplain, verbosity: option});
+ assert("command" in explainOutput);
+ const explainCmd = explainOutput["command"];
+ for (let key of Object.keys(cmdToExplain)) {
+ assert.eq(explainCmd[key], cmdToExplain[key]);
+ }
+ }
+}
+
+// Test 'find'.
+testExplainContainsCommand({find: collName, filter: {}});
+testExplainContainsCommand({find: collName, filter: {_id: 1}});
+
+// Test 'aggregate'.
+testExplainContainsCommand({aggregate: collName, pipeline: [{$match: {_id: 1}}], cursor: {}});
+
+// Test 'update'.
+testExplainContainsCommand({update: collName, updates: [{q: {_id: 1}, u: {_id: 10}}]});
+
+// Test 'delete'.
+testExplainContainsCommand({delete: collName, deletes: [{q: {_id: 1}, limit: 0}]});
+
+// Test 'findAndModify'.
+testExplainContainsCommand({findAndModify: collName, query: {_id: 10}, update: {_id: 1}});
+testExplainContainsCommand({findAndModify: collName, query: {_id: 1}, remove: true});
+
+// Test 'count'.
+testExplainContainsCommand({count: collName, query: {_id: 1}});
+
+// Test 'distinct'.
+testExplainContainsCommand({distinct: collName, key: "a"});
+})();
diff --git a/src/mongo/db/commands/count_cmd.cpp b/src/mongo/db/commands/count_cmd.cpp
index ed880886f40..2f9a844950b 100644
--- a/src/mongo/db/commands/count_cmd.cpp
+++ b/src/mongo/db/commands/count_cmd.cpp
@@ -198,7 +198,7 @@ public:
auto exec = std::move(statusWithPlanExecutor.getValue());
auto bodyBuilder = result->getBodyBuilder();
- Explain::explainStages(exec.get(), collection, verbosity, BSONObj(), &bodyBuilder);
+ Explain::explainStages(exec.get(), collection, verbosity, BSONObj(), cmdObj, &bodyBuilder);
return Status::OK();
}
diff --git a/src/mongo/db/commands/distinct.cpp b/src/mongo/db/commands/distinct.cpp
index ab5f0145184..24ff3604b35 100644
--- a/src/mongo/db/commands/distinct.cpp
+++ b/src/mongo/db/commands/distinct.cpp
@@ -180,7 +180,8 @@ public:
getExecutorDistinct(&collection, QueryPlannerParams::DEFAULT, &parsedDistinct));
auto bodyBuilder = result->getBodyBuilder();
- Explain::explainStages(executor.get(), collection, verbosity, BSONObj(), &bodyBuilder);
+ Explain::explainStages(
+ executor.get(), collection, verbosity, BSONObj(), cmdObj, &bodyBuilder);
return Status::OK();
}
diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp
index cade28cbfa1..4586958eeb7 100644
--- a/src/mongo/db/commands/find_and_modify.cpp
+++ b/src/mongo/db/commands/find_and_modify.cpp
@@ -299,7 +299,7 @@ public:
auto bodyBuilder = result->getBodyBuilder();
Explain::explainStages(
- exec.get(), collection.getCollection(), verbosity, BSONObj(), &bodyBuilder);
+ exec.get(), collection.getCollection(), verbosity, BSONObj(), cmdObj, &bodyBuilder);
} else {
auto request = UpdateRequest();
request.setNamespaceString(nsString);
@@ -323,7 +323,7 @@ public:
auto bodyBuilder = result->getBodyBuilder();
Explain::explainStages(
- exec.get(), collection.getCollection(), verbosity, BSONObj(), &bodyBuilder);
+ exec.get(), collection.getCollection(), verbosity, BSONObj(), cmdObj, &bodyBuilder);
}
return Status::OK();
diff --git a/src/mongo/db/commands/find_cmd.cpp b/src/mongo/db/commands/find_cmd.cpp
index afcb39f35c9..142d6bb9464 100644
--- a/src/mongo/db/commands/find_cmd.cpp
+++ b/src/mongo/db/commands/find_cmd.cpp
@@ -298,7 +298,8 @@ public:
auto bodyBuilder = result->getBodyBuilder();
// Got the execution tree. Explain it.
- Explain::explainStages(exec.get(), collection, verbosity, BSONObj(), &bodyBuilder);
+ Explain::explainStages(
+ exec.get(), collection, verbosity, BSONObj(), _request.body, &bodyBuilder);
}
/**
diff --git a/src/mongo/db/commands/map_reduce_agg.cpp b/src/mongo/db/commands/map_reduce_agg.cpp
index 84ec6647e60..4e404097d93 100644
--- a/src/mongo/db/commands/map_reduce_agg.cpp
+++ b/src/mongo/db/commands/map_reduce_agg.cpp
@@ -144,7 +144,7 @@ bool runAggregationMapReduce(OperationContext* opCtx,
if (expCtx->explain) {
Explain::explainPipeline(
- exec.get(), false /* executePipeline */, *expCtx->explain, &result);
+ exec.get(), false /* executePipeline */, *expCtx->explain, cmd, &result);
}
PlanSummaryStats planSummaryStats;
diff --git a/src/mongo/db/commands/run_aggregate.cpp b/src/mongo/db/commands/run_aggregate.cpp
index 753c6ca1f79..7c8f6565026 100644
--- a/src/mongo/db/commands/run_aggregate.cpp
+++ b/src/mongo/db/commands/run_aggregate.cpp
@@ -761,7 +761,7 @@ Status runAggregate(OperationContext* opCtx,
auto bodyBuilder = result->getBodyBuilder();
if (auto pipelineExec = dynamic_cast<PlanExecutorPipeline*>(explainExecutor)) {
Explain::explainPipeline(
- pipelineExec, true /* executePipeline */, *(expCtx->explain), &bodyBuilder);
+ pipelineExec, true /* executePipeline */, *(expCtx->explain), cmdObj, &bodyBuilder);
} else {
invariant(explainExecutor->getOpCtx() == opCtx);
// The explainStages() function for a non-pipeline executor may need to execute the plan
@@ -773,6 +773,7 @@ Status runAggregate(OperationContext* opCtx,
ctx->getCollection(),
*(expCtx->explain),
BSON("optimizedPipeline" << true),
+ cmdObj,
&bodyBuilder);
}
} else {
diff --git a/src/mongo/db/commands/write_commands/write_commands.cpp b/src/mongo/db/commands/write_commands/write_commands.cpp
index af7b96c9741..de4e0632cb8 100644
--- a/src/mongo/db/commands/write_commands/write_commands.cpp
+++ b/src/mongo/db/commands/write_commands/write_commands.cpp
@@ -557,8 +557,12 @@ private:
&parsedUpdate,
verbosity));
auto bodyBuilder = result->getBodyBuilder();
- Explain::explainStages(
- exec.get(), collection.getCollection(), verbosity, BSONObj(), &bodyBuilder);
+ Explain::explainStages(exec.get(),
+ collection.getCollection(),
+ verbosity,
+ BSONObj(),
+ _commandObj,
+ &bodyBuilder);
}
write_ops::Update _batch;
@@ -600,8 +604,9 @@ private:
class Invocation final : public InvocationBase {
public:
Invocation(const WriteCommand* cmd, const OpMsgRequest& request)
- : InvocationBase(cmd, request), _batch(DeleteOp::parse(request)) {}
-
+ : InvocationBase(cmd, request),
+ _batch(DeleteOp::parse(request)),
+ _commandObj(request.body) {}
private:
NamespaceString ns() const override {
@@ -654,11 +659,17 @@ private:
&parsedDelete,
verbosity));
auto bodyBuilder = result->getBodyBuilder();
- Explain::explainStages(
- exec.get(), collection.getCollection(), verbosity, BSONObj(), &bodyBuilder);
+ Explain::explainStages(exec.get(),
+ collection.getCollection(),
+ verbosity,
+ BSONObj(),
+ _commandObj,
+ &bodyBuilder);
}
write_ops::Delete _batch;
+
+ const BSONObj& _commandObj;
};
std::unique_ptr<CommandInvocation> parse(OperationContext*,
diff --git a/src/mongo/db/pipeline/document_source_cursor.cpp b/src/mongo/db/pipeline/document_source_cursor.cpp
index a9310e2ca7c..2bfc196c45c 100644
--- a/src/mongo/db/pipeline/document_source_cursor.cpp
+++ b/src/mongo/db/pipeline/document_source_cursor.cpp
@@ -228,6 +228,7 @@ Value DocumentSourceCursor::serialize(boost::optional<ExplainOptions::Verbosity>
_execStatus,
_winningPlanTrialStats,
BSONObj(),
+ BSONObj(),
&explainStatsBuilder);
}
diff --git a/src/mongo/db/query/explain.cpp b/src/mongo/db/query/explain.cpp
index a921e706501..17257934cc8 100644
--- a/src/mongo/db/query/explain.cpp
+++ b/src/mongo/db/query/explain.cpp
@@ -66,6 +66,7 @@
namespace mongo {
namespace {
+
/**
* Adds the 'queryPlanner' explain section to the BSON object being built by 'out'.
*
@@ -269,6 +270,7 @@ void Explain::explainStages(PlanExecutor* exec,
Status executePlanStatus,
boost::optional<PlanExplainer::PlanStatsDetails> winningPlanTrialStats,
BSONObj extraInfo,
+ const BSONObj& command,
BSONObjBuilder* out) {
//
// Use the stats trees to produce explain BSON.
@@ -281,11 +283,14 @@ void Explain::explainStages(PlanExecutor* exec,
if (verbosity >= ExplainOptions::Verbosity::kExecStats) {
generateExecutionInfo(exec, verbosity, executePlanStatus, winningPlanTrialStats, out);
}
+
+ explain_common::appendIfRoom(command, "command", out);
}
void Explain::explainPipeline(PlanExecutor* exec,
bool executePipeline,
ExplainOptions::Verbosity verbosity,
+ const BSONObj& command,
BSONObjBuilder* out) {
invariant(exec);
invariant(out);
@@ -303,12 +308,15 @@ void Explain::explainPipeline(PlanExecutor* exec,
*out << "stages" << Value(pipelineExec->writeExplainOps(verbosity));
explain_common::generateServerInfo(out);
+
+ explain_common::appendIfRoom(command, "command", out);
}
void Explain::explainStages(PlanExecutor* exec,
const CollectionPtr& collection,
ExplainOptions::Verbosity verbosity,
BSONObj extraInfo,
+ const BSONObj& command,
BSONObjBuilder* out) {
auto&& explainer = exec->getPlanExplainer();
auto winningPlanTrialStats = explainer.getWinningPlanStats(verbosity);
@@ -331,8 +339,14 @@ void Explain::explainStages(PlanExecutor* exec,
}
}
- explainStages(
- exec, *collectionPtr, verbosity, executePlanStatus, winningPlanTrialStats, extraInfo, out);
+ explainStages(exec,
+ *collectionPtr,
+ verbosity,
+ executePlanStatus,
+ winningPlanTrialStats,
+ extraInfo,
+ command,
+ out);
explain_common::generateServerInfo(out);
}
diff --git a/src/mongo/db/query/explain.h b/src/mongo/db/query/explain.h
index b59cccb1c96..453e518d85a 100644
--- a/src/mongo/db/query/explain.h
+++ b/src/mongo/db/query/explain.h
@@ -59,6 +59,8 @@ public:
*
* The 'extraInfo' parameter specifies additional information to include into the output.
*
+ * The 'command' parameter represents the command object that is being explained.
+ *
* Does not take ownership of its arguments.
*
* During this call it may be required to execute the plan to collect statistics. If the
@@ -72,6 +74,7 @@ public:
const CollectionPtr& collection,
ExplainOptions::Verbosity verbosity,
BSONObj extraInfo,
+ const BSONObj& command,
BSONObjBuilder* out);
/**
* Adds "queryPlanner" and "executionStats" (if requested in verbosity) fields to 'out'. Unlike
@@ -88,6 +91,7 @@ public:
* query wasn't executed).
* - 'winningPlanTrialStats' is the stats of the winning plan during the trial period. May be
* nullptr.
+ * - 'command' represents the command object that is being explained.
* - 'out' is the builder for the explain output.
*/
static void explainStages(
@@ -97,6 +101,7 @@ public:
Status executePlanStatus,
boost::optional<PlanExplainer::PlanStatsDetails> winningPlanTrialStats,
BSONObj extraInfo,
+ const BSONObj& command,
BSONObjBuilder* out);
/**
@@ -108,10 +113,13 @@ public:
* If 'verbosity' >= 'kExecStats' the 'executePipeline' flag is used to indicate whether the
* pipeline needs to be executed first, before the stats is collected. Otherwise, it is assumed
* that the plan was already executed until EOF and the stats are ready for collection.
+ *
+ * The 'command' parameter represents the command object that is being explained.
*/
static void explainPipeline(PlanExecutor* exec,
bool executePipeline,
ExplainOptions::Verbosity verbosity,
+ const BSONObj& command,
BSONObjBuilder* out);
/**
diff --git a/src/mongo/db/query/explain_common.cpp b/src/mongo/db/query/explain_common.cpp
index 01242acf6fd..fd0b918bb69 100644
--- a/src/mongo/db/query/explain_common.cpp
+++ b/src/mongo/db/query/explain_common.cpp
@@ -47,4 +47,24 @@ void generateServerInfo(BSONObjBuilder* out) {
serverBob.doneFast();
}
+bool appendIfRoom(const BSONObj& toAppend, StringData fieldName, BSONObjBuilder* out) {
+ if ((out->len() + toAppend.objsize()) < BSONObjMaxUserSize) {
+ out->append(fieldName, toAppend);
+ return true;
+ }
+
+ // The reserved buffer size for the warning message if 'out' exceeds the max BSON user size.
+ const int warningMsgSize = fieldName.size() + 60;
+
+ // Unless 'out' has already exceeded the max BSON user size, add a warning indicating
+ // that data has been truncated.
+ if (out->len() < BSONObjMaxUserSize - warningMsgSize) {
+ out->append("warning",
+ str::stream() << "'" << fieldName << "'"
+ << " has been omitted due to BSON size limit");
+ }
+
+ return false;
+}
+
} // namespace mongo::explain_common
diff --git a/src/mongo/db/query/explain_common.h b/src/mongo/db/query/explain_common.h
index d9b035beb8f..bc5637cebd1 100644
--- a/src/mongo/db/query/explain_common.h
+++ b/src/mongo/db/query/explain_common.h
@@ -43,4 +43,10 @@ namespace mongo::explain_common {
*/
void generateServerInfo(BSONObjBuilder* out);
+/**
+ * Conditionally appends a BSONObj to 'bob' depending on whether or not the maximum user size for a
+ * BSON object will be exceeded.
+ */
+bool appendIfRoom(const BSONObj& toAppend, StringData fieldName, BSONObjBuilder* out);
+
} // namespace mongo::explain_common
diff --git a/src/mongo/db/query/find.cpp b/src/mongo/db/query/find.cpp
index f344eb7cfca..36bf3c974ed 100644
--- a/src/mongo/db/query/find.cpp
+++ b/src/mongo/db/query/find.cpp
@@ -634,8 +634,12 @@ bool runQuery(OperationContext* opCtx,
bb.skip(sizeof(QueryResult::Value));
BSONObjBuilder explainBob;
- Explain::explainStages(
- exec.get(), collection.getCollection(), verbosity, BSONObj(), &explainBob);
+ Explain::explainStages(exec.get(),
+ collection.getCollection(),
+ verbosity,
+ BSONObj(),
+ upconvertedQuery,
+ &explainBob);
// Add the resulting object to the return buffer.
BSONObj explainObj = explainBob.obj();
diff --git a/src/mongo/dbtests/query_stage_multiplan.cpp b/src/mongo/dbtests/query_stage_multiplan.cpp
index 2ac93c0e04d..3275bcaed17 100644
--- a/src/mongo/dbtests/query_stage_multiplan.cpp
+++ b/src/mongo/dbtests/query_stage_multiplan.cpp
@@ -511,8 +511,12 @@ TEST_F(QueryStageMultiPlanTest, MPSExplainAllPlans) {
ASSERT_EQ(root->bestPlanIdx(), 0);
BSONObjBuilder bob;
- Explain::explainStages(
- exec.get(), ctx.getCollection(), ExplainOptions::Verbosity::kExecAllPlans, BSONObj(), &bob);
+ Explain::explainStages(exec.get(),
+ ctx.getCollection(),
+ ExplainOptions::Verbosity::kExecAllPlans,
+ BSONObj(),
+ BSONObj(),
+ &bob);
BSONObj explained = bob.done();
ASSERT_EQ(explained["executionStats"]["nReturned"].Int(), nDocs);
diff --git a/src/mongo/s/commands/cluster_count_cmd.cpp b/src/mongo/s/commands/cluster_count_cmd.cpp
index 00e186d4d05..fc8c7495194 100644
--- a/src/mongo/s/commands/cluster_count_cmd.cpp
+++ b/src/mongo/s/commands/cluster_count_cmd.cpp
@@ -264,7 +264,7 @@ public:
auto bodyBuilder = result->getBodyBuilder();
return ClusterExplain::buildExplainResult(
- opCtx, shardResponses, mongosStageName, millisElapsed, &bodyBuilder);
+ opCtx, shardResponses, mongosStageName, millisElapsed, cmdObj, &bodyBuilder);
}
private:
diff --git a/src/mongo/s/commands/cluster_distinct_cmd.cpp b/src/mongo/s/commands/cluster_distinct_cmd.cpp
index 9f836644ff5..41475a8cec2 100644
--- a/src/mongo/s/commands/cluster_distinct_cmd.cpp
+++ b/src/mongo/s/commands/cluster_distinct_cmd.cpp
@@ -156,7 +156,7 @@ public:
auto bodyBuilder = result->getBodyBuilder();
return ClusterExplain::buildExplainResult(
- opCtx, shardResponses, mongosStageName, millisElapsed, &bodyBuilder);
+ opCtx, shardResponses, mongosStageName, millisElapsed, cmdObj, &bodyBuilder);
}
bool run(OperationContext* opCtx,
diff --git a/src/mongo/s/commands/cluster_explain.cpp b/src/mongo/s/commands/cluster_explain.cpp
index 356f6c7c7a1..8c4cdb5e699 100644
--- a/src/mongo/s/commands/cluster_explain.cpp
+++ b/src/mongo/s/commands/cluster_explain.cpp
@@ -327,6 +327,7 @@ Status ClusterExplain::buildExplainResult(
const vector<AsyncRequestsSender::Response>& shardResponses,
const char* mongosStageName,
long long millisElapsed,
+ const BSONObj& command,
BSONObjBuilder* out) {
// Explain only succeeds if all shards support the explain command.
try {
@@ -338,6 +339,7 @@ Status ClusterExplain::buildExplainResult(
buildPlannerInfo(opCtx, shardResponses, mongosStageName, out);
buildExecStats(shardResponses, mongosStageName, millisElapsed, out);
explain_common::generateServerInfo(out);
+ appendIfRoom(out, command, "command");
return Status::OK();
}
diff --git a/src/mongo/s/commands/cluster_explain.h b/src/mongo/s/commands/cluster_explain.h
index 23e7f799194..60570c855d3 100644
--- a/src/mongo/s/commands/cluster_explain.h
+++ b/src/mongo/s/commands/cluster_explain.h
@@ -69,6 +69,7 @@ public:
const std::vector<AsyncRequestsSender::Response>& shardResponses,
const char* mongosStageName,
long long millisElapsed,
+ const BSONObj& command,
BSONObjBuilder* out);
diff --git a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp
index 0dba2c8ba40..2bfbb451bfd 100644
--- a/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp
+++ b/src/mongo/s/commands/cluster_find_and_modify_cmd.cpp
@@ -264,8 +264,12 @@ public:
shard->getId(), response, shard->getConnString().getServers().front()};
auto bodyBuilder = result->getBodyBuilder();
- return ClusterExplain::buildExplainResult(
- opCtx, {arsResponse}, ClusterExplain::kSingleShard, millisElapsed, &bodyBuilder);
+ return ClusterExplain::buildExplainResult(opCtx,
+ {arsResponse},
+ ClusterExplain::kSingleShard,
+ millisElapsed,
+ cmdObj,
+ &bodyBuilder);
}
bool run(OperationContext* opCtx,
diff --git a/src/mongo/s/commands/cluster_find_cmd.cpp b/src/mongo/s/commands/cluster_find_cmd.cpp
index 3bd8ff6c7c3..c1fc600c88e 100644
--- a/src/mongo/s/commands/cluster_find_cmd.cpp
+++ b/src/mongo/s/commands/cluster_find_cmd.cpp
@@ -179,8 +179,12 @@ public:
ClusterExplain::getStageNameForReadOp(shardResponses.size(), _request.body);
auto bodyBuilder = result->getBodyBuilder();
- uassertStatusOK(ClusterExplain::buildExplainResult(
- opCtx, shardResponses, mongosStageName, millisElapsed, &bodyBuilder));
+ uassertStatusOK(ClusterExplain::buildExplainResult(opCtx,
+ shardResponses,
+ mongosStageName,
+ millisElapsed,
+ _request.body,
+ &bodyBuilder));
} catch (const ExceptionFor<ErrorCodes::CommandOnShardedViewNotSupportedOnMongod>& ex) {
auto bodyBuilder = result->getBodyBuilder();
diff --git a/src/mongo/s/commands/cluster_write_cmd.cpp b/src/mongo/s/commands/cluster_write_cmd.cpp
index 1340e023275..48d1d36d206 100644
--- a/src/mongo/s/commands/cluster_write_cmd.cpp
+++ b/src/mongo/s/commands/cluster_write_cmd.cpp
@@ -596,8 +596,12 @@ private:
_commandOpWrite(
opCtx, _batchedRequest.getNS(), explainCmd, targetingBatchItem, &shardResponses);
auto bodyBuilder = result->getBodyBuilder();
- uassertStatusOK(ClusterExplain::buildExplainResult(
- opCtx, shardResponses, ClusterExplain::kWriteOnShards, timer.millis(), &bodyBuilder));
+ uassertStatusOK(ClusterExplain::buildExplainResult(opCtx,
+ shardResponses,
+ ClusterExplain::kWriteOnShards,
+ timer.millis(),
+ _request->body,
+ &bodyBuilder));
}
NamespaceString ns() const override {
diff --git a/src/mongo/s/commands/strategy.cpp b/src/mongo/s/commands/strategy.cpp
index 12559d00c9f..44a5f0ad044 100644
--- a/src/mongo/s/commands/strategy.cpp
+++ b/src/mongo/s/commands/strategy.cpp
@@ -1339,6 +1339,6 @@ void Strategy::explainFind(OperationContext* opCtx,
ClusterExplain::getStageNameForReadOp(shardResponses.size(), findCommand);
uassertStatusOK(ClusterExplain::buildExplainResult(
- opCtx, shardResponses, mongosStageName, millisElapsed, out));
+ opCtx, shardResponses, mongosStageName, millisElapsed, findCommand, out));
}
} // namespace mongo
diff --git a/src/mongo/s/query/cluster_aggregate.cpp b/src/mongo/s/query/cluster_aggregate.cpp
index 4b2da5f2b34..e99c6b70ad5 100644
--- a/src/mongo/s/query/cluster_aggregate.cpp
+++ b/src/mongo/s/query/cluster_aggregate.cpp
@@ -352,6 +352,12 @@ Status ClusterAggregate::runAggregate(OperationContext* opCtx,
updateHostsTargetedMetrics(opCtx, namespaces.executionNss, cm, involvedNamespaces);
// Report usage statistics for each stage in the pipeline.
liteParsedPipeline.tickGlobalStageCounters();
+
+ // Add 'command' object to explain output.
+ if (expCtx->explain) {
+ explain_common::appendIfRoom(
+ request.serializeToCommandObj().toBson(), "command", result);
+ }
}
return status;
}