/* * Tests general $range functionality. */ import "jstests/libs/query/sbe_assert_error_override.js"; import {arrayEq, assertErrorCode} from "jstests/aggregation/extras/utils.js"; const coll = db.range; coll.drop(); assert.commandWorked( coll.insert([ {city: "San Jose", distance: NumberInt(42)}, {city: "Sacramento", distance: NumberInt(88)}, {city: "Reno", distance: NumberInt(218)}, {city: "Los Angeles", distance: NumberInt(383)}, ]), ); const positiveRangeExpectedResult = [ {"city": "San Jose", "Rest stops": [0, 25]}, {"city": "Sacramento", "Rest stops": [0, 25, 50, 75]}, {"city": "Reno", "Rest stops": [0, 25, 50, 75, 100, 125, 150, 175, 200]}, { "city": "Los Angeles", "Rest stops": [0, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 325, 350, 375], }, ]; // Expecting the results to have an "Rest stops" array with positive elements. const positiveRangeResult = coll .aggregate([ { $project: { _id: 0, city: 1, "Rest stops": {$range: [NumberInt(0), "$distance", NumberInt(25)]}, }, }, ]) .toArray(); assert(arrayEq(positiveRangeExpectedResult, positiveRangeResult)); // Expecting same result when Int64 is used as long as it's value is representable in Int32. const positiveRangeResult2 = coll .aggregate([ { $project: { _id: 0, city: 1, "Rest stops": {$range: [NumberLong(0), "$distance", NumberLong(25)]}, }, }, ]) .toArray(); assert(arrayEq(positiveRangeExpectedResult, positiveRangeResult2)); const negativeRangeExpectedResult = [ {"city": "San Jose", "Rest stops": [0, -25]}, {"city": "Sacramento", "Rest stops": [0, -25, -50, -75]}, {"city": "Reno", "Rest stops": [0, -25, -50, -75, -100, -125, -150, -175, -200]}, { "city": "Los Angeles", "Rest stops": [0, -25, -50, -75, -100, -125, -150, -175, -200, -225, -250, -275, -300, -325, -350, -375], }, ]; // Expecting the results to have an "Rest stops" array with negative elements. const negativeRangeResult = coll .aggregate([ { $project: { _id: 0, city: 1, "Rest stops": {$range: [NumberInt(0), {"$multiply": ["$distance", -1]}, NumberInt(-25)]}, }, }, ]) .toArray(); assert(arrayEq(negativeRangeExpectedResult, negativeRangeResult)); const nothingRangeExpectedResult = [ {"city": "San Jose", "Rest stops": []}, {"city": "Sacramento", "Rest stops": []}, {"city": "Reno", "Rest stops": []}, {"city": "Los Angeles", "Rest stops": []}, ]; // Expecting the results to have an empty "Rest stops" array. const nothingRangeResult = coll .aggregate([ { $project: { _id: 0, city: 1, "Rest stops": {$range: [NumberInt(0), {"$multiply": ["$distance", -1]}, NumberInt(25)]}, }, }, ]) .toArray(); assert(arrayEq(nothingRangeExpectedResult, nothingRangeResult)); // Expecting the results to have an empty "Rest stops" array. const nothingRangeResult2 = coll .aggregate([ { $project: {_id: 0, city: 1, "Rest stops": {$range: ["$distance", "$distance", NumberInt(25)]}}, }, ]) .toArray(); assert(arrayEq(nothingRangeExpectedResult, nothingRangeResult2)); // Testing default step. coll.drop(); assert.commandWorked( coll.insert([ {city: "San Jose", distance: NumberInt(5)}, {city: "Sacramento", distance: NumberInt(8)}, {city: "Reno", distance: NumberInt(2)}, {city: "Los Angeles", distance: NumberInt(1)}, ]), ); const rangeDefaultStepExpectedResult = [ {"city": "San Jose", "Rest stops": [0, 1, 2, 3, 4]}, {"city": "Sacramento", "Rest stops": [0, 1, 2, 3, 4, 5, 6, 7]}, {"city": "Reno", "Rest stops": [0, 1]}, {"city": "Los Angeles", "Rest stops": [0]}, ]; // Expecting the results to have an "Rest stops" array with positive elements starting from 0 to the // distance value. const rangeDefaultStepResult = coll .aggregate([{$project: {_id: 0, city: 1, "Rest stops": {$range: [NumberInt(0), "$distance"]}}}]) .toArray(); assert(arrayEq(rangeDefaultStepExpectedResult, rangeDefaultStepResult)); // Expecting the results to have an empty "Rest stops" array. const nothingRangeDefaultStepResult = coll .aggregate([ { $project: { _id: 0, city: 1, "Rest stops": {$range: [NumberInt(0), {"$multiply": ["$distance", NumberInt(-1)]}]}, }, }, ]) .toArray(); assert(arrayEq(nothingRangeExpectedResult, nothingRangeDefaultStepResult)); // Expecting the results to have an empty "Rest stops" array. const nothingRangeDefaultStepResult2 = coll .aggregate([{$project: {_id: 0, city: 1, "Rest stops": {$range: ["$distance", "$distance"]}}}]) .toArray(); assert(arrayEq(nothingRangeExpectedResult, nothingRangeDefaultStepResult2)); coll.drop(); assert.commandWorked(coll.insert([{city: "San Jose", distance: NumberInt(100)}])); // Testing overflow errors due to $range start and end taking int32 values. // Example: {$range: [100, 2147483647, 1073741824]} // Output will OOM because array will look like this: // [ 100, 1073741924, -2147483548, -1073741724, 100, 1073741924, -2147483548, -1073741724, 100, … so // on and so forth ] const overflowRangeExpectedResult = [{"city": "San Jose", "Rest stops": [100, 1073741924]}]; const overflowRangeResult = coll .aggregate([ { $project: { _id: 0, city: 1, "Rest stops": {$range: ["$distance", NumberInt(2147483647), NumberInt(1073741824)]}, }, }, ]) .toArray(); assert(arrayEq(overflowRangeExpectedResult, overflowRangeResult)); // Testing int32 representable errors (Arguments to $range must be int32 representable). let pipeline; // Start value is too big. pipeline = [{$project: {_id: 0, city: 1, "Rest stops": {$range: [NumberLong("12147483647"), "$distance"]}}}]; assertErrorCode(coll, pipeline, 34444); // Start value is a decimal. pipeline = [{$project: {_id: 0, city: 1, "Rest stops": {$range: [NumberDecimal("0.35"), "$distance"]}}}]; assertErrorCode(coll, pipeline, 34444); // Start value is not a number. pipeline = [{$project: {_id: 0, city: 1, "Rest stops": {$range: ["String is not a number", "$distance"]}}}]; assertErrorCode(coll, pipeline, 34443); // Start value is null. pipeline = [{$project: {_id: 0, city: 1, "Rest stops": {$range: [null, "$distance"]}}}]; assertErrorCode(coll, pipeline, 34443); // End value is too big. pipeline = [{$project: {_id: 0, city: 1, "Rest stops": {$range: ["$distance", NumberLong("12147483647")]}}}]; assertErrorCode(coll, pipeline, 34446); // End value is a decimal. pipeline = [{$project: {_id: 0, city: 1, "Rest stops": {$range: ["$distance", NumberDecimal("0.35")]}}}]; assertErrorCode(coll, pipeline, 34446); // End value is not a number. pipeline = [{$project: {_id: 0, city: 1, "Rest stops": {$range: ["$distance", "String is not a number"]}}}]; assertErrorCode(coll, pipeline, 34445); // End value is null. pipeline = [{$project: {_id: 0, city: 1, "Rest stops": {$range: ["$distance", null]}}}]; assertErrorCode(coll, pipeline, 34445); // Step value is too big. pipeline = [ { $project: { _id: 0, city: 1, "Rest stops": {$range: ["$distance", NumberInt(100), NumberLong("12147483647")]}, }, }, ]; assertErrorCode(coll, pipeline, 34448); // Step value is a decimal. pipeline = [ { $project: { _id: 0, city: 1, "Rest stops": {$range: ["$distance", NumberInt(100), NumberDecimal("0.35")]}, }, }, ]; assertErrorCode(coll, pipeline, 34448); // Step value is not a number. pipeline = [ { $project: { _id: 0, city: 1, "Rest stops": {$range: ["$distance", NumberInt(100), "String is not a number"]}, }, }, ]; assertErrorCode(coll, pipeline, 34447); // Step value is null. pipeline = [{$project: {_id: 0, city: 1, "Rest stops": {$range: ["$distance", NumberInt(100), null]}}}]; assertErrorCode(coll, pipeline, 34447); // Step value is zero. pipeline = [{$project: {_id: 0, city: 1, "Rest stops": {$range: [0, "$distance", 0]}}}]; assertErrorCode(coll, pipeline, 34449); // Testing decimal values that are Int32 representable. const decimalRangeExpectedResult = [{"city": "San Jose", "Rest stops": [100, 200]}]; // Expecting results same as if decimals were Int32. const decimalRangeResult = coll .aggregate([ { $project: { _id: 0, city: 1, "Rest stops": {$range: ["$distance", NumberDecimal("201.0"), NumberDecimal("100.0")]}, }, }, ]) .toArray(); assert(arrayEq(decimalRangeExpectedResult, decimalRangeResult)); assert(coll.drop()); assert.commandWorked(coll.insertOne({_id: 1})); assertErrorCode(coll, [{$project: {result: {$range: [0, 1073741924]}}}], ErrorCodes.ExceededMemoryLimit); assert( arrayEq([{_id: 1, result: []}], coll.aggregate([{$project: {result: {$range: [0, 1073741924, -1]}}}]).toArray()), );