mongo/jstests/core/query/boolean_expression_simplification.js
Joshua Siegel 169f8dc283 SERVER-111817 Add tag to exclude tests from timeseries CRUD suite (#44113)
GitOrigin-RevId: a3ab3b89275fa89c9824f6af467d56d605096159
2025-11-21 18:08:16 +00:00

130 lines
4.7 KiB
JavaScript

/**
* Tests boolean expression simplifier produces expected results.
* @tags: [
* requires_fcv_72,
* # explain command, used by the test, does not support majority read concern.
* assumes_read_concern_local,
* # Explain will return different plan than expected when a collection becomes a time-series
* # collection. Also, query shape will be different.
* exclude_from_timeseries_crud_passthrough,
* ]
*/
import {getPlanStages, getWinningPlanFromExplain} from "jstests/libs/query/analyze_plan.js";
const parameterName = "internalQueryEnableBooleanExpressionsSimplifier";
const isSimplifierEnabled = assert.commandWorked(db.adminCommand({getParameter: 1, [parameterName]: 1}))[parameterName];
if (!isSimplifierEnabled) {
jsTest.log("Skipping the Boolean simplifier tests, since the simplifier is disabled...");
quit();
}
/**
* Checks possible representations of an empty filter in query plans, which can be empty object '{}'
* or missing value.
*/
function isEmptyFilter(filter) {
// null or undefined
if (filter === null || filter === undefined) {
return true;
}
if (Object.keys(filter).length === 0) {
return true;
}
return false;
}
/**
* Checks that the given 'filter' expression is simplified to the 'simplifiedFilter' expression in
* the generated COLLSCAN plan.
*/
function testSimplifierForCollscan(coll, filter, simplifiedFilter) {
const explain = assert.commandWorked(coll.find(filter).explain());
const winningPlan = getWinningPlanFromExplain(explain);
const collScans = getPlanStages(winningPlan, "COLLSCAN");
assert.neq(0, collScans.length, winningPlan);
for (const collScan of collScans) {
const emptyFilters = isEmptyFilter(collScan.filter) && isEmptyFilter(simplifiedFilter);
if (!emptyFilters) {
assert.docEq(simplifiedFilter, collScan.filter, collScan);
}
}
}
db.coll.drop();
assert.commandWorked(
db.coll.insertMany([
{a: 1, b: 1},
{a: 1, b: 2},
{a: 2, b: 1},
{a: 2, b: 2},
]),
);
const testCases = [
// a == 1 and a != 1 is simplified to AlwaysFalse
[{"$or": [{"$and": [{a: 1}, {a: {"$ne": 1}}]}, {b: 2}]}, {b: {"$eq": 2}}],
// $elemMatch expression is not mixed with other expressions
[{$and: [{c: 1}, {c: {$elemMatch: {$ne: 1}}}]}, {$and: [{c: {$elemMatch: {$not: {$eq: 1}}}}, {c: {$eq: 1}}]}],
// nested $elemMatch
[
{d: {$elemMatch: {e: {$elemMatch: {f: {$elemMatch: {$eq: 11}}}}}}},
{d: {$elemMatch: {e: {$elemMatch: {f: {$elemMatch: {$eq: 11}}}}}}},
],
// CNF -> DNF: we don't simplify an expression if the resulting one is bigger then the original
// one
[
{
$and: [{$or: [{a: {$eq: 1}}, {b: {$eq: 1}}]}, {$or: [{c: {$eq: 2}}, {d: {$eq: 2}}]}],
},
{
$and: [{$or: [{a: {$eq: 1}}, {b: {$eq: 1}}]}, {$or: [{c: {$eq: 2}}, {d: {$eq: 2}}]}],
},
],
// Show that we have achieve optimization originally requested in SERVER-31360.
[{$or: [{}, {}]}, {}],
// Show that we have achieve optimization originally requested in SERVER-31360.
[{$or: [{}]}, {}],
// Show that we have achieve optimization for the first query originally requested in
// SERVER-22857.
[{$and: [{"a": 1}, {"a": 1}]}, {a: {$eq: 1}}],
// Inside $elemMatch $not is not expanded
[{a: {$elemMatch: {$not: {$gt: 5, $lt: 8}, $gt: 4}}}, {a: {$elemMatch: {$not: {$gt: 5, $lt: 8}, $gt: 4}}}],
// TODO SERVER-81788: we don't optimize $elemMatch yet as originally requested in the second
// example of SERVER-22857.
[
{
$and: [{a: {$elemMatch: {$and: [{b: {$eq: 9}}, {id: {$eq: 1}}]}}}, {a: {$eq: {id: {$eq: 1}}}}],
},
{
$and: [{a: {$elemMatch: {$and: [{b: {$eq: 9}}, {id: {$eq: 1}}]}}}, {a: {$eq: {id: {$eq: 1}}}}],
},
],
// TODO SERVER-81792: we don't optimize $exists yet as originally requested in SERVER-35018.
[{a: {$eq: 1, $exists: true}}, {$and: [{a: {$eq: 1}}, {a: {$exists: true}}]}],
// TODO SERVER-81792: we don't optimize $ne yet as originally requested in SERVER-35018.
[{a: {$eq: 1, $ne: null}}, {$and: [{a: {$eq: 1}}, {a: {$not: {$eq: null}}}]}],
// Simplify off redundant $or.
[
{
"$and": [
{b: 0},
{
"$or": [
{a: 0},
{
"$and": [{c: {"$in": [34, 45]}}, {c: {"$nin": [34, 45]}}],
},
],
},
],
},
{$and: [{a: {$eq: 0}}, {b: {$eq: 0}}]},
],
];
for (const [filter, simplifiedFilter] of testCases) {
testSimplifierForCollscan(db.coll, filter, simplifiedFilter);
}