|
Our optimizer assumes that $setUnion is commutative when it's actually not in certain unimportant pathological cases. For example,
// {$setUnion: [[0], [NumberDecimal("-0")]]}
|
// produces: [0]
|
|
// On the other hand, {$setUnion: [[NumberDecimal("-0")], [0]]}
|
// produces: [NumbeDecimal("-0")]
|
Here is a repro that shows how the query behaves differently when optimizations are enabled vs disabled, written by @Justin Seyster.
(function() {
|
"use strict";
|
|
const coll = db.set_union_optimization;
|
coll.drop();
|
coll.insert({a: [-0]});
|
|
const pipeline = [
|
{$unwind: "$a"},
|
{
|
$project: {
|
_id: 0,
|
union: {
|
$setUnion: [
|
[
|
NumberDecimal("-0"),
|
],
|
["$a"]
|
]
|
}
|
}
|
},
|
// This stage is unnecessary but goes to show that a failure like this is technically possible
|
// even if we tune the fuzzer so that NumberDecimal("-0") === 0.
|
//{$addFields: {type: {$type: {$arrayElemAt: ["$union", 0]}}}}
|
];
|
|
const resultWithOptimizations = coll.aggregate(pipeline).toArray();
|
|
assert.commandWorked(
|
db.adminCommand({configureFailPoint: "disableMatchExpressionOptimization", mode: "alwaysOn"}));
|
assert.commandWorked(
|
db.adminCommand({configureFailPoint: "disablePipelineOptimization", mode: "alwaysOn"}));
|
coll.getPlanCache().clear();
|
|
const resultWithoutOptimizations = coll.aggregate(pipeline).toArray();
|
assert.sameMembers(resultWithOptimizations, resultWithoutOptimizations);
|
}());
|
The task of this ticket is to decide whether this behavior merits any server changes, or whether we should ignore it and continue to treat the expression as commutative.
|