mirror of https://github.com/mongodb/mongo
73 lines
2.6 KiB
JavaScript
73 lines
2.6 KiB
JavaScript
/**
|
|
* Pushing down computed projection before sort+limit can cause the projection to be evaluated on
|
|
* invalid documents and throw exceptions. This test checks that we do not push computed projection
|
|
* before sort+limit, preventing valid queries from failing.
|
|
*
|
|
* This test was intended to reproduce a bug, SERVER-54128.
|
|
*/
|
|
const coll = db.expression_swap_limit;
|
|
coll.drop();
|
|
|
|
const NUM_INVALID_DOCS = 10;
|
|
|
|
const docs = Array.from({length: NUM_INVALID_DOCS}, (_, i) => ({_id: i, a: ""}));
|
|
docs.push({_id: 99, a: 123});
|
|
coll.insert(docs);
|
|
|
|
coll.createIndex({some_other_field: 1});
|
|
|
|
const predicate = {
|
|
$or: [
|
|
// One branch of the $or needs to have more than one relevant index.
|
|
{_id: {$lt: 9999}, some_other_field: {$ne: 3}},
|
|
// The other branch doesn't matter: it's only here to prevent the $or being
|
|
// optimized out.
|
|
{this_predicate_matches_nothing: true},
|
|
],
|
|
};
|
|
const sortSpec = {
|
|
_id: -1,
|
|
};
|
|
const oppositeSortSpec = {
|
|
_id: +1,
|
|
};
|
|
const projection = {
|
|
_id: 1,
|
|
b: {$round: "$a"},
|
|
};
|
|
const pipeline1 = [
|
|
// We need a rooted $or to trigger the SubplanStage.
|
|
// Instead of one MultiPlanStage for the whole query, we get one
|
|
// per branch of the $or.
|
|
{$match: predicate},
|
|
// Next we need a $sort+$limit. These two stages unambiguously select _id: 99.
|
|
// From the user's point of view, the $limit returns a bag of 1 document.
|
|
{$sort: sortSpec},
|
|
{$limit: 1},
|
|
// This projection raises an error if we evaluate it on any document other than
|
|
// _id: 99, because that is the only document where 'a' is numeric.
|
|
{$project: projection},
|
|
];
|
|
{
|
|
// The pipeline should succeed.
|
|
const aggResult = coll.aggregate(pipeline1).toArray();
|
|
assert.docEq([{_id: 99, b: 123}], aggResult);
|
|
|
|
// The pipeline should succeed without pushing down to find.
|
|
const noOptResult = coll.aggregate([{$_internalInhibitOptimization: {}}].concat(pipeline1)).toArray();
|
|
assert.docEq([{_id: 99, b: 123}], noOptResult);
|
|
}
|
|
|
|
// Similarly, we can select the 1 valid document by flipping the sort and skipping
|
|
// all but one document.
|
|
const pipeline2 = [{$match: predicate}, {$sort: oppositeSortSpec}, {$skip: NUM_INVALID_DOCS}, {$project: projection}];
|
|
{
|
|
// The pipeline should succeed.
|
|
const aggResult = coll.aggregate(pipeline2).toArray();
|
|
assert.docEq([{_id: 99, b: 123}], aggResult);
|
|
|
|
// The pipeline should succeed without pushing down to find.
|
|
const noOptResult = coll.aggregate([{$_internalInhibitOptimization: {}}].concat(pipeline2)).toArray();
|
|
assert.docEq([{_id: 99, b: 123}], noOptResult);
|
|
}
|