Files
mongo/jstests/core/index/partial_index_optimization.js
Ben Shteinfeld 612485d8ad SERVER-102825 Fix false negative in isSubsetOf() when RHS is disjunction (#34366)
GitOrigin-RevId: f067482c9275104faca8cb6880e930cf97d767e1
2025-04-10 05:45:22 +00:00

222 lines
7.1 KiB
JavaScript

/**
* Tests for classic query optimization with partial indices: do not generate fetch stage in the
* plan if a query predicate is satisfied by the filter expression of the chosen partial index. If
* the fetch phase is needed for another reason, make sure that the predicate is not in the fetch
* filter.
*
* @tags: [
* # the test conflicts with hidden wildcard indexes
* assumes_no_implicit_index_creation,
* does_not_support_stepdowns,
* multiversion_incompatible,
* requires_fcv_70,
* ]
*/
import {
assertFetchFilter,
assertNoFetchFilter,
assertStagesForExplainOfCommand,
getWinningPlanFromExplain,
isIxscan,
} from "jstests/libs/query/analyze_plan.js";
function flagVal(n) {
return (n % 5 > 3) ? true : false;
}
function stateVal(n) {
const states = ["open", "closed", "unknown"];
return states[n % 3];
}
function getDocs(len, start = 0) {
return Array.from({length: len}, (_, i) => ({
_id: start + i,
a: i,
b: i + 3,
c: [i, i + 5],
flag: flagVal(i),
state: stateVal(i),
array: [{a: i, state: stateVal(i)}, {b: i}]
}));
}
const coll = db.partial_index_opt;
coll.drop();
assert.commandWorked(coll.insertMany(getDocs(100)));
assert.commandWorked(coll.insertMany([
{
_id: 100,
a: 100,
state: "open",
array: [{a: 100, state: "closed"}, {a: 101, state: "closed"}]
},
{_id: 101, a: 101, state: "open", array: [{a: 101, state: "open"}]},
{_id: 102, a: 102, state: "closed", array: [{a: 102, state: "open"}]}
]));
const expectedStagesCount = ["COUNT", "COUNT_SCAN"];
assert.commandWorked(coll.createIndex({a: 1}, {"partialFilterExpression": {flag: true}}));
let cmdObj = {find: coll.getName(), filter: {flag: true, a: 4}, projection: {_id: 0, a: 1}};
assertNoFetchFilter({coll: coll, cmdObj: cmdObj});
// The following plan has a fetch phase because of the projection, but no filter on it.
cmdObj = {
find: coll.getName(),
filter: {flag: true, a: 4},
projection: {a: 1}
};
assertNoFetchFilter({coll: coll, cmdObj: cmdObj});
// Count command.
cmdObj = {
count: coll.getName(),
query: {flag: true, a: 4}
};
assertStagesForExplainOfCommand({
coll: coll,
cmdObj: cmdObj,
expectedStages: expectedStagesCount,
stagesNotExpected: ["FETCH"]
});
// Partial index with filter expression with conjunction.
assert.commandWorked(coll.createIndex(
{a: 1}, {name: "a_1_range", "partialFilterExpression": {a: {$gte: 20, $lte: 40}}}));
cmdObj = {
find: coll.getName(),
filter: {a: {$gte: 20, $lte: 40}},
projection: {_id: 0, a: 1}
};
assertNoFetchFilter({coll: coll, cmdObj: cmdObj});
cmdObj = {
find: coll.getName(),
filter: {a: {$gte: 25, $lte: 30}},
projection: {_id: 0, a: 1}
};
assertNoFetchFilter({coll: coll, cmdObj: cmdObj});
// Partial index with compound key.
assert.commandWorked(coll.createIndex({a: 1, b: 1}, {"partialFilterExpression": {flag: true}}));
cmdObj = {
find: coll.getName(),
filter: {a: {$gte: 50}, b: {$in: [55, 57, 59, 62]}, flag: true},
projection: {_id: 0, a: 1, b: 1}
};
assertNoFetchFilter({coll: coll, cmdObj: cmdObj});
// Filter expression with conjunction on multiple fields.
assert.commandWorked(coll.createIndex(
{b: 1}, {name: "b_1_state_open", "partialFilterExpression": {state: "open", b: {$gt: 50}}}));
cmdObj = {
find: coll.getName(),
filter: {state: "open", b: {$gt: 80}},
projection: {_id: 0, b: 1}
};
assertNoFetchFilter({coll: coll, cmdObj: cmdObj});
cmdObj = {
count: coll.getName(),
query: {state: "open", b: {$gt: 50}}
};
assertStagesForExplainOfCommand({
coll: coll,
cmdObj: cmdObj,
expectedStages: expectedStagesCount,
stagesNotExpected: ["FETCH"]
});
// Index filter expression with $exists.
assert.commandWorked(coll.createIndex(
{a: 1}, {name: "a_1_b_exists", "partialFilterExpression": {b: {$exists: true}}}));
cmdObj = {
find: coll.getName(),
filter: {a: {$gte: 90}, b: {$exists: true}},
projection: {_id: 0, a: 1}
};
assertNoFetchFilter({coll: coll, cmdObj: cmdObj});
// Filter expression in a multi-key index.
assert.commandWorked(
coll.createIndex({c: 1}, {name: "c_1_a", partialFilterExpression: {a: {$lte: 30}}}));
cmdObj = {
count: coll.getName(),
query: {c: {$lte: 50}, a: {$lte: 30}}
};
assertStagesForExplainOfCommand({
coll: coll,
cmdObj: cmdObj,
expectedStages: expectedStagesCount,
stagesNotExpected: ["FETCH"]
});
// The following plan has a fetch phase, but no filter on 'a'.
cmdObj = {
find: coll.getName(),
filter: {c: {$lte: 50}, a: {$lte: 30}},
projection: {_id: 0, c: 1}
};
assertNoFetchFilter({coll: coll, cmdObj: cmdObj});
// Test that the same filter expression under $elemMatch will not be removed from the fetch filter.
assert.commandWorked(
coll.createIndex({a: 1}, {name: "a_1_state_open", "partialFilterExpression": {state: "open"}}));
let predicate = {
a: {$gte: 100},
state: "open",
array: {$elemMatch: {$and: [{a: {$gte: 100}}, {state: "open"}]}}
};
let fetchFilter = {
"array": {"$elemMatch": {"$and": [{"a": {"$gte": 100}}, {"state": {"$eq": "open"}}]}}
};
assertFetchFilter({coll: coll, predicate: predicate, expectedFilter: fetchFilter, nReturned: 1});
// Index on $elemMatch predicate. Test that the index filter predicate is removed from the fetch
// filter while $elemMatch predicate is preserved.
assert.commandWorked(coll.createIndex(
{"array.a": 1}, {name: "array_a_1_state_open", "partialFilterExpression": {state: "open"}}));
predicate = {
state: "open",
array: {$elemMatch: {$and: [{a: {$gte: 100}}, {state: "open"}]}}
};
fetchFilter = {
"array": {"$elemMatch": {"$and": [{"a": {"$gte": 100}}, {"state": {"$eq": "open"}}]}}
};
assertFetchFilter({coll: coll, predicate: predicate, expectedFilter: fetchFilter, nReturned: 1});
// Test for index filter expression over nested field.
assert.commandWorked(coll.createIndex(
{"array.a": 1},
{name: "array_a_1_array_state_open", "partialFilterExpression": {"array.state": "open"}}));
cmdObj = {
find: coll.getName(),
filter: {$and: [{"array.a": {$gte: 100}}, {"array.state": "open"}]},
projection: {_id: 0, array: 1}
};
assertNoFetchFilter({coll: coll, cmdObj: cmdObj});
// Tests that the query predicate is not removed if it is a subset of an $or index filter.
assert.commandWorked(coll.createIndex(
{a: 1}, {name: "a_1_or", "partialFilterExpression": {$or: [{b: {$gte: 80}}, {flag: "true"}]}}));
predicate = {
$and: [{a: {$gte: 75}}, {b: {$gte: 80}}]
};
fetchFilter = {
"b": {"$gte": 80}
};
assertFetchFilter({coll: coll, predicate: predicate, expectedFilter: fetchFilter, nReturned: 23});
const exp =
coll.find({$and: [{a: {$gte: 90}}, {$or: [{b: {$gte: 80}}, {flag: "true"}]}]}).explain();
assert(isIxscan(db, getWinningPlanFromExplain(exp)), "Expected index scan, got " + tojson(exp));