mirror of https://github.com/mongodb/mongo
127 lines
5.1 KiB
JavaScript
127 lines
5.1 KiB
JavaScript
// SERVER-726
|
|
// This test makes assertions about how many keys are examined during query execution, which can
|
|
// change depending on whether/how many documents are filtered out by the SHARDING_FILTER stage.
|
|
// @tags: [
|
|
// assumes_unsharded_collection,
|
|
// no_selinux,
|
|
// # Different assertions are made depending on whether SBE or classic is used. Implicitly
|
|
// # creating an index can change which engine is used.
|
|
// assumes_no_implicit_index_creation,
|
|
// # This test assumes that either SBE or classic is fully enabled and that we're not running in
|
|
// # a mixed version cluster.
|
|
// requires_fcv_63,
|
|
// # During fcv upgrade/downgrade the engine might not be what we expect.
|
|
// cannot_run_during_upgrade_downgrade,
|
|
// ]
|
|
|
|
import {getPlanStages} from "jstests/libs/query/analyze_plan.js";
|
|
import {checkSbeFullyEnabled} from "jstests/libs/query/sbe_util.js";
|
|
|
|
const t = db[jsTestName()];
|
|
t.drop();
|
|
|
|
class ExplainWithKeysExamined {
|
|
constructor(explain, keysExamined) {
|
|
this.explain = explain;
|
|
this.keysExamined = keysExamined;
|
|
}
|
|
}
|
|
|
|
function keysExamined(query, hint, sort) {
|
|
if (!hint) {
|
|
hint = {};
|
|
}
|
|
if (!sort) {
|
|
sort = {};
|
|
}
|
|
const explain = t.find(query).sort(sort).hint(hint).explain("executionStats");
|
|
return new ExplainWithKeysExamined(explain, explain.executionStats.totalKeysExamined);
|
|
}
|
|
|
|
assert.commandWorked(t.createIndex({a: 1}));
|
|
assert.commandWorked(t.insert({a: 5}));
|
|
assert.eq(0, keysExamined({a: {$gt: 4, $lt: 5}}).keysExamined, "A");
|
|
|
|
assert(t.drop());
|
|
assert.commandWorked(t.createIndex({a: 1}));
|
|
assert.commandWorked(t.insert({a: 4}));
|
|
assert.eq(0, keysExamined({a: {$gt: 4, $lt: 5}}).keysExamined, "B");
|
|
|
|
assert.commandWorked(t.insert({a: 5}));
|
|
assert.eq(0, keysExamined({a: {$gt: 4, $lt: 5}}).keysExamined, "D");
|
|
|
|
assert.commandWorked(t.insert({a: 4}));
|
|
assert.eq(0, keysExamined({a: {$gt: 4, $lt: 5}}).keysExamined, "C");
|
|
|
|
assert.commandWorked(t.insert({a: 5}));
|
|
assert.eq(0, keysExamined({a: {$gt: 4, $lt: 5}}).keysExamined, "D");
|
|
|
|
assert(t.drop());
|
|
assert.commandWorked(t.createIndex({a: 1, b: 1}));
|
|
assert.commandWorked(t.insert({a: 1, b: 1}));
|
|
assert.commandWorked(t.insert({a: 1, b: 2}));
|
|
assert.commandWorked(t.insert({a: 2, b: 1}));
|
|
assert.commandWorked(t.insert({a: 2, b: 2}));
|
|
|
|
// We make different assertions about the number of keys examined depending on whether we are using
|
|
// SBE or the classic engine. This is because the classic engine will use a multi-interval index
|
|
// scan whereas SBE will decompose the intervals into a set of single-interval bounds and will end
|
|
// up examining 0 keys.
|
|
const isSBEEnabled = checkSbeFullyEnabled(db);
|
|
let expectedKeys = isSBEEnabled ? 0 : 3;
|
|
let errMsg = function (explainWithKeysExamined) {
|
|
// Check if sbe is enabled instead of using the cached value.
|
|
return (
|
|
"Chosen plan examined " +
|
|
explainWithKeysExamined.keysExamined +
|
|
" keys. isSBEEnabled: " +
|
|
checkSbeFullyEnabled(db) +
|
|
". explain: " +
|
|
explainWithKeysExamined.explain
|
|
);
|
|
};
|
|
let keysExaminedRet = keysExamined({a: {$in: [1, 2]}, b: {$gt: 1, $lt: 2}}, {a: 1, b: 1});
|
|
assert.eq(keysExaminedRet.keysExamined, expectedKeys, errMsg(keysExaminedRet));
|
|
|
|
keysExaminedRet = keysExamined({a: {$in: [1, 2]}, b: {$gt: 1, $lt: 2}}, {a: 1, b: 1}, {a: -1, b: -1});
|
|
assert.eq(keysExaminedRet.keysExamined, expectedKeys, errMsg(keysExaminedRet));
|
|
|
|
assert.commandWorked(t.insert({a: 1, b: 1}));
|
|
assert.commandWorked(t.insert({a: 1, b: 1}));
|
|
keysExaminedRet = keysExamined({a: {$in: [1, 2]}, b: {$gt: 1, $lt: 2}}, {a: 1, b: 1});
|
|
assert.eq(keysExaminedRet.keysExamined, expectedKeys, errMsg(keysExaminedRet));
|
|
|
|
keysExaminedRet = keysExamined({a: {$in: [1, 2]}, b: {$gt: 1, $lt: 2}}, {a: 1, b: 1});
|
|
assert.eq(keysExaminedRet.keysExamined, expectedKeys, errMsg(keysExaminedRet));
|
|
|
|
keysExaminedRet = keysExamined({a: {$in: [1, 2]}, b: {$gt: 1, $lt: 2}}, {a: 1, b: 1}, {a: -1, b: -1});
|
|
assert.eq(keysExaminedRet.keysExamined, expectedKeys, errMsg(keysExaminedRet));
|
|
|
|
// We examine one less key in the classic engine because the bounds are slightly tighter.
|
|
if (!isSBEEnabled) {
|
|
expectedKeys = 2;
|
|
}
|
|
|
|
keysExaminedRet = keysExamined({a: {$in: [1, 1.9]}, b: {$gt: 1, $lt: 2}}, {a: 1, b: 1});
|
|
assert.eq(keysExaminedRet.keysExamined, expectedKeys, errMsg(keysExaminedRet));
|
|
|
|
keysExaminedRet = keysExamined({a: {$in: [1.1, 2]}, b: {$gt: 1, $lt: 2}}, {a: 1, b: 1}, {a: -1, b: -1});
|
|
assert.eq(keysExaminedRet.keysExamined, expectedKeys, errMsg(keysExaminedRet));
|
|
assert.commandWorked(t.insert({a: 1, b: 1.5}));
|
|
|
|
// We examine one extra key in both engines because we've inserted a document that falls within
|
|
// both sets of bounds being scanned.
|
|
expectedKeys = isSBEEnabled ? 1 : 4;
|
|
keysExaminedRet = keysExamined({a: {$in: [1, 2]}, b: {$gt: 1, $lt: 2}}, {a: 1, b: 1});
|
|
assert.eq(keysExaminedRet.keysExamined, expectedKeys, errMsg(keysExaminedRet));
|
|
|
|
if (isSBEEnabled) {
|
|
const explain = t
|
|
.find({a: {$gte: 1, $lt: 3}, b: {$gte: 1, $lt: 3}})
|
|
.hint({a: 1, b: 1})
|
|
.explain("executionStats");
|
|
const stage = getPlanStages(explain.executionStats.executionStages, "ixscan_generic");
|
|
assert.eq(1, stage.length, explain);
|
|
assert.eq(5, stage[0].keyCheckSkipped, stage);
|
|
}
|