Files
mongo/jstests/aggregation/explain_limit.js
David Storch d1a128b434 SERVER-36723 Push $limit beneath DocumentSourceCursor into the PlanStage layer.
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.
2019-10-10 22:16:39 +00:00

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"});
})();