mirror of https://github.com/mongodb/mongo
326 lines
9.3 KiB
JavaScript
326 lines
9.3 KiB
JavaScript
// Tests the behavior of $ceil, $floor, $exp, $log10, $ln and $sqrt when used in agg expressions.
|
|
|
|
import "jstests/libs/query/sbe_assert_error_override.js";
|
|
|
|
import {assertErrorCode} from "jstests/aggregation/extras/utils.js";
|
|
|
|
const coll = db.unary_numeric;
|
|
coll.drop();
|
|
|
|
// Testing behavior for common cases.
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{_id: 0, x: null},
|
|
{_id: 1, x: undefined},
|
|
{_id: 2},
|
|
{_id: 3, x: 1},
|
|
{_id: 4, x: 10},
|
|
{_id: 5, x: 100},
|
|
{_id: 6, x: 2.3},
|
|
{_id: 7, x: 2.6},
|
|
{_id: 8, x: NumberDecimal("40")},
|
|
{_id: 9, x: NumberLong(4)},
|
|
{_id: 10, x: NumberLong("9223372036854775807")}, // LLONG_MAX
|
|
]),
|
|
);
|
|
|
|
let results = coll
|
|
.aggregate([
|
|
{
|
|
$project: {
|
|
ceil: {$ceil: "$x"},
|
|
exp: {$exp: "$x"},
|
|
floor: {$floor: "$x"},
|
|
ln: {$ln: "$x"},
|
|
log10: {$log10: "$x"},
|
|
sqrt: {$sqrt: "$x"},
|
|
abs: {$abs: "$x"},
|
|
},
|
|
},
|
|
{$sort: {_id: 1}},
|
|
])
|
|
.toArray();
|
|
|
|
let expectedResults = [
|
|
{
|
|
_id: 0,
|
|
"ceil": null,
|
|
"exp": null,
|
|
"floor": null,
|
|
"ln": null,
|
|
"log10": null,
|
|
"sqrt": null,
|
|
"abs": null,
|
|
},
|
|
{
|
|
_id: 1,
|
|
"ceil": null,
|
|
"exp": null,
|
|
"floor": null,
|
|
"ln": null,
|
|
"log10": null,
|
|
"sqrt": null,
|
|
"abs": null,
|
|
},
|
|
{
|
|
_id: 2,
|
|
"ceil": null,
|
|
"exp": null,
|
|
"floor": null,
|
|
"ln": null,
|
|
"log10": null,
|
|
"sqrt": null,
|
|
"abs": null,
|
|
},
|
|
{
|
|
_id: 3,
|
|
"ceil": 1,
|
|
"exp": 2.718281828459045,
|
|
"floor": 1,
|
|
"ln": 0,
|
|
"log10": 0,
|
|
"sqrt": 1,
|
|
"abs": 1,
|
|
},
|
|
{
|
|
_id: 4,
|
|
"ceil": 10,
|
|
"exp": 22026.465794806718,
|
|
"floor": 10,
|
|
"ln": 2.302585092994046,
|
|
"log10": 1,
|
|
"sqrt": 3.1622776601683795,
|
|
"abs": 10,
|
|
},
|
|
{
|
|
_id: 5,
|
|
"ceil": 100,
|
|
"exp": 2.6881171418161356e43,
|
|
"floor": 100,
|
|
"ln": 4.605170185988092,
|
|
"log10": 2,
|
|
"sqrt": 10,
|
|
"abs": 100,
|
|
},
|
|
{
|
|
_id: 6,
|
|
"ceil": 3,
|
|
"exp": 9.974182454814718,
|
|
"floor": 2,
|
|
"ln": 0.8329091229351039,
|
|
"log10": 0.36172783601759284,
|
|
"sqrt": 1.51657508881031,
|
|
"abs": 2.3,
|
|
},
|
|
{
|
|
_id: 7,
|
|
"ceil": 3,
|
|
"exp": 13.463738035001692,
|
|
"floor": 2,
|
|
"ln": 0.9555114450274363,
|
|
"log10": 0.414973347970818,
|
|
"sqrt": 1.61245154965971,
|
|
"abs": 2.6,
|
|
},
|
|
{
|
|
// Note that we do not test exp, ln, log10, and sqrt here, because they provide inexact
|
|
// results, and there is no simple way to do an approximate comparison of two NumberDecimal
|
|
// objects. The unit tests for these operators should give us enough coverage already.
|
|
_id: 8,
|
|
"ceil": NumberDecimal("40"),
|
|
"floor": NumberDecimal("40"),
|
|
"abs": NumberDecimal("40"),
|
|
},
|
|
{
|
|
_id: 9,
|
|
"ceil": NumberLong(4),
|
|
"exp": 54.598150033144236,
|
|
"floor": NumberLong(4),
|
|
"ln": 1.3862943611198906,
|
|
"log10": 0.6020599913279624,
|
|
"sqrt": 2,
|
|
"abs": NumberLong(4),
|
|
},
|
|
{
|
|
_id: 10,
|
|
"ceil": NumberLong("9223372036854775807"),
|
|
"exp": Infinity,
|
|
"floor": NumberLong("9223372036854775807"),
|
|
"ln": 43.66827237527655,
|
|
"log10": 18.964889726830815,
|
|
"sqrt": 3037000499.97605,
|
|
"abs": NumberLong("9223372036854775807"),
|
|
},
|
|
];
|
|
|
|
// Compare each document in the 'results' array with each document in the 'expectedResults' array,
|
|
// using an approximate equality comparison for numbers. Many of the operators tested may vary in
|
|
// their last few bits depending on platform.
|
|
for (const resultDoc of results) {
|
|
const expectedDoc = expectedResults.find((doc) => resultDoc._id === doc._id);
|
|
assert(expectedDoc);
|
|
|
|
for (const [key, expectedValue] of Object.entries(expectedDoc)) {
|
|
assert(resultDoc.hasOwnProperty(key));
|
|
const resultValue = resultDoc[key];
|
|
|
|
let matches = false;
|
|
if (typeof expectedValue == "object" && typeof resultValue == "object") {
|
|
// NumberDecimal case.
|
|
matches = bsonWoCompare({value: expectedValue}, {value: resultValue}) === 0;
|
|
} else if (isFinite(expectedValue) && isFinite(resultValue)) {
|
|
// Regular numbers; do an approximate comparison, expecting 48 bits of precision.
|
|
const epsilon = Math.pow(2, -48);
|
|
const delta = Math.abs(expectedValue - resultValue);
|
|
matches =
|
|
delta === 0 ||
|
|
delta / Math.min(Math.abs(expectedValue) + Math.abs(resultValue), Number.MAX_VALUE) < epsilon;
|
|
} else {
|
|
matches = expectedValue === resultValue;
|
|
}
|
|
assert(
|
|
matches,
|
|
`Mismatched ${key} field in document with _id ${resultDoc._id} -- Expected: ${
|
|
expectedValue
|
|
}, Actual: ${resultValue}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
// Testing behavior for special cases: negative numbers, 0, NaN & Infinity on expressions that don't
|
|
// error.
|
|
assert(coll.drop());
|
|
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{_id: 0, x: 0},
|
|
{_id: 1, x: NaN},
|
|
{_id: 2, x: Infinity},
|
|
{_id: 3, x: -Infinity},
|
|
{_id: 4, x: -2.6},
|
|
]),
|
|
);
|
|
|
|
results = coll
|
|
.aggregate([
|
|
{$project: {ceil: {$ceil: "$x"}, floor: {$floor: "$x"}, exp: {$ceil: {$exp: "$x"}}}},
|
|
{$sort: {_id: 1}},
|
|
])
|
|
.toArray();
|
|
|
|
assert.eq(results, [
|
|
{
|
|
_id: 0,
|
|
"ceil": 0,
|
|
"floor": 0,
|
|
"exp": 1,
|
|
},
|
|
{
|
|
_id: 1,
|
|
"ceil": NaN,
|
|
"floor": NaN,
|
|
"exp": NaN,
|
|
},
|
|
{
|
|
_id: 2,
|
|
"ceil": Infinity,
|
|
"floor": Infinity,
|
|
"exp": Infinity,
|
|
},
|
|
{
|
|
_id: 3,
|
|
"ceil": -Infinity,
|
|
"floor": -Infinity,
|
|
"exp": 0,
|
|
},
|
|
{
|
|
_id: 4,
|
|
"ceil": -2,
|
|
"floor": -3,
|
|
"exp": 1,
|
|
},
|
|
]);
|
|
|
|
// Testing $sqrt, $ln & $log10 success with NaN:
|
|
assert(coll.drop());
|
|
|
|
assert.commandWorked(
|
|
coll.insert([
|
|
{_id: 0, x: NaN},
|
|
{_id: 1, x: NumberDecimal("NaN")},
|
|
]),
|
|
);
|
|
|
|
results = coll
|
|
.aggregate([{$project: {ln: {$ln: "$x"}, log10: {$log10: "$x"}, sqrt: {$sqrt: "$x"}}}, {$sort: {_id: 1}}])
|
|
.toArray();
|
|
|
|
assert.eq(results, [
|
|
{_id: 0, "ln": NaN, "log10": NaN, "sqrt": NaN},
|
|
{_id: 1, "ln": NaN, "log10": NaN, "sqrt": NumberDecimal("NaN")},
|
|
]);
|
|
|
|
// Testing error codes.
|
|
// $ln, $log10 fail for 0 and negative numbers.
|
|
// $sqrt fails for negative numbers.
|
|
|
|
// Testing failures with 0:
|
|
assert(coll.drop());
|
|
assert.commandWorked(coll.insert([{_id: 0, x: 0}]));
|
|
assertErrorCode(coll, [{$project: {a: {$ln: "$x"}}}], 28766);
|
|
assertErrorCode(coll, [{$project: {a: {$log10: "$x"}}}], 28761);
|
|
|
|
// Testing $sqrt success with 0:
|
|
results = coll.aggregate([{$project: {a: {$sqrt: "$x"}}}]).toArray();
|
|
assert.eq(results, [{_id: 0, a: 0}]);
|
|
|
|
// Testing failures with NumberLong(0):
|
|
assert(coll.drop());
|
|
assert.commandWorked(coll.insert([{_id: 0, x: NumberLong(0)}]));
|
|
assertErrorCode(coll, [{$project: {a: {$ln: "$x"}}}], 28766);
|
|
assertErrorCode(coll, [{$project: {a: {$log10: "$x"}}}], 28761);
|
|
|
|
// Testing $sqrt success with NumberLong(0):
|
|
results = coll.aggregate([{$project: {a: {$sqrt: "$x"}}}]).toArray();
|
|
assert.eq(results, [{_id: 0, a: 0}]);
|
|
|
|
// Testing failures with NumberDecimal(0):
|
|
assert(coll.drop());
|
|
assert.commandWorked(coll.insert([{_id: 0, x: NumberDecimal("0")}]));
|
|
assertErrorCode(coll, [{$project: {a: {$ln: "$x"}}}], 28766);
|
|
assertErrorCode(coll, [{$project: {a: {$log10: "$x"}}}], 28761);
|
|
|
|
// Testing $sqrt success with NumberDecimal(0):
|
|
results = coll.aggregate([{$project: {a: {$sqrt: "$x"}}}]).toArray();
|
|
assert.eq(results, [{_id: 0, a: NumberDecimal("0")}]);
|
|
|
|
// Testing failures with negative numbers (all types):
|
|
assert(coll.drop());
|
|
assert.commandWorked(coll.insert([{_id: 0, x: -1}]));
|
|
assertErrorCode(coll, [{$project: {a: {$ln: "$x"}}}], 28766);
|
|
assertErrorCode(coll, [{$project: {a: {$log10: "$x"}}}], 28761);
|
|
assertErrorCode(coll, [{$project: {a: {$sqrt: "$x"}}}], 28714);
|
|
|
|
assert(coll.drop());
|
|
assert.commandWorked(coll.insert([{_id: 0, x: NumberLong(-1)}]));
|
|
assertErrorCode(coll, [{$project: {a: {$ln: "$x"}}}], 28766);
|
|
assertErrorCode(coll, [{$project: {a: {$log10: "$x"}}}], 28761);
|
|
assertErrorCode(coll, [{$project: {a: {$sqrt: "$x"}}}], 28714);
|
|
|
|
assert(coll.drop());
|
|
assert.commandWorked(coll.insert([{_id: 0, x: NumberDecimal("-1")}]));
|
|
assertErrorCode(coll, [{$project: {a: {$ln: "$x"}}}], 28766);
|
|
assertErrorCode(coll, [{$project: {a: {$log10: "$x"}}}], 28761);
|
|
assertErrorCode(coll, [{$project: {a: {$sqrt: "$x"}}}], 28714);
|
|
|
|
// All unary numeric agg operators fail for strings:
|
|
assert(coll.drop());
|
|
assert.commandWorked(coll.insert([{_id: 0, x: "string"}]));
|
|
assertErrorCode(coll, [{$project: {a: {$abs: "$x"}}}], 28765);
|
|
assertErrorCode(coll, [{$project: {a: {$ceil: "$x"}}}], 28765);
|
|
assertErrorCode(coll, [{$project: {a: {$floor: "$x"}}}], 28765);
|
|
assertErrorCode(coll, [{$project: {a: {$exp: "$x"}}}], 28765);
|
|
assertErrorCode(coll, [{$project: {a: {$log10: "$x"}}}], 28765);
|
|
assertErrorCode(coll, [{$project: {a: {$ln: "$x"}}}], 28765);
|
|
assertErrorCode(coll, [{$project: {a: {$sqrt: "$x"}}}], 28765);
|