In addition towards working towards the general goal of doing as much query execution as possible with a PlanStage tree, this should have a positive performance impact for certain agg pipelines. Previously, a pipeline with a $project (or a $project-like stage such as $addFields) followed by a $limit might have applied this limit only after a full batch of data was loaded by DocumentSourceCursor. After this change, the limit will take effect prior to DocumentSourceCursor batching, and thus may reduce the amount of data processed by the query.
75 lines
2.9 KiB
JavaScript
75 lines
2.9 KiB
JavaScript
// Tests the behavior of explain() when used with the aggregation pipeline and limits.
|
|
// @tags: [do_not_wrap_aggregations_in_facets]
|
|
(function() {
|
|
"use strict";
|
|
|
|
load("jstests/libs/analyze_plan.js"); // For getAggPlanStages().
|
|
|
|
let coll = db.explain_limit;
|
|
|
|
const kCollSize = 105;
|
|
const kLimit = 10;
|
|
|
|
// Return whether or explain() was successful and contained the appropriate fields given the
|
|
// requested verbosity. Checks that the number of documents examined and returned are correct given
|
|
// the value of the limit.
|
|
function checkResults({results, verbosity}) {
|
|
let cursorSubdocs = getAggPlanStages(results, "LIMIT");
|
|
assert.gt(cursorSubdocs.length, 0);
|
|
for (let stageResult of cursorSubdocs) {
|
|
assert.eq(stageResult.limitAmount, NumberLong(kLimit), results);
|
|
if (verbosity !== "queryPlanner") {
|
|
assert.eq(stageResult.nReturned, NumberLong(kLimit), results);
|
|
}
|
|
}
|
|
|
|
// Explain should report that we only have to examine as many documents as the limit.
|
|
if (verbosity !== "queryPlanner") {
|
|
if (results.hasOwnProperty("executionStats")) {
|
|
assert.eq(results.executionStats.nReturned, kLimit, results);
|
|
assert.eq(results.executionStats.totalDocsExamined, kLimit, results);
|
|
} else {
|
|
// This must be output for a sharded explain. Verify that each shard reports the
|
|
// expected execution stats.
|
|
assert(results.hasOwnProperty("shards"));
|
|
for (let elem in results.shards) {
|
|
const shardExecStats = results.shards[elem].executionStats;
|
|
assert.eq(shardExecStats.nReturned, kLimit, results);
|
|
assert.eq(shardExecStats.totalDocsExamined, kLimit, results);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// explain() should respect limit.
|
|
coll.drop();
|
|
assert.commandWorked(coll.createIndex({a: 1}));
|
|
|
|
for (let i = 0; i < kCollSize; i++) {
|
|
assert.commandWorked(coll.insert({a: 1}));
|
|
}
|
|
|
|
const pipeline = [{$match: {a: 1}}, {$limit: kLimit}];
|
|
|
|
let plannerLevel = coll.explain("queryPlanner").aggregate(pipeline);
|
|
checkResults({results: plannerLevel, verbosity: "queryPlanner"});
|
|
|
|
let execLevel = coll.explain("executionStats").aggregate(pipeline);
|
|
checkResults({results: execLevel, verbosity: "executionStats"});
|
|
|
|
let allPlansExecLevel = coll.explain("allPlansExecution").aggregate(pipeline);
|
|
checkResults({results: allPlansExecLevel, verbosity: "allPlansExecution"});
|
|
|
|
// Create a second index so that more than one plan is available.
|
|
assert.commandWorked(coll.createIndex({a: 1, b: 1}));
|
|
|
|
plannerLevel = coll.explain("queryPlanner").aggregate(pipeline);
|
|
checkResults({results: plannerLevel, verbosity: "queryPlanner"});
|
|
|
|
execLevel = coll.explain("executionStats").aggregate(pipeline);
|
|
checkResults({results: execLevel, verbosity: "executionStats"});
|
|
|
|
allPlansExecLevel = coll.explain("allPlansExecution").aggregate(pipeline);
|
|
checkResults({results: allPlansExecLevel, verbosity: "allPlansExecution"});
|
|
})();
|