Files
mongo/jstests/core/query/min_max_bounds.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

172 lines
5.9 KiB
JavaScript

/**
* This test ensures that queries using simple ranges handle bound inclusion properly.
* @tags: [
* assumes_balancer_off,
* requires_getmore,
* # Time series collections (as views) have specific limitations on aggregation stages (e.g. $merge, $out) or cursor options (e.g. noCursorTimeout, singleBatch).
* exclude_from_timeseries_crud_passthrough,
* ]
*/
import {resultsEq} from "jstests/aggregation/extras/utils.js";
import {FixtureHelpers} from "jstests/libs/fixture_helpers.js";
const collNamePrefix = "query_bound_inclusion_";
let collCount = 0;
let coll = db.getCollection(collNamePrefix + collCount++);
coll.drop();
assert.commandWorked(coll.insert({a: 1, b: 1}));
assert.commandWorked(coll.insert({a: 2, b: 2}));
assert.commandWorked(coll.insert({a: 3, b: 3}));
assert.commandWorked(coll.createIndex({a: 1}));
let res = coll.find().sort({a: 1}).toArray();
assert.eq(res.length, 3);
assert.eq(res[0].a, 1);
assert.eq(res[1].a, 2);
assert.eq(res[2].a, 3);
res = coll.find().sort({a: -1}).toArray();
assert.eq(res.length, 3);
assert.eq(res[0].a, 3);
assert.eq(res[1].a, 2);
assert.eq(res[2].a, 1);
res = coll.find().min({a: 1}).max({a: 3}).hint({a: 1}).toArray();
assert.eq(res.length, 2);
if (FixtureHelpers.numberOfShardsForCollection(coll) === 1) {
assert.eq(res[0].a, 1);
assert.eq(res[1].a, 2);
} else {
// With more than one shard, we cannot assume the results will come back in order, since we
// did not request a sort.
assert(
resultsEq(
res.map((result) => result.a),
[1, 2],
),
);
}
res = coll.find().min({a: 1}).max({a: 3}).sort({a: -1}).hint({a: 1}).toArray();
assert.eq(res.length, 2);
assert.eq(res[0].a, 2);
assert.eq(res[1].a, 1);
assert.commandWorked(coll.createIndex({b: -1}));
res = coll.find().sort({b: -1}).toArray();
assert.eq(res.length, 3);
assert.eq(res[0].b, 3);
assert.eq(res[1].b, 2);
assert.eq(res[2].b, 1);
res = coll.find().sort({b: 1}).toArray();
assert.eq(res.length, 3);
assert.eq(res[0].b, 1);
assert.eq(res[1].b, 2);
assert.eq(res[2].b, 3);
res = coll.find().min({b: 3}).max({b: 1}).hint({b: -1}).toArray();
assert.eq(res.length, 2);
if (FixtureHelpers.numberOfShardsForCollection(coll) === 1) {
assert.eq(res[0].b, 3);
assert.eq(res[1].b, 2);
} else {
// With more than one shard, we cannot assume the results will come back in order, since we
// did not request a sort.
assert(
resultsEq(
res.map((result) => result.b),
[3, 2],
),
);
}
res = coll.find().min({b: 3}).max({b: 1}).sort({b: 1}).hint({b: -1}).toArray();
assert.eq(res.length, 2);
assert.eq(res[0].b, 2);
assert.eq(res[1].b, 3);
// Bug SERVER-54552.
const testBoundsInclusivity = (indexDirection) => {
coll = db.getCollection(collNamePrefix + collCount++);
coll.drop();
assert.commandWorked(coll.createIndex({i: indexDirection}));
assert.commandWorked(coll.insert([{i: "a"}, {i: "b"}, {i: "c"}, {i: "d"}, {i: "d"}, {i: "e"}]));
// Test for all the posible ranges.
[
["$gte", "$lte", 4],
["$gt", "$lte", 3],
["$gte", "$lt", 2],
["$gt", "$lt", 1],
].forEach(([gt, lt, count]) => {
let query = {i: {}};
query["i"][gt] = "b";
query["i"][lt] = "d";
assert.eq(count, coll.find(query).count(), {query, indexDirection});
// Test with forward and backward sorts.
assert.eq(count, coll.find(query).sort({i: indexDirection}).count(), {query, sort: 1, indexDirection});
assert.eq(count, coll.find(query).sort({i: -1}).count(), {query, sort: -1, indexDirection});
});
};
testBoundsInclusivity(1);
testBoundsInclusivity(-1);
// Tests inclusivity correctness with all sort directions on an index with a given direction
// for range queries that can use a 2 property compound index.
const testBoundsInclusivityCompound = (lIndexDirection, nIndexDirection) => {
coll = db.getCollection(collNamePrefix + collCount++);
coll.drop();
assert.commandWorked(coll.createIndex({l: lIndexDirection, n: nIndexDirection}));
let docs = [];
["a", "b", "c", "d", "e"].forEach((l) => [1, 2, 3, 4, 5].forEach((n) => docs.push({l, n})));
assert.commandWorked(coll.insert(docs));
// The eq variants of the operators add one more document to the result.
const opExtra = {"$lt": 0, "$lte": 1, "$gt": 0, "$gte": 1};
const lts = ["$lt", "$lte"];
const gts = ["$gt", "$gte"];
// Iterate through all posible combinations of operators.
lts.forEach((llt) =>
gts.forEach((lgt) => {
lts.forEach((nlt) =>
gts.forEach((ngt) => {
let query = {l: {}, n: {}};
query["l"][llt] = "d";
query["l"][lgt] = "b";
query["n"][nlt] = 4;
query["n"][ngt] = 2;
// Calculate the count of documents based on operators used.
const count = (1 + opExtra[llt] + opExtra[lgt]) * (1 + opExtra[nlt] + opExtra[ngt]);
assert.eq(count, coll.find(query).count(), {query, lIndexDirection, nIndexDirection});
// Also check with all posible sort combinations.
const withSort = (lSort, nSort) => {
assert.eq(count, coll.find(query).sort({l: lSort, n: nSort}).count(), {
query,
lSort,
nSort,
lIndexDirection,
nIndexDirection,
});
};
withSort(1, 1);
withSort(-1, 1);
withSort(1, -1);
withSort(-1, -1);
}),
);
}),
);
};
testBoundsInclusivityCompound(1, 1);
testBoundsInclusivityCompound(-1, 1);
testBoundsInclusivityCompound(1, -1);
testBoundsInclusivityCompound(-1, -1);