summaryrefslogtreecommitdiff
path: root/jstests/aggregation
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/aggregation')
-rw-r--r--jstests/aggregation/accumulators/accumulator_js.js5
-rw-r--r--jstests/aggregation/accumulators/top_bottom_top_n_bottom_n.js13
-rw-r--r--jstests/aggregation/add_with_date.js97
-rw-r--r--jstests/aggregation/api_version_stage_allowance_checks.js3
-rw-r--r--jstests/aggregation/bugs/empty_field_names.js19
-rw-r--r--jstests/aggregation/bugs/groupMissing.js62
-rw-r--r--jstests/aggregation/bugs/hash_lookup_spill_large_and_small_documents_correctly.js74
-rw-r--r--jstests/aggregation/bugs/server18427.js (renamed from jstests/aggregation/expressions/log_pow_exp.js)205
-rw-r--r--jstests/aggregation/bugs/server6570.js23
-rw-r--r--jstests/aggregation/bugs/server72651.js12
-rw-r--r--jstests/aggregation/bugs/timeseries_should_not_push_match_before_project.js30
-rw-r--r--jstests/aggregation/bugs/window_inside_facet.js77
-rw-r--r--jstests/aggregation/collection_uuid_coll_stats_index_stats.js11
-rw-r--r--jstests/aggregation/documents_merge.js113
-rw-r--r--jstests/aggregation/expressions/add.js121
-rw-r--r--jstests/aggregation/expressions/collation_expressions.js13
-rw-r--r--jstests/aggregation/expressions/date_from_string.js4
-rw-r--r--jstests/aggregation/expressions/expression_trigonometric.js469
-rw-r--r--jstests/aggregation/expressions/internal_key_string_value.js199
-rw-r--r--jstests/aggregation/expressions/subtract.js81
-rw-r--r--jstests/aggregation/expressions/switch_errors.js19
-rw-r--r--jstests/aggregation/extras/utils.js13
-rw-r--r--jstests/aggregation/group_by_objectid.js36
-rw-r--r--jstests/aggregation/group_conversion_to_distinct_scan.js25
-rw-r--r--jstests/aggregation/ifnull.js12
-rw-r--r--jstests/aggregation/large_bson_mid_pipeline.js27
-rw-r--r--jstests/aggregation/match_no_swap_rand.js164
-rw-r--r--jstests/aggregation/optimize_away_pipeline.js3
-rw-r--r--jstests/aggregation/sources/densify/explicit_range.js92
-rw-r--r--jstests/aggregation/sources/densify/full_range.js32
-rw-r--r--jstests/aggregation/sources/densify/libs/densify_in_js.js66
-rw-r--r--jstests/aggregation/sources/geonear/requires_geo_index.js12
-rw-r--r--jstests/aggregation/sources/graphLookup/filter.js65
-rw-r--r--jstests/aggregation/sources/indexStats/verify_index_stats_output.js3
-rw-r--r--jstests/aggregation/sources/lookup/lookup_non_correlated_prefix.js36
-rw-r--r--jstests/aggregation/sources/lookup/lookup_query_stats.js7
-rw-r--r--jstests/aggregation/sources/merge/merge_with_dollar_fields.js132
-rw-r--r--jstests/aggregation/sources/merge/mode_merge_fail.js8
-rw-r--r--jstests/aggregation/sources/merge/mode_replace_fail.js8
-rw-r--r--jstests/aggregation/sources/multiple_unpack_bucket_error.js28
-rw-r--r--jstests/aggregation/sources/search_stage_error.js40
-rw-r--r--jstests/aggregation/sources/setWindowFields/comprehensive_parse.js51
-rw-r--r--jstests/aggregation/sources/setWindowFields/derivative.js6
-rw-r--r--jstests/aggregation/sources/setWindowFields/explain.js7
-rw-r--r--jstests/aggregation/sources/setWindowFields/integral.js6
-rw-r--r--jstests/aggregation/sources/setWindowFields/memory_limit.js6
-rw-r--r--jstests/aggregation/sources/setWindowFields/output_overwrites_existing_data.js2
-rw-r--r--jstests/aggregation/sources/setWindowFields/range_wrong_type.js36
-rw-r--r--jstests/aggregation/sources/setWindowFields/spill_to_disk.js4
-rw-r--r--jstests/aggregation/sources/shred_documents.js61
-rw-r--r--jstests/aggregation/sources/unionWith/unionWith_explain.js30
-rw-r--r--jstests/aggregation/spill_to_disk.js223
-rw-r--r--jstests/aggregation/unwind_sort.js27
-rw-r--r--jstests/aggregation/variables/search_meta.js2
54 files changed, 480 insertions, 2440 deletions
diff --git a/jstests/aggregation/accumulators/accumulator_js.js b/jstests/aggregation/accumulators/accumulator_js.js
index 16e9a62116a..3a389851e24 100644
--- a/jstests/aggregation/accumulators/accumulator_js.js
+++ b/jstests/aggregation/accumulators/accumulator_js.js
@@ -192,9 +192,8 @@ command.pipeline = [{
}
}
}];
-// ErrorCodes.TypeMismatch means "$add only supports numeric or date types". Code 16554 represented
-// a type mismatch before 6.0 for this specific check.
-assert.commandFailedWithCode(db.runCommand(command), [16554, ErrorCodes.TypeMismatch]);
+// 16554 means "$add only supports numeric or date types"
+assert.commandFailedWithCode(db.runCommand(command), 16554);
// Test that initArgs can have a different length per group.
assert(db.accumulator_js.drop());
diff --git a/jstests/aggregation/accumulators/top_bottom_top_n_bottom_n.js b/jstests/aggregation/accumulators/top_bottom_top_n_bottom_n.js
index 2556850752a..d0844a4acac 100644
--- a/jstests/aggregation/accumulators/top_bottom_top_n_bottom_n.js
+++ b/jstests/aggregation/accumulators/top_bottom_top_n_bottom_n.js
@@ -392,17 +392,4 @@ const testOperatorText = (op) => {
// most relevant results first.
testOperatorText("$bottomN");
testOperatorText("$topN");
-
-// Test constant output and sortBy.
-assert(coll.drop());
-assert.commandWorked(coll.insertMany([{a: 1}, {a: 2}, {a: 3}]));
-const testConstantOutputAndSort = (op) => {
- const results =
- coll.aggregate([{$group: {_id: null, result: {[op]: {n: 3, output: "abc", sortBy: {}}}}}])
- .toArray();
- assert.eq(results.length, 1, results);
- assert.docEq(results[0], {_id: null, result: ["abc", "abc", "abc"]}, results);
-};
-testConstantOutputAndSort("$topN");
-testConstantOutputAndSort("$bottomN");
})();
diff --git a/jstests/aggregation/add_with_date.js b/jstests/aggregation/add_with_date.js
index 84335e0ecc1..8e3cb8597b5 100644
--- a/jstests/aggregation/add_with_date.js
+++ b/jstests/aggregation/add_with_date.js
@@ -1,19 +1,12 @@
-// @tags: [do_not_wrap_aggregations_in_facets,]
(function() {
"use strict";
-load("jstests/libs/sbe_assert_error_override.js"); // Override error-code-checking APIs.
-// TODO BACKPORT-19405, BACKPORT-19406: SBE and classic have different behaviors documented in this
-// test. These are unified by the SERVER tickets of the mentioned backports.
-load("jstests/libs/sbe_util.js"); // For checkSBEEnabled.
const coll = db.getSiblingDB(jsTestName()).coll;
coll.drop();
-const isSbeEnabled = checkSBEEnabled(db, ["featureFlagSbeFull"]);
-
function getResultOfExpression(expr) {
const resultArray = coll.aggregate({$project: {computed: expr}}).toArray();
- assert.eq(1, resultArray.length, "ERROR from " + tojson(expr));
+ assert.eq(1, resultArray.length);
return resultArray[0].computed;
}
@@ -68,24 +61,13 @@ assert.eq(ISODate("2019-01-30T07:30:10.957Z"),
getResultOfExpression({$add: ["$int32Val", "$dateVal"]}));
// Addition with a date and multiple values of differing data types.
-// SBE uses doubleDoubleSum for $add whereas classic does not.
-if (isSbeEnabled) {
- assert.eq(
- ISODate("2019-01-30T07:30:12.596Z"),
- getResultOfExpression({$add: ["$dateVal", "$decimalVal", "$doubleVal", "$int64Val"]}));
- assert.eq(
- ISODate("2019-01-30T07:30:12.596Z"),
- getResultOfExpression({$add: ["$decimalVal", "$dateVal", "$doubleVal", "$int64Val"]}));
-} else {
- assert.eq(
- ISODate("2019-01-30T07:30:12.597Z"),
- getResultOfExpression({$add: ["$dateVal", "$decimalVal", "$doubleVal", "$int64Val"]}));
- assert.eq(
- ISODate("2019-01-30T07:30:12.597Z"),
- getResultOfExpression({$add: ["$decimalVal", "$dateVal", "$doubleVal", "$int64Val"]}));
-}
+assert.eq(ISODate("2019-01-30T07:30:12.596Z"),
+ getResultOfExpression({$add: ["$dateVal", "$decimalVal", "$doubleVal", "$int64Val"]}));
+assert.eq(ISODate("2019-01-30T07:30:12.596Z"),
+ getResultOfExpression({$add: ["$decimalVal", "$dateVal", "$doubleVal", "$int64Val"]}));
assert.eq(ISODate("2019-01-30T07:30:12.596Z"),
getResultOfExpression({$add: ["$decimalVal", "$doubleVal", "$int64Val", "$dateVal"]}));
+
// The result of an addition must remain in the range of int64_t in order to convert back to a Date;
// an overflow into the domain of double-precision floating point numbers triggers a query-fatal
// error.
@@ -103,27 +85,13 @@ assert.throwsWithCode(
() => getResultOfExpression({$add: ["$int64Val", "$dateVal", "$overflowDouble"]}),
ErrorCodes.Overflow);
-// An overflow into the domain of Decimal128 results in an overflow exception.
-// SBE overflows to return nan, whereas classic throws.
-if (isSbeEnabled) {
- // One quirk of date addition semantics is that an overflow into the domain of Decimal128 is not
- // fatal and instead results in an invalid "NaN" Date value.
- const nanDate = new Date("");
- assert.eq(nanDate, getResultOfExpression({$add: ["$dateVal", "$overflowDecimal"]}));
- assert.eq(nanDate,
- getResultOfExpression({$add: ["$int64Val", "$dateVal", "$overflowDecimal"]}));
- assert.eq(nanDate,
- getResultOfExpression({$add: ["$dateVal", "$overflowDouble", "$overflowDecimal"]}));
-} else {
- assert.throwsWithCode(() => getResultOfExpression({$add: ["$dateVal", "$overflowDecimal"]}),
- ErrorCodes.Overflow);
- assert.throwsWithCode(
- () => getResultOfExpression({$add: ["$int64Val", "$dateVal", "$overflowDecimal"]}),
- ErrorCodes.Overflow);
- assert.throwsWithCode(
- () => getResultOfExpression({$add: ["$dateVal", "$overflowDouble", "$overflowDecimal"]}),
- ErrorCodes.Overflow);
-}
+// One quirk of date addition semantics is that an overflow into the domain of Decimal128 is not
+// fatal and instead results in an invalid "NaN" Date value.
+const nanDate = new Date("");
+assert.eq(nanDate, getResultOfExpression({$add: ["$dateVal", "$overflowDecimal"]}));
+assert.eq(nanDate,
+ getResultOfExpression({$add: ["$dateVal", "$overflowDouble", "$overflowDecimal"]}));
+assert.eq(nanDate, getResultOfExpression({$add: ["$int64Val", "$dateVal", "$overflowDecimal"]}));
// Adding a double-typed NaN to a date value.
assert.throwsWithCode(() => getResultOfExpression({$add: ["$dateVal", "$nanDouble"]}),
@@ -132,43 +100,14 @@ assert.throwsWithCode(() => getResultOfExpression({$add: ["$dateVal", "$nanDoubl
assert.throwsWithCode(() => getResultOfExpression({$add: ["$nanDouble", "$dateVal"]}),
ErrorCodes.Overflow);
-// An NaN Decimal128 added to date results in an overflow exception.
-// SBE overflows to return NaN, whereas classic throws.
-if (isSbeEnabled) {
- const nanDate = new Date("");
- assert.eq(nanDate, getResultOfExpression({$add: ["$dateVal", "$nanDecimal"]}));
- assert.eq(nanDate, getResultOfExpression({$add: ["$nanDecimal", "$dateVal"]}));
-} else {
- assert.throwsWithCode(() => getResultOfExpression({$add: ["$dateVal", "$nanDecimal"]}),
- ErrorCodes.Overflow);
- assert.throwsWithCode(() => getResultOfExpression({$add: ["$nanDecimal", "$dateVal"]}),
- ErrorCodes.Overflow);
-}
+// Adding a Decimal128-typed NaN to a date value.
+assert.eq(nanDate, getResultOfExpression({$add: ["$dateVal", "$nanDecimal"]}));
+assert.eq(nanDate, getResultOfExpression({$add: ["$nanDecimal", "$dateVal"]}));
+
// Addition with a date, a double-typed NaN, and a third value.
assert.throwsWithCode(() => getResultOfExpression({$add: ["$dateVal", "$doubleVal", "$nanDouble"]}),
ErrorCodes.Overflow);
// Addition with a date, and both types of NaN.
-// SBE returns NaN if it's a value in $add, whereas classic throws.
-if (isSbeEnabled) {
- const nanDate = new Date("");
- assert.eq(nanDate, getResultOfExpression({$add: ["$dateVal", "$nanDouble", "$nanDecimal"]}));
-} else {
- assert.throwsWithCode(
- () => getResultOfExpression({$add: ["$dateVal", "$nanDouble", "$nanDecimal"]}),
- ErrorCodes.Overflow);
-}
-// Throw error when there're two or more date in $add.
-assert.throwsWithCode(() => getResultOfExpression({$add: ["$dateVal", 1, "$dateVal"]}), 4974202);
-
-// Test very large long and verify that we're maintaining the precision of long arithmetic.
-// 2397083434877565865 and 239708343487756586 both cast to the same double value from longs
-assert.eq(ISODate("2019-01-30T07:30:10.958Z"), getResultOfExpression({
- $add: [
- "$dateVal",
- NumberLong("2397083434877565865"),
- "$doubleVal",
- NumberLong("-2397083434877565864")
- ]
- }));
+assert.eq(nanDate, getResultOfExpression({$add: ["$dateVal", "$nanDouble", "$nanDecimal"]}));
}());
diff --git a/jstests/aggregation/api_version_stage_allowance_checks.js b/jstests/aggregation/api_version_stage_allowance_checks.js
index 0c3e404842e..f47b27fd630 100644
--- a/jstests/aggregation/api_version_stage_allowance_checks.js
+++ b/jstests/aggregation/api_version_stage_allowance_checks.js
@@ -43,6 +43,7 @@ let result = curDB.runCommand({
remotes: [],
nss: "test.mergeCursors",
allowPartialResults: false,
+ recordRemoteOpWaitTime: false
}
}],
cursor: {},
@@ -63,6 +64,7 @@ result = testDB.runCommand({
remotes: [],
nss: "test.mergeCursors",
allowPartialResults: false,
+ recordRemoteOpWaitTime: false
}
}],
cursor: {},
@@ -83,6 +85,7 @@ result = testDB.runCommand({
remotes: [],
nss: "test.mergeCursors",
allowPartialResults: false,
+ recordRemoteOpWaitTime: false
}
}],
cursor: {},
diff --git a/jstests/aggregation/bugs/empty_field_names.js b/jstests/aggregation/bugs/empty_field_names.js
deleted file mode 100644
index 04429027e51..00000000000
--- a/jstests/aggregation/bugs/empty_field_names.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// Testing documents that contain empty field names. This was written as part of
-// SERVER-86619.
-db.emptyFields.drop();
-db.createCollection('emptyFields');
-
-const kNumDocs = 50;
-
-for (let i = 0; i < 50; ++i) {
- db.emptyFields.insert({"": 123, "b": 456, sortField: i});
-}
-
-assert.eq(db.emptyFields
- .aggregate([
- {$sort: {sortField: 1}},
- {$addFields: {"m": {$meta: "sortKey"}}},
- {$match: {"b": 456}}
- ])
- .itcount(),
- kNumDocs);
diff --git a/jstests/aggregation/bugs/groupMissing.js b/jstests/aggregation/bugs/groupMissing.js
index ba6b0f58fff..f13477a90b6 100644
--- a/jstests/aggregation/bugs/groupMissing.js
+++ b/jstests/aggregation/bugs/groupMissing.js
@@ -7,23 +7,23 @@
// @tags: [
// do_not_wrap_aggregations_in_facets,
// ]
-load('jstests/aggregation/extras/utils.js'); // For assertArrayEq.
+load('jstests/aggregation/extras/utils.js'); // For resultsEq.
(function() {
"use strict";
-const coll = db.getCollection(jsTestName());
+var coll = db.groupMissing;
coll.drop();
-assert.commandWorked(coll.insert({a: null}));
-assert.commandWorked(coll.insert({}));
+coll.insert({a: null});
+coll.insert({});
-let res = coll.aggregate({$group: {_id: "$a"}});
-let arr = res.toArray();
+var res = coll.aggregate({$group: {_id: "$a"}});
+var arr = res.toArray();
assert.eq(arr.length, 1);
assert.eq(arr[0]._id, null);
-assert.commandWorked(coll.createIndex({a: 1}));
+coll.createIndex({a: 1});
res = coll.aggregate({$sort: {a: 1}}, {$group: {_id: "$a"}});
arr = res.toArray();
assert.eq(arr.length, 1);
@@ -31,49 +31,41 @@ assert.eq(arr[0]._id, null);
coll.drop();
-assert.commandWorked(coll.insert({a: null}));
-assert.commandWorked(coll.insert({}));
+coll.insert({a: null});
+coll.insert({});
// Bug, see SERVER-21992.
res = coll.aggregate({$group: {_id: {a: "$a"}}});
-assertArrayEq({actual: res.toArray(), expected: [{_id: {a: null}}]});
+assert(resultsEq(res.toArray(), [{_id: {a: null}}]));
// Bug, see SERVER-21992.
-assert.commandWorked(coll.createIndex({a: 1}));
+coll.createIndex({a: 1});
res = coll.aggregate({$group: {_id: {a: "$a"}}});
-assertArrayEq({actual: res.toArray(), expected: [{_id: {a: null}}]});
+assert(resultsEq(res.toArray(), [{_id: {a: null}}]));
// Correct behavior after SERVER-21992 is fixed.
if (0) {
res = coll.aggregate({$group: {_id: {a: "$a"}}});
- assertArrayEq({actual: res.toArray(), expected: [{_id: {a: null}}, {_id: {}}]});
+ assert(resultsEq(res.toArray(), [{_id: {a: null}}, {_id: {}}]));
}
coll.drop();
-assert.commandWorked(coll.insert({a: null, b: 1}));
-assert.commandWorked(coll.insert({b: 1}));
-assert.commandWorked(coll.insert({a: null, b: 1}));
+coll.insert({a: null, b: 1});
+coll.insert({b: 1});
+coll.insert({a: null, b: 1});
res = coll.aggregate({$group: {_id: {a: "$a", b: "$b"}}});
-assertArrayEq({actual: res.toArray(), expected: [{_id: {b: 1}}, {_id: {a: null, b: 1}}]});
+assert(resultsEq(res.toArray(), [{_id: {b: 1}}, {_id: {a: null, b: 1}}]));
-assert.commandWorked(coll.createIndex({a: 1, b: 1}));
-res = coll.aggregate([{$group: {_id: {a: "$a", b: "$b"}}}, {$sort: {"_id.a": 1, "_id.b": 1}}]);
-// Before fixing SERVER-23229 we were getting [{_id: {a: null, b: 1}}]
-assertArrayEq({actual: res.toArray(), expected: [{_id: {b: 1}}, {_id: {a: null, b: 1}}]});
+// Bug, see SERVER-23229.
+coll.createIndex({a: 1, b: 1});
+res = coll.aggregate({$sort: {a: 1, b: 1}}, {$group: {_id: {a: "$a", b: "$b"}}});
+assert(resultsEq(res.toArray(), [{_id: {a: null, b: 1}}]));
-// Try another variation of the query that is taken more directly from the bug report SERVER-23229.
-coll.drop();
-assert.commandWorked(coll.insert({a: 1, b: null}));
-assert.commandWorked(coll.insert({a: null, b: 1}));
-assert.commandWorked(coll.insert({b: 1}));
-assert.commandWorked(coll.insert({a: 1}));
-
-let preSortResult =
- coll.aggregate({$sort: {a: 1, b: 1}}, {$group: {_id: {a: "$a", b: "$b"}}}).toArray();
-assert.commandWorked(coll.createIndex({a: 1, b: 1}));
-assertArrayEq({
- actual: preSortResult,
- expected: coll.aggregate({$group: {_id: {a: "$a", b: "$b"}}}).toArray()
-});
+// Correct behavior after SERVER-23229 is fixed.
+if (0) {
+ coll.createIndex({a: 1, b: 1});
+ res = coll.aggregate({$sort: {a: 1, b: 1}}, {$group: {_id: {a: "$a", b: "$b"}}});
+ assert(resultsEq(res.toArray(), [{_id: {b: 1}}, {_id: {a: null, b: 1}}]));
+}
}());
diff --git a/jstests/aggregation/bugs/hash_lookup_spill_large_and_small_documents_correctly.js b/jstests/aggregation/bugs/hash_lookup_spill_large_and_small_documents_correctly.js
deleted file mode 100644
index de8f79bbea7..00000000000
--- a/jstests/aggregation/bugs/hash_lookup_spill_large_and_small_documents_correctly.js
+++ /dev/null
@@ -1,74 +0,0 @@
-// Regression test to check that different document sizes work correctly with $lookup.
-// @tags: [
-// requires_fcv_71,
-// ]
-(function() {
-'use strict';
-
-load('jstests/libs/fixture_helpers.js'); // For 'FixtureHelpers'
-
-const localColl = db.lookup_spill_local;
-const foreignColl = db.lookup_spill_foreign;
-localColl.drop();
-foreignColl.drop();
-
-const memoryLimit = 128; // Spill at 128 bytes
-
-function setHashLookupMemoryLimit(memoryLimit) {
- const commandResArr = FixtureHelpers.runCommandOnEachPrimary({
- db: db.getSiblingDB("admin"),
- cmdObj: {
- setParameter: 1,
- internalQuerySlotBasedExecutionHashLookupApproxMemoryUseInBytesBeforeSpill: memoryLimit,
- }
- });
- assert.gt(commandResArr.length, 0, "Setting memory limit on primaries failed");
- assert.commandWorked(commandResArr[0]);
-}
-
-function runHashLookupSpill() {
- const smallStr = "small";
- const bigStr = Array(memoryLimit).toString();
- const localDoc = {_id: 1, a: 2};
- const foreignDocs = [
- {_id: 0, b: 1, padding: smallStr},
- {_id: 1, b: 2, padding: bigStr},
- {_id: 2, b: 1, padding: smallStr},
- {_id: 3, b: 2, padding: bigStr},
- {_id: 4, b: 1, padding: smallStr},
- {_id: 5, b: 2, padding: bigStr},
- {_id: 6, b: 1, padding: smallStr},
- {_id: 7, b: 2, padding: bigStr},
- {_id: 8, b: 1, padding: smallStr},
- ];
-
- assert.commandWorked(localColl.insert(localDoc));
- assert.commandWorked(foreignColl.insertMany(foreignDocs));
- const pipeline = [
- {$lookup: {from: foreignColl.getName(), localField: "a", foreignField: "b", as: "matched"}},
- {$sort: {_id: 1}}
- ];
-
- const result = localColl.aggregate(pipeline).toArray();
- assert.eq(result.length, 1, result);
- assert.eq(result[0].matched.length, 4, result);
- for (let matched of result[0].matched) {
- assert.eq(matched.padding, bigStr);
- }
-}
-
-const oldMemoryLimit =
- assert
- .commandWorked(db.adminCommand({
- getParameter: 1,
- internalQuerySlotBasedExecutionHashLookupApproxMemoryUseInBytesBeforeSpill: 1
- }))
- .internalQuerySlotBasedExecutionHashLookupApproxMemoryUseInBytesBeforeSpill;
-
-try {
- setHashLookupMemoryLimit(memoryLimit);
- runHashLookupSpill();
-} finally {
- setHashLookupMemoryLimit(oldMemoryLimit);
-}
-})();
diff --git a/jstests/aggregation/expressions/log_pow_exp.js b/jstests/aggregation/bugs/server18427.js
index b74cf4fed93..a633632ab3d 100644
--- a/jstests/aggregation/expressions/log_pow_exp.js
+++ b/jstests/aggregation/bugs/server18427.js
@@ -10,29 +10,13 @@ var coll = db.log_exponential_expressions;
coll.drop();
assert.commandWorked(coll.insert({_id: 0, a: 8, b: 2}));
-const doubleE = 2.7182818284590452;
-const decimalE = NumberDecimal("2.718281828459045235360287471352662");
-const decimal1overE = NumberDecimal("0.3678794411714423215955237701614609");
-
-// Given a double, is it an integer?
-function isInteger(n) {
- return !n.toString().includes('.');
-}
-
-function isNumberDecimal(n) {
- return n.toString().includes('NumberDecimal');
-}
+var decimalE = NumberDecimal("2.718281828459045235360287471352662");
+var decimal1overE = NumberDecimal("0.3678794411714423215955237701614609");
// Helper for testing that op returns expResult.
-function testOp(op, expResult, failMsg) {
- const pipeline = [{$project: {_id: 0, result: op}}];
- const result = coll.aggregate(pipeline).toArray();
- assert.eq(result.length, 1);
- if (expResult === null || isNaN(expResult) || isNumberDecimal(expResult)) {
- assert.eq(result[0].result, expResult, failMsg);
- } else {
- assert.close(result[0].result, expResult, failMsg, 12 /*places*/);
- }
+function testOp(op, expResult) {
+ var pipeline = [{$project: {_id: 0, result: op}}];
+ assert.eq(coll.aggregate(pipeline).toArray(), [{result: expResult}]);
}
// $log, $log10, $ln.
@@ -42,180 +26,11 @@ function testOp(op, expResult, failMsg) {
testOp({$log: [10, 10]}, 1);
testOp({$log10: [10]}, 1);
testOp({$ln: [Math.E]}, 1);
-
-// Different double and NumberDecimal inputs, verified manually.
-const logTestCases = [
- // Base 8
- {input: 1, base: 8, doubleResult: 0, decResult: NumberDecimal("0E+33")},
- {
- input: 2.5,
- base: 8,
- doubleResult: 0.4406426982957875,
- decResult: NumberDecimal("0.4406426982957874492901064764964633")
- },
- {
- input: 7,
- base: 8,
- doubleResult: 0.9357849740192015,
- decResult: NumberDecimal("0.9357849740192013691473231057439436")
- },
- {input: 8, base: 8, doubleResult: 1, decResult: NumberDecimal("1")},
- {
- input: 64,
- base: 8,
- doubleResult: 2,
- decResult: NumberDecimal("2.000000000000000000000000000000000")
- },
- {
- input: 65,
- base: 8,
- doubleResult: 2.0074559376761516,
- decResult: NumberDecimal("2.007455937676151502755710694582028")
- },
-
- // Base 9, a more unusual base.
- {input: 1, base: 9, doubleResult: 0, decResult: NumberDecimal("0E+33")},
- {
- input: 2.5,
- base: 9,
- doubleResult: 0.41702188357323483,
- decResult: NumberDecimal("0.4170218835732348650487566466679398")
- },
- {
- input: 4,
- base: 9,
- doubleResult: 0.6309297535714574,
- decResult: NumberDecimal("0.6309297535714574370995271143427609")
- },
- {input: 9, base: 9, doubleResult: 1, decResult: NumberDecimal("1")},
- {
- input: 10,
- base: 9,
- doubleResult: 1.0479516371446924,
- decResult: NumberDecimal("1.047951637144692302148283761010701")
- },
- {input: 81, base: 9, doubleResult: 2, decResult: NumberDecimal("2")},
- {
- input: 82,
- base: 9,
- doubleResult: 2.0055843597957064,
- decResult: NumberDecimal("2.005584359795706389324272570756155")
- },
-
- // Base 12.77, an even MORE unusual base.
- {input: 1, base: 12.77, doubleResult: 0, decResult: NumberDecimal("0E+33")},
- {
- input: 2.5,
- base: 12.77,
- doubleResult: 0.3597390013391846,
- decResult: NumberDecimal("0.3597390013391846393309152124110717")
- },
- {input: 12.77, base: 12.77, doubleResult: 1, decResult: NumberDecimal("1")},
- {
- input: 13,
- base: 12.77,
- doubleResult: 1.0070082433896357,
- decResult: NumberDecimal("1.007008243389635671677137269228113")
- },
- {
- input: 163.0729,
- base: 12.77,
- doubleResult: 2,
- decResult: NumberDecimal("2.000000000000000000000000000000000")
- },
- {
- input: 170,
- base: 12.77,
- doubleResult: 2.016332738676606,
- decResult: NumberDecimal("2.016332738676605709114981994718173")
- },
-];
-for (const test of logTestCases) {
- // If we can cast our input, base (or both) to integer types, test them as well.
- const inputs = [test.input, NumberDecimal(test.input.toString())];
- if (isInteger(test.input)) {
- inputs.push(NumberInt(test.input), NumberLong(test.input));
- }
- const bases = [test.base, NumberDecimal(test.base.toString())];
- if (isInteger(test.base)) {
- bases.push(NumberInt(test.base), NumberLong(test.base));
- }
- for (const input of inputs) {
- for (const base of bases) {
- const hasDecimalInput = isNumberDecimal(input) || isNumberDecimal(base);
- testOp(
- {$log: [input, base]}, hasDecimalInput ? test.decResult : test.doubleResult, test);
- }
- }
-}
-
-// Base 10, using $log10
-const log10TestCases = [
- {input: 1, doubleResult: 0, decResult: NumberDecimal("0")},
- {
- input: 2.5,
- doubleResult: 0.3979400086720376,
- decResult: NumberDecimal("0.3979400086720376095725222105510140")
- },
- {input: 10, doubleResult: 1, decResult: NumberDecimal("1")},
- {
- input: 11,
- doubleResult: 1.041392685158225,
- decResult: NumberDecimal("1.041392685158225040750199971243024")
- },
- {input: 100, doubleResult: 2, decResult: NumberDecimal("2")},
- {
- input: 101,
- doubleResult: 2.0043213737826426,
- decResult: NumberDecimal("2.004321373782642574275188178222938")
- },
-];
-for (const test of log10TestCases) {
- // If the input is an integer anyway, test with our integer types as well.
- if (isInteger(test.input)) {
- testOp({$log10: NumberInt(test.input)}, test.doubleResult, test);
- testOp({$log10: NumberLong(test.input)}, test.doubleResult, test);
- }
- testOp({$log10: test.input}, test.doubleResult, test);
- testOp({$log10: NumberDecimal(test.input.toString())}, test.decResult, test);
-}
-
-// Base `e`, using $ln.
-const lnTestCases = [
- {input: 1, doubleResult: 0, decResult: NumberDecimal("0")},
- // `e` is about 2.7, so this should be close to 1.
- {
- input: 2.5,
- doubleResult: 0.9162907318741551,
- decResult: NumberDecimal("0.9162907318741550651835272117680110")
- },
- {
- input: 7,
- doubleResult: 1.9459101490553132,
- decResult: NumberDecimal("1.945910149055313305105352743443180")
- },
- {
- input: 10,
- doubleResult: 2.302585092994046,
- decResult: NumberDecimal("2.302585092994045684017991454684364")
- },
-];
-for (const test of lnTestCases) {
- if (isInteger(test.input)) {
- testOp({$ln: NumberInt(test.input)}, test.doubleResult, test);
- testOp({$ln: NumberLong(test.input)}, test.doubleResult, test);
- }
- testOp({$ln: test.input}, test.doubleResult, test);
- testOp({$ln: NumberDecimal(test.input.toString())}, test.decResult, test);
-}
-
-// We represent `e` differently with double and NumberDecimal, so test that here.
-testOp({$ln: doubleE}, 1);
-testOp({$ln: 1 / doubleE}, -1);
-// The below answer is actually correct: the input is an approximation of E.
-testOp({$ln: decimalE}, NumberDecimal("0.9999999999999999999999999999999998"));
-testOp({$ln: decimal1overE}, NumberDecimal("-0.9999999999999999999999999999999998"));
-
+// - NumberDecimal
+testOp({$log: [NumberDecimal("10"), NumberDecimal("10")]}, NumberDecimal("1"));
+testOp({$log10: [NumberDecimal("10")]}, NumberDecimal("1"));
+// The below answer is actually correct: the input is an approximation of E
+testOp({$ln: [decimalE]}, NumberDecimal("0.9999999999999999999999999999999998"));
// All types converted to doubles.
testOp({$log: [NumberLong("10"), NumberLong("10")]}, 1);
testOp({$log10: [NumberLong("10")]}, 1);
diff --git a/jstests/aggregation/bugs/server6570.js b/jstests/aggregation/bugs/server6570.js
index 1d0b6338b43..112feb49406 100644
--- a/jstests/aggregation/bugs/server6570.js
+++ b/jstests/aggregation/bugs/server6570.js
@@ -6,19 +6,10 @@ c = db.s6570;
c.drop();
c.save({x: 17, y: "foo"});
-// 16554 was the code used instead of TypeMismatch before 6.0.
-assertErrorCode(
- c, {$project: {string_fields: {$add: [3, "$y", 4, "$y"]}}}, [16554, ErrorCodes.TypeMismatch]);
-assertErrorCode(c,
- {$project: {number_fields: {$add: ["a", "$x", "b", "$x"]}}},
- [16554, ErrorCodes.TypeMismatch]);
-assertErrorCode(
- c, {$project: {all_strings: {$add: ["c", "$y", "d", "$y"]}}}, [16554, ErrorCodes.TypeMismatch]);
-assertErrorCode(
- c, {$project: {potpourri_1: {$add: [5, "$y", "e", "$x"]}}}, [16554, ErrorCodes.TypeMismatch]);
-assertErrorCode(
- c, {$project: {potpourri_2: {$add: [6, "$x", "f", "$y"]}}}, [16554, ErrorCodes.TypeMismatch]);
-assertErrorCode(
- c, {$project: {potpourri_3: {$add: ["g", "$y", 7, "$x"]}}}, [16554, ErrorCodes.TypeMismatch]);
-assertErrorCode(
- c, {$project: {potpourri_4: {$add: ["h", "$x", 8, "$y"]}}}, [16554, ErrorCodes.TypeMismatch]); \ No newline at end of file
+assertErrorCode(c, {$project: {string_fields: {$add: [3, "$y", 4, "$y"]}}}, 16554);
+assertErrorCode(c, {$project: {number_fields: {$add: ["a", "$x", "b", "$x"]}}}, 16554);
+assertErrorCode(c, {$project: {all_strings: {$add: ["c", "$y", "d", "$y"]}}}, 16554);
+assertErrorCode(c, {$project: {potpourri_1: {$add: [5, "$y", "e", "$x"]}}}, 16554);
+assertErrorCode(c, {$project: {potpourri_2: {$add: [6, "$x", "f", "$y"]}}}, 16554);
+assertErrorCode(c, {$project: {potpourri_3: {$add: ["g", "$y", 7, "$x"]}}}, 16554);
+assertErrorCode(c, {$project: {potpourri_4: {$add: ["h", "$x", 8, "$y"]}}}, 16554);
diff --git a/jstests/aggregation/bugs/server72651.js b/jstests/aggregation/bugs/server72651.js
deleted file mode 100644
index b4100bdc32c..00000000000
--- a/jstests/aggregation/bugs/server72651.js
+++ /dev/null
@@ -1,12 +0,0 @@
-// SERVER-72651 $match filter is erroneously pushed past $project into COLLSCAN
-(function() {
-
-const c = db.server72651;
-
-c.drop();
-assert.commandWorked(c.insert({_id: 0, a: 1}));
-// The bug caused the query below to return {"_id" : 0} instead of no documents.
-assert.eq(
- [],
- c.aggregate([{$project: {"b": 1}}, {$match: {$expr: {$getField: {$literal: "a"}}}}]).toArray());
-})(); \ No newline at end of file
diff --git a/jstests/aggregation/bugs/timeseries_should_not_push_match_before_project.js b/jstests/aggregation/bugs/timeseries_should_not_push_match_before_project.js
deleted file mode 100644
index f2465988f0e..00000000000
--- a/jstests/aggregation/bugs/timeseries_should_not_push_match_before_project.js
+++ /dev/null
@@ -1,30 +0,0 @@
-// Regression test for SERVER-71270.
-(function() {
-"use strict";
-load('jstests/aggregation/extras/utils.js'); // For assertArrayEq.
-const doc = {
- _id: 0,
- time: new Date('2019-01-18T13:24:15.443Z'),
- tag: {},
-};
-
-db.ts.drop();
-db.coll.drop();
-
-db.createCollection('ts', {timeseries: {timeField: 'time', metaField: 'tag'}});
-db.createCollection('coll');
-
-db.ts.insertOne(doc);
-db.coll.insertOne(doc);
-const pipeline = [
- {$project: {'time': 0}},
- {$match: {'time': {$lte: new Date('2019-02-13T11:36:03.481Z')}}},
-];
-
-const ts = db.ts.aggregate(pipeline).toArray();
-const vanilla = db.coll.aggregate(pipeline).toArray();
-assertArrayEq({
- actual: ts,
- expected: vanilla,
-});
-}());
diff --git a/jstests/aggregation/bugs/window_inside_facet.js b/jstests/aggregation/bugs/window_inside_facet.js
deleted file mode 100644
index baf9fcd5150..00000000000
--- a/jstests/aggregation/bugs/window_inside_facet.js
+++ /dev/null
@@ -1,77 +0,0 @@
-// Test that $setWindowFields inside $facet correctly propagates its state when it encounters paused
-// execution.
-(function() {
-"use strict";
-
-const coll = db.window_inside_facet;
-coll.drop();
-
-assert.commandWorked(coll.insert([
- {_id: 'a', n: 0},
- {_id: 'b', n: 1},
- {_id: 'c', n: 2},
- {_id: 'd', n: 3},
- {_id: 'e', n: 4},
- {_id: 'f', n: 5}
-]));
-
-// Test window with a sort within $facet alongside another pipeline that will cause it to pause
-// execution. The sort will cause the window to hit paused execution immediately, before an advance.
-let result =
- coll.aggregate({
- $facet: {
- facet1: [{
- $setWindowFields: {
- output: {prevId: {$shift: {by: -1, default: null, output: "$_id"}}},
- sortBy: {_id: 1}
- }
- }],
- facet2: [{$count: "count"}]
- }
- })
- .toArray()[0];
-let expected = {
- facet1: [
- {_id: 'a', n: 0, prevId: null},
- {_id: 'b', n: 1, prevId: 'a'},
- {_id: 'c', n: 2, prevId: 'b'},
- {_id: 'd', n: 3, prevId: 'c'},
- {_id: 'e', n: 4, prevId: 'd'},
- {_id: 'f', n: 5, prevId: 'e'}
- ],
- facet2: [{count: 6}]
-};
-assert.docEq(expected, result, "$setWindowFields with sort failed.");
-
-// Test window with no sort within $facet alongside another pipeline that will cause it to pause
-// execution. Having no sort will cause the window to hit paused execution after advancing.
-result = coll.aggregate({
- $facet: {
- facet1: [
- {
- $setWindowFields: {
- output: {
- min: {$min: "$n"},
- max: {$max: "$n"},
- }
- }
- },
- {$sort: {_id: 1}}
- ],
- facet2: [{$count: "count"}]
- }
- })
- .toArray()[0];
-expected = {
- facet1: [
- {_id: 'a', n: 0, min: 0, max: 5},
- {_id: 'b', n: 1, min: 0, max: 5},
- {_id: 'c', n: 2, min: 0, max: 5},
- {_id: 'd', n: 3, min: 0, max: 5},
- {_id: 'e', n: 4, min: 0, max: 5},
- {_id: 'f', n: 5, min: 0, max: 5}
- ],
- facet2: [{count: 6}]
-};
-assert.docEq(expected, result, "$setWindowFields without sort failed.");
-}());
diff --git a/jstests/aggregation/collection_uuid_coll_stats_index_stats.js b/jstests/aggregation/collection_uuid_coll_stats_index_stats.js
index 67c8ca65113..5aa92524652 100644
--- a/jstests/aggregation/collection_uuid_coll_stats_index_stats.js
+++ b/jstests/aggregation/collection_uuid_coll_stats_index_stats.js
@@ -49,19 +49,10 @@ const testCommand = function(cmd, cmdObj) {
jsTestLog("The command '" + cmd +
"' fails when the provided UUID corresponds to a different collection, even if the " +
"provided namespace does not exist.");
- assert.commandWorked(testDB.runCommand({drop: coll2.getName()}));
+ coll2.drop();
res =
assert.commandFailedWithCode(testDB.runCommand(cmdObj), ErrorCodes.CollectionUUIDMismatch);
validateErrorResponse(res, testDB.getName(), uuid, coll2.getName(), coll.getName());
- assert(!testDB.getCollectionNames().includes(coll2.getName()));
-
- jsTestLog("The command '" + cmd +
- "' fails with CollectionUUIDMismatch even if the database does not exist.");
- const nonexistentDB = testDB.getSiblingDB(testDB.getName() + '_nonexistent');
- cmdObj[cmd] = 'nonexistent';
- res = assert.commandFailedWithCode(nonexistentDB.runCommand(cmdObj),
- ErrorCodes.CollectionUUIDMismatch);
- validateErrorResponse(res, nonexistentDB.getName(), uuid, 'nonexistent', null);
jsTestLog("The command '" + cmd + "' succeeds on view when no UUID is provided.");
const viewName = "view";
diff --git a/jstests/aggregation/documents_merge.js b/jstests/aggregation/documents_merge.js
deleted file mode 100644
index 99e9a8f1a41..00000000000
--- a/jstests/aggregation/documents_merge.js
+++ /dev/null
@@ -1,113 +0,0 @@
-/**
- * This is the test for $documents stage along with $merge stage in an aggregation pipeline,
- * including verifying the bug in SERVER-85892 is addressed when the spec 'whenMatched' is not
- * empty.
- *
- * @tags: [
- * requires_fcv_80,
- * assumes_against_mongod_not_mongos, # not yet supported until 7.2 with SERVER-65534
- * ]
- */
-
-load("jstests/aggregation/extras/merge_helpers.js"); // For withEachMergeMode and
- // dropWithoutImplicitRecreate.
-
-const outColl = db[`${jsTest.name()}_out`];
-const outCollName = outColl.getName();
-const expectedTotalDocs = 100;
-
-function assertDocsInsertedCorrectly(docs, pipeline) {
- const msg = `Failed with pipeline: ${JSON.stringify(pipeline, null, 2)}`;
-
- assert.eq(expectedTotalDocs, docs.length, msg);
- for (let i = 0; i < expectedTotalDocs; i++) {
- assert.eq(docs[i].x, i, msg);
- }
-}
-
-const documentsStage = {
- $documents: {$map: {input: {$range: [0, expectedTotalDocs]}, in : {x: "$$this"}}}
-};
-
-function testFn(pipeline, assertFn) {
- // Creates an index as $merge requires a unique index with the 'on' identifier field. Then
- // inserts a document allowed to be matched.
- dropWithoutImplicitRecreate(outCollName);
- assert.commandWorked(outColl.createIndex({x: 1}, {unique: true}));
- assert.commandWorked(outColl.insert({x: 10}));
-
- assert.doesNotThrow(() => db.aggregate(pipeline));
- let res = outColl.find({}, {_id: 0}).sort({x: 1}).toArray();
- assertDocsInsertedCorrectly(res, pipeline);
- assertFn(res);
-}
-
-{ // Tests $merge with non-empty pipeline along with let in whenMatched spec.
- const pipeline = [
- documentsStage,
- {
- $merge: {
- into: outCollName,
- let : {num: 123},
- whenMatched: [{$set: {number: "$$num"}}],
- on: "x"
- }
- }
- ];
-
- testFn(pipeline, res => {
- assert.eq(res.filter(elem => elem.number === 123).length, 1);
- });
-}
-
-{ // Tests $merge with non-empty pipeline in whenMatched spec.
- const pipeline = [
- documentsStage,
- {$merge: {into: outCollName, whenMatched: [{$set: {new: true}}], on: "x"}}
- ];
-
- testFn(pipeline, res => {
- assert.eq(res.filter(elem => elem.new === true).length, 1);
- });
-}
-
-// Tests each combination of merge modes.
-withEachMergeMode(({whenMatchedMode, whenNotMatchedMode}) => {
- const expectErrorCode = whenMatchedMode === "fail"
- ? ErrorCodes.DuplicateKey
- : whenNotMatchedMode === "fail" ? ErrorCodes.MergeStageNoMatchingDocument : null;
-
- // Creates an index as $merge requires a unique index with the 'on' identifier field. Then
- // inserts a document allowed to be matched.
- dropWithoutImplicitRecreate(outCollName);
- assert.commandWorked(outColl.createIndex({x: 1}, {unique: true}));
- assert.commandWorked(outColl.insert({x: 10, old: true}));
-
- const pipeline = [
- documentsStage,
- {
- $merge: {
- into: outCollName,
- whenMatched: whenMatchedMode,
- whenNotMatched: whenNotMatchedMode,
- on: "x",
- }
- }
- ];
-
- if (expectErrorCode) {
- assert.throwsWithCode(() => db.aggregate(pipeline), expectErrorCode);
- return;
- }
-
- assert.doesNotThrow(() => db.aggregate(pipeline));
- let res = outColl.find({}, {_id: 0}).sort({x: 1}).toArray();
- if (whenNotMatchedMode == "discard") {
- assert.eq(outColl.count(), 1);
- } else {
- assertDocsInsertedCorrectly(res, pipeline);
- }
-
- // Asserts if the old document is replaced when 'whenMatchedMode' is "replace".
- assert.eq(res.filter(elem => elem.old === true).length, whenMatchedMode == "replace" ? 0 : 1);
-});
diff --git a/jstests/aggregation/expressions/add.js b/jstests/aggregation/expressions/add.js
deleted file mode 100644
index 28fff6e33c7..00000000000
--- a/jstests/aggregation/expressions/add.js
+++ /dev/null
@@ -1,121 +0,0 @@
-(function() {
-"use strict";
-load("jstests/aggregation/extras/utils.js"); // For assertErrorCode and assertErrMsgContains.
-load("jstests/libs/sbe_assert_error_override.js"); // Override error-code-checking APIs.
-// TODO BACKPORT-19405, BACKPORT-19406: SBE and classic have different behaviors documented in this
-// test. These are unified by the SERVER tickets of the mentioned backports.
-load("jstests/libs/sbe_util.js"); // For checkSBEEnabled.
-
-// In SERVER-63012, translation of $add expression into sbe now defaults the translation of $add
-// with no operands to a zero integer constant.
-const coll = db.add_coll;
-coll.drop();
-
-const isSbeEnabled = checkSBEEnabled(db, ["featureFlagSbeFull"]);
-
-assert.commandWorked(coll.insert({x: 1}));
-let result = coll.aggregate([{$project: {y: {$add: []}}}]).toArray();
-assert.eq(result[0]["y"], 0);
-
-// Confirm that we're not using DoubleDoubleSummation for $add expression with a set of double
-// values.
-let arr = [
- 1.4831356930199802e-05, -3.121724665346865, 3041897608700.073, 1001318343149.7166,
- -1714.6229586696593, 1731390114894580.8, 6.256645803154374e-08, -107144114533844.25,
- -0.08839485091750919, -265119153.02185738, -0.02450615965231944, 0.0002684331017079073,
- 32079040427.68358, -0.04733295911845742, 0.061381859083076085, -25329.59126796951,
- -0.0009567520620034965, -1553879364344.9932, -2.1101077525869814e-08, -298421079729.5547,
- 0.03182394834273594, 22.201944843278916, -33.35667991109125, 11496013.960449915,
- -40652595.33210472, 3.8496066090328163, 2.5074042398147304e-08, -0.02208724071782122,
- -134211.37290639878, 0.17640433666616578, 4.463787499171126, 9.959669945399718,
- 129265976.35224283, 1.5865526187526546e-07, -4746011.710555799, -712048598925.0789,
- 582214206210.4034, 0.025236204812875362, 530078170.91147506, -14.865307666195053,
- 1.6727994895185032e-05, -113386276.03121366, -6.135827207137054, 10644945799901.145,
- -100848907797.1582, 2.2404406961625282e-08, 1.315662618424494e-09, -0.832190208349044,
- -9.779323414999364, -546522170658.2997
-];
-let doc = {_id: 0};
-let i = 0;
-let queryArr = [];
-arr.forEach(num => {
- i++;
- doc[`f_${i}`] = num;
- queryArr.push(`$f_${i}`);
-});
-
-assert.eq(true, coll.drop());
-assert.commandWorked(coll.insert(doc));
-
-let addResult = coll.aggregate([{$project: {add: {$add: queryArr}}}]).toArray();
-let sumResult = coll.aggregate([{$project: {sum: {$sum: queryArr}}}]).toArray();
-
-// SBE uses doubleDoubleSum for $add and $sum, whereas classic uses it for only $sum.
-if (isSbeEnabled) {
- assert.eq(addResult[0]["add"], sumResult[0]["sum"]);
-} else {
- assert.neq(addResult[0]["add"], sumResult[0]["sum"]);
- assert.eq(addResult[0]["add"], arr.reduce((a, b) => a + b));
-}
-
-assert.eq(true, coll.drop());
-// Doubles are rounded to int64 when added to Date
-assert.commandWorked(coll.insert({_id: 0, lhs: new Date(1683794065002), rhs: 0.5}));
-assert.commandWorked(coll.insert({_id: 1, lhs: new Date(1683794065002), rhs: 1.4}));
-assert.commandWorked(coll.insert({_id: 2, lhs: new Date(1683794065002), rhs: 1.5}));
-assert.commandWorked(coll.insert({_id: 3, lhs: new Date(1683794065002), rhs: 1.7}));
-// Decimals are rounded to int64, when tie rounded to even, when added to Date
-assert.commandWorked(
- coll.insert({_id: 4, lhs: new Date(1683794065002), rhs: new NumberDecimal("1.4")}));
-assert.commandWorked(
- coll.insert({_id: 5, lhs: new Date(1683794065002), rhs: new NumberDecimal("1.5")}));
-assert.commandWorked(
- coll.insert({_id: 6, lhs: new Date(1683794065002), rhs: new NumberDecimal("1.7")}));
-assert.commandWorked(
- coll.insert({_id: 7, lhs: new Date(1683794065002), rhs: new NumberDecimal("2.5")}));
-
-let result1 =
- coll.aggregate([{$project: {sum: {$add: ["$lhs", "$rhs"]}}}, {$sort: {_id: 1}}]).toArray();
-
-// SBE uses doubleDoubleSum for $add whereas classic does not.
-if (isSbeEnabled) {
- assert.eq(result1[0].sum, new Date(1683794065002));
-} else {
- assert.eq(result1[0].sum, new Date(1683794065003));
-}
-assert.eq(result1[1].sum, new Date(1683794065003));
-
-// SBE uses doubleDoubleSum for $add whereas classic does not.
-if (isSbeEnabled) {
- assert.eq(result1[2].sum, new Date(1683794065003));
-} else {
- assert.eq(result1[2].sum, new Date(1683794065004));
-}
-assert.eq(result1[3].sum, new Date(1683794065004));
-assert.eq(result1[4].sum, new Date(1683794065003));
-assert.eq(result1[5].sum, new Date(1683794065004));
-assert.eq(result1[6].sum, new Date(1683794065004));
-assert.eq(result1[7].sum, new Date(1683794065004));
-
-coll.drop();
-
-assert.commandWorked(coll.insert([{
- _id: 0,
- veryBigPositiveLong: NumberLong("9223372036854775806"),
- veryBigPositiveDouble: 9223372036854775806,
- veryBigPositiveDecimal: NumberDecimal("9223372036854775806")
-}]));
-
-let pipeline = [{$project: {res: {$add: [new Date(10), "$veryBigPositiveLong"]}}}];
-assertErrCodeAndErrMsgContains(coll, pipeline, ErrorCodes.Overflow, "date overflow");
-
-pipeline = [{$project: {res: {$add: [new Date(10), "$veryBigPositiveDouble"]}}}];
-assertErrCodeAndErrMsgContains(coll, pipeline, ErrorCodes.Overflow, "date overflow");
-
-pipeline = [{$project: {res: {$add: [new Date(10), "$veryBigPositiveDecimal"]}}}];
-if (isSbeEnabled) {
- assert.commandWorked(
- coll.getDB().runCommand({aggregate: coll.getName(), pipeline: pipeline, cursor: {}}));
-} else {
- assertErrCodeAndErrMsgContains(coll, pipeline, ErrorCodes.Overflow, "date overflow");
-}
-}());
diff --git a/jstests/aggregation/expressions/collation_expressions.js b/jstests/aggregation/expressions/collation_expressions.js
index c9643d49506..5ecad4f02ec 100644
--- a/jstests/aggregation/expressions/collation_expressions.js
+++ b/jstests/aggregation/expressions/collation_expressions.js
@@ -86,19 +86,6 @@ results = coll.aggregate([{$project: {out: {$setUnion: [["a", "B", "c"], ["d", "
assert.eq(1, results.length);
assert.eq(4, results[0].out.length);
-// Test that $setUnion is not commutative when a collation is set. A non-const value is required for
-// reordering to occur.
-assert(coll.drop);
-coll.drop();
-assert.commandWorked(coll.insert({_id: 1, upper: "A", lower: "a"}));
-var results1 = coll.aggregate([{$project: {out: {$setUnion: [["$upper"], ["a"]]}}}],
- {collation: caseInsensitive})
- .toArray();
-var results2 = coll.aggregate([{$project: {out: {$setUnion: [["A"], ["$lower"]]}}}],
- {collation: caseInsensitive})
- .toArray();
-assert.eq(results1, results2);
-
// Test that $setDifference respects the collation.
testExpressionWithCollation(coll, {$setDifference: [["a", "B"], ["b", "A"]]}, [], caseInsensitive);
diff --git a/jstests/aggregation/expressions/date_from_string.js b/jstests/aggregation/expressions/date_from_string.js
index 16b4d0ce311..9b4d0702e5e 100644
--- a/jstests/aggregation/expressions/date_from_string.js
+++ b/jstests/aggregation/expressions/date_from_string.js
@@ -705,7 +705,7 @@ assertErrorCode(coll, pipeline, ErrorCodes.ConversionFailure);
// Test umatched format specifier string.
pipeline = [{$project: {date: {$dateFromString: {dateString: "2018-01", format: "%Y-%m-%d"}}}}];
-assertErrCodeAndErrMsgContains(coll, pipeline, ErrorCodes.ConversionFailure, "Not enough data");
+assertErrCodeAndErrMsgContains(coll, pipeline, ErrorCodes.ConversionFailure, "Data missing");
pipeline = [{$project: {date: {$dateFromString: {dateString: "2018-01", format: "%Y"}}}}];
assertErrCodeAndErrMsgContains(coll, pipeline, ErrorCodes.ConversionFailure, "Trailing data");
@@ -766,7 +766,7 @@ assertErrCodeAndErrMsgContains(coll,
// Test embedded null bytes in the 'dateString' and 'format' fields.
pipeline =
[{$project: {date: {$dateFromString: {dateString: "12/31\0/2018", format: "%m/%d/%Y"}}}}];
-assertErrCodeAndErrMsgContains(coll, pipeline, ErrorCodes.ConversionFailure, "Not enough data");
+assertErrCodeAndErrMsgContains(coll, pipeline, ErrorCodes.ConversionFailure, "Data missing");
pipeline =
[{$project: {date: {$dateFromString: {dateString: "12/31/2018", format: "%m/%d\0/%Y"}}}}];
diff --git a/jstests/aggregation/expressions/expression_trigonometric.js b/jstests/aggregation/expressions/expression_trigonometric.js
index 29da3b96315..8992ab4b5cc 100644
--- a/jstests/aggregation/expressions/expression_trigonometric.js
+++ b/jstests/aggregation/expressions/expression_trigonometric.js
@@ -8,286 +8,285 @@ load('jstests/libs/sbe_assert_error_override.js'); // Override error-code-check
const coll = db.expression_trigonometric;
coll.drop();
-
-// Constructs and inserts a document containing 'val', which are the arguments to trigonometric
-// aggregation expression 'op'. If an aggregation expression has constant arguments, it will be
-// constant folded and will not be evaluated during query execution. Embedding in a document ensures
-// we evaluate expressions during query execution. eg. {$acos: NumberInt(1)} -> {$acos: "$y"} on
-// document {"y" : NumberInt(1)}
-function convertToOpOnDocument(op, val) {
- let nonOptimizedOp = {};
- if (Array.isArray(val) && val.length === 2) {
- nonOptimizedOp = {[op]: ["$y", "$x"]};
- assert.commandWorked(coll.insert({"y": val[0], "x": val[1]}));
- } else {
- nonOptimizedOp = {[op]: "$y"};
- assert.commandWorked(coll.insert({"y": val}));
+// We need at least one document in the collection in order to test expressions, add it here.
+assert.commandWorked(coll.insert({}));
+
+// Run a pipeline with `op` and check that the result of operation against the `expResult` value. If
+// the actual or expected result is non-numeric (NaN or infinite), this function asserts that they
+// match and returns 0 if they do. If they are both numeric, this function returns the difference
+// between them as a double.
+function testOpReturningDifference(op, expResult) {
+ const pipeline = [
+ {$project: {_id: 0, result: op, expected: {$literal: expResult}}},
+ {
+ $addFields: {
+ resultType: {$type: "$result"},
+ expType: {$type: "$expected"},
+ resultAsString: {$convert: {input: "$result", to: "string"}},
+ expAsString: {$convert: {input: "$expected", to: "string"}},
+ difference:
+ {$abs: {$convert: {input: {$subtract: ["$result", "$expected"]}, to: "double"}}}
+ }
+ }
+ ];
+ const {result, resultType, expType, resultAsString, expAsString, difference} = function() {
+ const resultArray = coll.aggregate(pipeline).toArray();
+ assert.eq(resultArray.length, 1, resultArray);
+ return resultArray[0];
+ }();
+
+ assert.eq(resultType, expType);
+
+ // We don't want to do direct comparisons with non-real values, because they can have unexpected
+ // semantics. String comparisons gives us the desired equality semantics for the purposes of
+ // this test.
+ const nonRealValues = ["NaN", "Infinity", "-Infinity"];
+ if (nonRealValues.includes(expAsString) || nonRealValues.includes(resultAsString)) {
+ assert.eq(expAsString, resultAsString, tojson(result));
+ return 0;
}
- return nonOptimizedOp;
+
+ return difference;
}
-function testOp(op, val, expResult) {
- const nonOptimizedOp = convertToOpOnDocument(op, val);
- const pipeline = [{$project: {_id: 0, result: nonOptimizedOp}}];
- assert.eq(coll.aggregate(pipeline).toArray(), [{result: expResult}]);
- assert(coll.drop());
+// Helper for testing that op returns expResult.
+function testOp(op, expResult) {
+ const diff = testOpReturningDifference(op, expResult);
+ assert.eq(0, diff);
}
// Helper for testing that the aggregation expression 'op' returns expResult, approximately,
// since NumberDecimal has so many representations for a given number (0 versus 0e-40 for
// instance).
-function testOpApprox(op, val, expResult) {
- const nonOptimizedOp = convertToOpOnDocument(op, val);
- const pipeline = [{$project: {_id: 0, result: nonOptimizedOp}}];
- const res = coll.aggregate(pipeline).toArray();
- const {result} = res[0];
- const pipeline2 = {
- $project: {
- difference: {$abs: {$convert: {input: {$subtract: [result, expResult]}, to: "double"}}}
- }
- };
- const res2 = coll.aggregate(pipeline2).toArray();
- const {difference} = res2[0];
- assert.lt(difference, 0.00000005);
- assert(coll.drop());
-}
-
-function testErrorCode(op, val, expErrorCode) {
- const nonOptimizedOp = convertToOpOnDocument(op, val);
- const pipeline = [{$project: {_id: 0, result: nonOptimizedOp}}];
- assertErrorCode(coll, pipeline, expErrorCode);
- assert(coll.drop());
+function testOpApprox(op, expResult) {
+ const diff = testOpReturningDifference(op, expResult);
+ assert.lt(diff, 0.00000005);
}
// Simple successful int input.
-testOp("$acos", NumberInt(1), 0);
-testOp("$acosh", NumberInt(1), 0);
-testOp("$asin", NumberInt(0), 0);
-testOp("$asinh", NumberInt(0), 0);
-testOp("$atan", NumberInt(0), 0);
-testOp("$atan2", [NumberInt(0), NumberInt(1)], 0);
-testOp("$atan2", [NumberInt(0), NumberInt(0)], 0);
-testOp("$atanh", NumberInt(0), 0);
-testOp("$cos", NumberInt(0), 1);
-testOp("$cosh", NumberInt(0), 1);
-testOp("$sin", NumberInt(0), 0);
-testOp("$sinh", NumberInt(0), 0);
-testOp("$tan", NumberInt(0), 0);
-testOp("$tanh", NumberInt(0), 0);
-testOp("$degreesToRadians", NumberInt(0), 0);
-testOp("$radiansToDegrees", NumberInt(0), 0);
+testOp({$acos: NumberInt(1)}, 0);
+testOp({$acosh: NumberInt(1)}, 0);
+testOp({$asin: NumberInt(0)}, 0);
+testOp({$asinh: NumberInt(0)}, 0);
+testOp({$atan: NumberInt(0)}, 0);
+testOp({$atan2: [NumberInt(0), NumberInt(1)]}, 0);
+testOp({$atan2: [NumberInt(0), NumberInt(0)]}, 0);
+testOp({$atanh: NumberInt(0)}, 0);
+testOp({$cos: NumberInt(0)}, 1);
+testOp({$cosh: NumberInt(0)}, 1);
+testOp({$sin: NumberInt(0)}, 0);
+testOp({$sinh: NumberInt(0)}, 0);
+testOp({$tan: NumberInt(0)}, 0);
+testOp({$tanh: NumberInt(0)}, 0);
+testOp({$degreesToRadians: NumberInt(0)}, 0);
+testOp({$radiansToDegrees: NumberInt(0)}, 0);
// Simple successful long input.
-testOp("$acos", NumberLong(1), 0);
-testOp("$acosh", NumberLong(1), 0);
-testOp("$asin", NumberLong(0), 0);
-testOp("$asinh", NumberLong(0), 0);
-testOp("$atan", NumberLong(0), 0);
-testOp("$atan2", [NumberLong(0), NumberLong(1)], 0);
-testOp("$atan2", [NumberLong(0), NumberLong(0)], 0);
-testOp("$atanh", NumberLong(0), 0);
-testOp("$cos", NumberLong(0), 1);
-testOp("$cosh", NumberLong(0), 1);
-testOp("$sin", NumberLong(0), 0);
-testOp("$sinh", NumberLong(0), 0);
-testOp("$tan", NumberLong(0), 0);
-testOp("$tanh", NumberLong(0), 0);
-testOp("$degreesToRadians", NumberLong(0), 0);
-testOp("$radiansToDegrees", NumberLong(0), 0);
+testOp({$acos: NumberLong(1)}, 0);
+testOp({$acosh: NumberLong(1)}, 0);
+testOp({$asin: NumberLong(0)}, 0);
+testOp({$asinh: NumberLong(0)}, 0);
+testOp({$atan: NumberLong(0)}, 0);
+testOp({$atan2: [NumberLong(0), NumberLong(1)]}, 0);
+testOp({$atan2: [NumberLong(0), NumberLong(0)]}, 0);
+testOp({$atanh: NumberLong(0)}, 0);
+testOp({$cos: NumberLong(0)}, 1);
+testOp({$cosh: NumberLong(0)}, 1);
+testOp({$sin: NumberLong(0)}, 0);
+testOp({$sinh: NumberLong(0)}, 0);
+testOp({$tan: NumberLong(0)}, 0);
+testOp({$tanh: NumberLong(0)}, 0);
+testOp({$degreesToRadians: NumberLong(0)}, 0);
+testOp({$radiansToDegrees: NumberLong(0)}, 0);
// Simple successful double input.
-testOp("$acos", 1, 0);
-testOp("$acosh", 1, 0);
-testOp("$asin", 0, 0);
-testOp("$asinh", 0, 0);
-testOp("$atan", 0, 0);
-testOp("$atan2", [0, 1], 0);
-testOp("$atan2", [0, 0], 0);
-testOp("$atanh", 0, 0);
-testOp("$cos", 0, 1);
-testOp("$cosh", 0, 1);
-testOp("$sin", 0, 0);
-testOp("$sinh", 0, 0);
-testOp("$tan", 0, 0);
-testOp("$tanh", 0, 0);
-testOp("$degreesToRadians", 0, 0);
-testOp("$radiansToDegrees", 0, 0);
+testOp({$acos: 1}, 0);
+testOp({$acosh: 1}, 0);
+testOp({$asin: 0}, 0);
+testOp({$asinh: 0}, 0);
+testOp({$atan: 0}, 0);
+testOp({$atan2: [0, 1]}, 0);
+testOp({$atan2: [0, 0]}, 0);
+testOp({$atanh: 0}, 0);
+testOp({$cos: 0}, 1);
+testOp({$cosh: 0}, 1);
+testOp({$sin: 0}, 0);
+testOp({$sinh: 0}, 0);
+testOp({$tan: 0}, 0);
+testOp({$tanh: 0}, 0);
+testOp({$degreesToRadians: 0}, 0);
+testOp({$radiansToDegrees: 0}, 0);
// Simple successful decimal input.
-testOpApprox("$acos", NumberDecimal(1), NumberDecimal(0));
-testOpApprox("$acosh", NumberDecimal(1), NumberDecimal(0));
-testOpApprox("$asin", NumberDecimal(0), NumberDecimal(0));
-testOpApprox("$asinh", NumberDecimal(0), NumberDecimal(0));
-testOpApprox("$atan", NumberDecimal(0), NumberDecimal(0));
-testOpApprox("$atan2", [NumberDecimal(0), 1], NumberDecimal(0));
-testOpApprox("$atan2", [NumberDecimal(0), 0], NumberDecimal(0));
-testOpApprox("$atanh", NumberDecimal(0), NumberDecimal(0));
-testOpApprox("$cos", NumberDecimal(0), NumberDecimal(1));
-testOpApprox("$cosh", NumberDecimal(0), NumberDecimal(1));
-testOpApprox("$sin", NumberDecimal(0), NumberDecimal(0));
-testOpApprox("$sinh", NumberDecimal(0), NumberDecimal(0));
-testOpApprox("$tan", NumberDecimal(0), NumberDecimal(0));
-testOpApprox("$tanh", NumberDecimal(0), NumberDecimal(0));
-testOpApprox("$degreesToRadians", NumberDecimal(0), NumberDecimal(0));
-testOpApprox("$radiansToDegrees", NumberDecimal(0), NumberDecimal(0));
+testOpApprox({$acos: NumberDecimal(1)}, NumberDecimal(0));
+testOpApprox({$acosh: NumberDecimal(1)}, NumberDecimal(0));
+testOpApprox({$asin: NumberDecimal(0)}, NumberDecimal(0));
+testOpApprox({$asinh: NumberDecimal(0)}, NumberDecimal(0));
+testOpApprox({$atan: NumberDecimal(0)}, NumberDecimal(0));
+testOpApprox({$atan2: [NumberDecimal(0), 1]}, NumberDecimal(0));
+testOpApprox({$atan2: [NumberDecimal(0), 0]}, NumberDecimal(0));
+testOpApprox({$atanh: NumberDecimal(0)}, NumberDecimal(0));
+testOpApprox({$cos: NumberDecimal(0)}, NumberDecimal(1));
+testOpApprox({$cosh: NumberDecimal(0)}, NumberDecimal(1));
+testOpApprox({$sin: NumberDecimal(0)}, NumberDecimal(0));
+testOpApprox({$sinh: NumberDecimal(0)}, NumberDecimal(0));
+testOpApprox({$tan: NumberDecimal(0)}, NumberDecimal(0));
+testOpApprox({$tanh: NumberDecimal(0)}, NumberDecimal(0));
+testOpApprox({$degreesToRadians: NumberDecimal(0)}, NumberDecimal(0));
+testOpApprox({$radiansToDegrees: NumberDecimal(0)}, NumberDecimal(0));
// Infinity input produces out of bounds error.
-testErrorCode("$acos", -Infinity, 50989);
-testErrorCode("$acos", NumberDecimal('-Infinity'), 50989);
-testErrorCode("$acos", Infinity, 50989);
-testErrorCode("$acos", NumberDecimal('Infinity'), 50989);
-
-testErrorCode("$acosh", -Infinity, 50989);
-testErrorCode("$acosh", NumberDecimal('-Infinity'), 50989);
-
-testErrorCode("$asin", -Infinity, 50989);
-testErrorCode("$asin", NumberDecimal('-Infinity'), 50989);
-testErrorCode("$asin", Infinity, 50989);
-testErrorCode("$asin", NumberDecimal('Infinity'), 50989);
-
-testErrorCode("$atanh", -Infinity, 50989);
-testErrorCode("$atanh", NumberDecimal('-Infinity'), 50989);
-testErrorCode("$atanh", Infinity, 50989);
-testErrorCode("$atanh", NumberDecimal('Infinity'), 50989);
-
-testErrorCode("$cos", -Infinity, 50989);
-testErrorCode("$cos", NumberDecimal('-Infinity'), 50989);
-testErrorCode("$cos", Infinity, 50989);
-testErrorCode("$cos", NumberDecimal('Infinity'), 50989);
-
-testErrorCode("$sin", -Infinity, 50989);
-testErrorCode("$sin", NumberDecimal('-Infinity'), 50989);
-testErrorCode("$sin", Infinity, 50989);
-testErrorCode("$sin", NumberDecimal('Infinity'), 50989);
-
-testErrorCode("$tan", -Infinity, 50989);
-testErrorCode("$tan", NumberDecimal('-Infinity'), 50989);
-testErrorCode("$tan", Infinity, 50989);
-testErrorCode("$tan", NumberDecimal('Infinity'), 50989);
+assertErrorCode(coll, [{$project: {a: {$acos: -Infinity}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$acos: NumberDecimal('-Infinity')}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$acos: Infinity}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$acos: NumberDecimal('Infinity')}}}], 50989);
+
+assertErrorCode(coll, [{$project: {a: {$acosh: -Infinity}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$acosh: NumberDecimal('-Infinity')}}}], 50989);
+
+assertErrorCode(coll, [{$project: {a: {$asin: -Infinity}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$asin: NumberDecimal('-Infinity')}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$asin: Infinity}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$asin: NumberDecimal('Infinity')}}}], 50989);
+
+assertErrorCode(coll, [{$project: {a: {$atanh: -Infinity}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$atanh: NumberDecimal('-Infinity')}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$atanh: Infinity}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$atanh: NumberDecimal('Infinity')}}}], 50989);
+
+assertErrorCode(coll, [{$project: {a: {$cos: -Infinity}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$cos: NumberDecimal('-Infinity')}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$cos: Infinity}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$cos: NumberDecimal('Infinity')}}}], 50989);
+
+assertErrorCode(coll, [{$project: {a: {$sin: -Infinity}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$sin: NumberDecimal('-Infinity')}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$sin: Infinity}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$sin: NumberDecimal('Infinity')}}}], 50989);
+
+assertErrorCode(coll, [{$project: {a: {$tan: -Infinity}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$tan: NumberDecimal('-Infinity')}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$tan: Infinity}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$tan: NumberDecimal('Infinity')}}}], 50989);
// Infinity input produces Infinity as output.
-testOp("$acosh", NumberDecimal('Infinity'), NumberDecimal('Infinity'));
-testOp("$acosh", Infinity, Infinity);
-
-testOp("$asinh", NumberDecimal('Infinity'), NumberDecimal('Infinity'));
-testOp("$asinh", NumberDecimal('-Infinity'), NumberDecimal('-Infinity'));
-testOp("$asinh", Infinity, Infinity);
-testOp("$asinh", -Infinity, -Infinity);
-testOp("$cosh", NumberDecimal('Infinity'), NumberDecimal('Infinity'));
-testOp("$cosh", NumberDecimal('-Infinity'), NumberDecimal('Infinity'));
-testOp("$cosh", Infinity, Infinity);
-testOp("$cosh", -Infinity, Infinity);
-testOp("$sinh", NumberDecimal('Infinity'), NumberDecimal('Infinity'));
-testOp("$sinh", NumberDecimal('-Infinity'), NumberDecimal('-Infinity'));
-testOp("$sinh", Infinity, Infinity);
-testOp("$sinh", -Infinity, -Infinity);
+testOp({$acosh: NumberDecimal('Infinity')}, NumberDecimal('Infinity'));
+testOp({$acosh: Infinity}, Infinity);
+
+testOp({$asinh: NumberDecimal('Infinity')}, NumberDecimal('Infinity'));
+testOp({$asinh: NumberDecimal('-Infinity')}, NumberDecimal('-Infinity'));
+testOp({$asinh: Infinity}, Infinity);
+testOp({$asinh: -Infinity}, -Infinity);
+testOp({$cosh: NumberDecimal('Infinity')}, NumberDecimal('Infinity'));
+testOp({$cosh: NumberDecimal('-Infinity')}, NumberDecimal('Infinity'));
+testOp({$cosh: Infinity}, Infinity);
+testOp({$cosh: -Infinity}, Infinity);
+testOp({$sinh: NumberDecimal('Infinity')}, NumberDecimal('Infinity'));
+testOp({$sinh: NumberDecimal('-Infinity')}, NumberDecimal('-Infinity'));
+testOp({$sinh: Infinity}, Infinity);
+testOp({$sinh: -Infinity}, -Infinity);
// Infinity produces finite output (due to asymptotic bounds).
-testOpApprox("$atan", NumberDecimal('Infinity'), NumberDecimal(Math.PI / 2));
-testOpApprox("$atan", NumberDecimal('-Infinity'), NumberDecimal(-Math.PI / 2));
-testOpApprox("$atan", Infinity, Math.PI / 2);
-testOpApprox("$atan", -Infinity, -Math.PI / 2);
-
-testOpApprox("$atan2", [NumberDecimal('Infinity'), 0], NumberDecimal(Math.PI / 2));
-testOpApprox("$atan2", [NumberDecimal('-Infinity'), 0], NumberDecimal(-Math.PI / 2));
-testOpApprox(
- "$atan2", [NumberDecimal('-Infinity'), NumberDecimal("Infinity")], NumberDecimal(-Math.PI / 4));
-testOpApprox("$atan2",
- [NumberDecimal('-Infinity'), NumberDecimal("-Infinity")],
+testOpApprox({$atan: NumberDecimal('Infinity')}, NumberDecimal(Math.PI / 2));
+testOpApprox({$atan: NumberDecimal('-Infinity')}, NumberDecimal(-Math.PI / 2));
+testOpApprox({$atan: Infinity}, Math.PI / 2);
+testOpApprox({$atan: -Infinity}, -Math.PI / 2);
+
+testOpApprox({$atan2: [NumberDecimal('Infinity'), 0]}, NumberDecimal(Math.PI / 2));
+testOpApprox({$atan2: [NumberDecimal('-Infinity'), 0]}, NumberDecimal(-Math.PI / 2));
+testOpApprox({$atan2: [NumberDecimal('-Infinity'), NumberDecimal("Infinity")]},
+ NumberDecimal(-Math.PI / 4));
+testOpApprox({$atan2: [NumberDecimal('-Infinity'), NumberDecimal("-Infinity")]},
NumberDecimal(-3 * Math.PI / 4));
-testOpApprox("$atan2", [NumberDecimal('0'), NumberDecimal("-Infinity")], NumberDecimal(Math.PI));
-testOpApprox("$atan2", [NumberDecimal('0'), NumberDecimal("Infinity")], NumberDecimal(0));
+testOpApprox({$atan2: [NumberDecimal('0'), NumberDecimal("-Infinity")]}, NumberDecimal(Math.PI));
+testOpApprox({$atan2: [NumberDecimal('0'), NumberDecimal("Infinity")]}, NumberDecimal(0));
-testOp("$tanh", NumberDecimal('Infinity'), NumberDecimal('1'));
-testOp("$tanh", NumberDecimal('-Infinity'), NumberDecimal('-1'));
+testOp({$tanh: NumberDecimal('Infinity')}, NumberDecimal('1'));
+testOp({$tanh: NumberDecimal('-Infinity')}, NumberDecimal('-1'));
// Finite input produces infinite outputs.
-testOp("$atanh", NumberDecimal(1), NumberDecimal('Infinity'));
-testOp("$atanh", NumberDecimal(-1), NumberDecimal('-Infinity'));
-testOp("$atanh", 1, Infinity);
-testOp("$atanh", -1, -Infinity);
+testOp({$atanh: NumberDecimal(1)}, NumberDecimal('Infinity'));
+testOp({$atanh: NumberDecimal(-1)}, NumberDecimal('-Infinity'));
+testOp({$atanh: 1}, Infinity);
+testOp({$atanh: -1}, -Infinity);
-testOp("$tanh", Infinity, 1);
-testOp("$tanh", -Infinity, -1);
+testOp({$tanh: Infinity}, 1);
+testOp({$tanh: -Infinity}, -1);
// Int argument out of bounds.
-testErrorCode("$acos", NumberInt(-2), 50989);
-testErrorCode("$acos", NumberInt(2), 50989);
-testErrorCode("$asin", NumberInt(-2), 50989);
-testErrorCode("$asin", NumberInt(2), 50989);
-testErrorCode("$acosh", NumberInt(0), 50989);
-testErrorCode("$atanh", NumberInt(2), 50989);
-testErrorCode("$atanh", NumberInt(-2), 50989);
+assertErrorCode(coll, [{$project: {a: {$acos: NumberInt(-2)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$acos: NumberInt(2)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$asin: NumberInt(-2)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$asin: NumberInt(2)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$acosh: NumberInt(0)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$atanh: NumberInt(2)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$atanh: NumberInt(-2)}}}], 50989);
// Long argument out of bounds.
-testErrorCode("$acos", NumberLong(-2), 50989);
-testErrorCode("$acos", NumberLong(2), 50989);
-testErrorCode("$asin", NumberLong(-2), 50989);
-testErrorCode("$asin", NumberLong(2), 50989);
-testErrorCode("$acosh", NumberLong(0), 50989);
-testErrorCode("$atanh", NumberLong(2), 50989);
-testErrorCode("$atanh", NumberLong(-2), 50989);
+assertErrorCode(coll, [{$project: {a: {$acos: NumberLong(-2)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$acos: NumberLong(2)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$asin: NumberLong(-2)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$asin: NumberLong(2)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$acosh: NumberLong(0)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$atanh: NumberLong(2)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$atanh: NumberLong(-2)}}}], 50989);
// Double argument out of bounds.
-testErrorCode("$acos", -1.1, 50989);
-testErrorCode("$acos", 1.1, 50989);
-testErrorCode("$asin", -1.1, 50989);
-testErrorCode("$asin", 1.1, 50989);
-testErrorCode("$acosh", 0.9, 50989);
-testErrorCode("$atanh", -1.00001, 50989);
-testErrorCode("$atanh", 1.00001, 50989);
+assertErrorCode(coll, [{$project: {a: {$acos: -1.1}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$acos: 1.1}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$asin: -1.1}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$asin: 1.1}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$acosh: 0.9}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$atanh: -1.00001}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$atanh: 1.00001}}}], 50989);
// Decimal argument out of bounds.
-testErrorCode("$acos", NumberDecimal(-1.1), 50989);
-testErrorCode("$acos", NumberDecimal(1.1), 50989);
-testErrorCode("$asin", NumberDecimal(-1.1), 50989);
-testErrorCode("$asin", NumberDecimal(1.1), 50989);
-testErrorCode("$acosh", NumberDecimal(0.9), 50989);
-testErrorCode("$atanh", NumberDecimal(-1.00001), 50989);
-testErrorCode("$atanh", NumberDecimal(1.000001), 50989);
+assertErrorCode(coll, [{$project: {a: {$acos: NumberDecimal(-1.1)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$acos: NumberDecimal(1.1)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$asin: NumberDecimal(-1.1)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$asin: NumberDecimal(1.1)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$acosh: NumberDecimal(0.9)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$atanh: NumberDecimal(-1.00001)}}}], 50989);
+assertErrorCode(coll, [{$project: {a: {$atanh: NumberDecimal(1.000001)}}}], 50989);
// Check NaN is preserved.
["$acos", "$asin", "$atan", "$cos", "$sin", "$tan"].forEach(op => {
- testOp([op], NaN, NaN);
- testOp([op], NumberDecimal(NaN), NumberDecimal(NaN));
+ testOp({[op]: NaN}, NaN);
+ testOp({[op]: NumberDecimal(NaN)}, NumberDecimal(NaN));
// Check the hyperbolic version of each function.
- testOp([op + 'h'], NaN, NaN);
- testOp([op + 'h'], NumberDecimal(NaN), NumberDecimal(NaN));
+ testOp({[op + 'h']: NaN}, NaN);
+ testOp({[op + 'h']: NumberDecimal(NaN)}, NumberDecimal(NaN));
});
["$radiansToDegrees", "$degreesToRadians"].forEach(op => {
- testOp([op], NaN, NaN);
- testOp([op], NumberDecimal(NaN), NumberDecimal(NaN));
- testOp([op], -Infinity, -Infinity);
- testOp([op], NumberDecimal(-Infinity), NumberDecimal(-Infinity));
- testOp([op], Infinity, Infinity);
- testOp([op], NumberDecimal(Infinity), NumberDecimal(Infinity));
+ testOp({[op]: NaN}, NaN);
+ testOp({[op]: NumberDecimal(NaN)}, NumberDecimal(NaN));
+ testOp({[op]: -Infinity}, -Infinity);
+ testOp({[op]: NumberDecimal(-Infinity)}, NumberDecimal(-Infinity));
+ testOp({[op]: Infinity}, Infinity);
+ testOp({[op]: NumberDecimal(Infinity)}, NumberDecimal(Infinity));
});
-testOp("$atan2", [NumberDecimal('NaN'), NumberDecimal('NaN')], NumberDecimal('NaN'));
-testOp("$atan2", [NumberDecimal('NaN'), NumberDecimal('0')], NumberDecimal('NaN'));
-testOp("$atan2", [NumberDecimal('0'), NumberDecimal('NaN')], NumberDecimal('NaN'));
-
-// atan2 additional testing with unknown constants
-testOpApprox("$atan2", [NumberInt(3), NumberInt(2)], NumberDecimal(0.9827937232));
-testOpApprox("$atan2", [NumberInt(621), NumberInt(84)], NumberDecimal(1.4363466632));
+testOp({$atan2: [NumberDecimal('NaN'), NumberDecimal('NaN')]}, NumberDecimal('NaN'));
+testOp({$atan2: [NumberDecimal('NaN'), NumberDecimal('0')]}, NumberDecimal('NaN'));
+testOp({$atan2: [NumberDecimal('0'), NumberDecimal('NaN')]}, NumberDecimal('NaN'));
// Non-numeric input.
-testErrorCode("$acos", "string", 28765);
-testErrorCode("$acosh", "string", 28765);
-testErrorCode("$asin", "string", 28765);
-testErrorCode("$asinh", "string", 28765);
-testErrorCode("$atan", "string", 28765);
-testErrorCode("$atan2", ["string", "string"], 51044);
-testErrorCode("$atan2", ["string", 0.0], 51044);
-testErrorCode("$atan2", [0.0, "string"], 51045);
-testErrorCode("$atanh", "string", 28765);
-testErrorCode("$cos", "string", 28765);
-testErrorCode("$cosh", "string", 28765);
-testErrorCode("$sin", "string", 28765);
-testErrorCode("$sinh", "string", 28765);
-testErrorCode("$tan", "string", 28765);
-testErrorCode("$tanh", "string", 28765);
-testErrorCode("$degreesToRadians", "string", 28765);
-testErrorCode("$radiansToDegrees", "string", 28765);
+assertErrorCode(coll, [{$project: {a: {$acos: "string"}}}], 28765);
+assertErrorCode(coll, [{$project: {a: {$acosh: "string"}}}], 28765);
+assertErrorCode(coll, [{$project: {a: {$asin: "string"}}}], 28765);
+assertErrorCode(coll, [{$project: {a: {$asinh: "string"}}}], 28765);
+assertErrorCode(coll, [{$project: {a: {$atan: "string"}}}], 28765);
+assertErrorCode(coll, [{$project: {a: {$atan2: ["string", "string"]}}}], 51044);
+assertErrorCode(coll, [{$project: {a: {$atan2: ["string", 0.0]}}}], 51044);
+assertErrorCode(coll, [{$project: {a: {$atan2: [0.0, "string"]}}}], 51045);
+assertErrorCode(coll, [{$project: {a: {$atanh: "string"}}}], 28765);
+assertErrorCode(coll, [{$project: {a: {$cos: "string"}}}], 28765);
+assertErrorCode(coll, [{$project: {a: {$cosh: "string"}}}], 28765);
+assertErrorCode(coll, [{$project: {a: {$sin: "string"}}}], 28765);
+assertErrorCode(coll, [{$project: {a: {$sinh: "string"}}}], 28765);
+assertErrorCode(coll, [{$project: {a: {$tan: "string"}}}], 28765);
+assertErrorCode(coll, [{$project: {a: {$tanh: "string"}}}], 28765);
+assertErrorCode(coll, [{$project: {a: {$degreesToRadians: "string"}}}], 28765);
+assertErrorCode(coll, [{$project: {a: {$radiansToDegrees: "string"}}}], 28765);
}());
diff --git a/jstests/aggregation/expressions/internal_key_string_value.js b/jstests/aggregation/expressions/internal_key_string_value.js
deleted file mode 100644
index 71cbb7635c5..00000000000
--- a/jstests/aggregation/expressions/internal_key_string_value.js
+++ /dev/null
@@ -1,199 +0,0 @@
-// Tests the behavior of $_internalKeyStringValue when used in agg expressions.
-
-(function() {
-"use strict";
-
-const coll = db[jsTestName()];
-
-// Testing behavior for basic data types.
-coll.drop();
-let docs = [
- {_id: 0, a: NumberInt(1)},
- {_id: 1, a: NumberLong(2)},
- {_id: 2, a: 3.0},
- {_id: 3, a: NumberDecimal("4.000000")},
- {_id: 4, a: "abc"},
- {_id: 5, a: ISODate("2024-01-01")},
- {_id: 6, a: [1, 2, 3]},
- {_id: 7, a: {b: 1, c: 2, d: 3}},
-];
-assert.commandWorked(coll.insert(docs));
-
-let results =
- coll.aggregate(
- [{$sort: {_id: 1}}, {$addFields: {b: {$_internalKeyStringValue: {input: "$a"}}}}])
- .toArray();
-assert.eq(results,
- [
- {_id: 0, a: NumberInt(1), b: BinData(0, "KwIE")},
- {_id: 1, a: NumberLong(2), b: BinData(0, "KwQE")},
- {_id: 2, a: 3.0, b: BinData(0, "KwYE")},
- {_id: 3, a: NumberDecimal("4.000000"), b: BinData(0, "KwgE")},
- {_id: 4, a: "abc", b: BinData(0, "PGFiYwAE")},
- {_id: 5, a: ISODate("2024-01-01"), b: BinData(0, "eIAAAYzCUfQABA==")},
- {_id: 6, a: [1, 2, 3], b: BinData(0, "UCsCKwQrBgAE")},
- {_id: 7, a: {b: 1, c: 2, d: 3}, b: BinData(0, "Rh5iACsCHmMAKwQeZAArBgAE")},
- ],
- results);
-
-// Testing behavior for same numeric values of different types.
-assert(coll.drop());
-docs = [
- {_id: 0, a: NumberInt(1)},
- {_id: 1, a: NumberLong(1)},
- {_id: 2, a: 1.0},
- {_id: 3, a: NumberDecimal("1.000")},
- {_id: 4, a: NumberDecimal("1.000000")},
-];
-assert.commandWorked(coll.insert(docs));
-
-results = coll.aggregate(
- [{$sort: {_id: 1}}, {$addFields: {b: {$_internalKeyStringValue: {input: "$a"}}}}])
- .toArray();
-assert.eq(docs.length, results.length, results);
-assert(results.every(result => bsonWoCompare(result.b, results[0].b) === 0), results);
-
-// Testing behavior for same numeric values of different types inside object.
-assert(coll.drop());
-docs = [
- {_id: 0, a: {b: NumberInt(1)}},
- {_id: 1, a: {b: 1.0}},
- {_id: 2, a: {b: NumberDecimal("1.000")}},
- {_id: 3, a: {b: NumberDecimal("1.000000")}},
-];
-assert.commandWorked(coll.insert(docs));
-
-results = coll.aggregate(
- [{$sort: {_id: 1}}, {$addFields: {c: {$_internalKeyStringValue: {input: "$a"}}}}])
- .toArray();
-assert.eq(docs.length, results.length, results);
-assert(results.every(result => bsonWoCompare(result.c, results[0].c) === 0), results);
-
-// Testing behavior for close numeric values.
-assert(coll.drop());
-docs = [
- {_id: 0, a: 1},
- {_id: 1, a: 1.00001},
-];
-assert.commandWorked(coll.insert(docs));
-
-results = coll.aggregate(
- [{$sort: {_id: 1}}, {$addFields: {b: {$_internalKeyStringValue: {input: "$a"}}}}])
- .toArray();
-assert.eq(2, results.length, results);
-assert(bsonWoCompare(results[0].b, results[1].b) !== 0, results);
-
-// Testing behavior for large numeric values.
-assert(coll.drop());
-docs = [
- {_id: 0, a: 1e20},
- {_id: 1, a: NumberDecimal("1e21")},
-];
-assert.commandWorked(coll.insert(docs));
-
-results =
- coll.aggregate([
- {$sort: {_id: 1}},
- {
- $addFields:
- {b: {$_internalKeyStringValue: {input: "$a"}}, c: {$toHashedIndexKey: "$a"}}
- }
- ])
- .toArray();
-assert.eq(2, results.length, results);
-assert(bsonWoCompare(results[0].b, results[1].b) !== 0, results);
-// $toHashedIndexKey hashes large numbers greater than 2^63 to the same result.
-assert(bsonWoCompare(results[0].c, results[1].c) === 0, results);
-
-// Testing behavior for strings under case-sensitive collation that doesn't match.
-assert(coll.drop());
-docs = [
- {_id: 0, a: "aAa"},
- {_id: 1, a: "AaA"},
-];
-assert.commandWorked(coll.insert(docs));
-
-results = coll.aggregate([
- {$sort: {_id: 1}},
- {
- $addFields: {
- b: {
- $_internalKeyStringValue:
- {input: "$a", collation: {locale: "en", strength: 3}}
- }
- }
- }
- ])
- .toArray();
-assert.eq(2, results.length, results);
-assert(bsonWoCompare(results[0].b, results[1].b) !== 0, results);
-
-// Testing behavior for strings under case-sensitive collation that matches.
-assert(coll.drop());
-docs = [
- {_id: 0, a: "aAa"},
- {_id: 1, a: "aAa"},
-];
-assert.commandWorked(coll.insert(docs));
-
-results = coll.aggregate([
- {$sort: {_id: 1}},
- {
- $addFields: {
- b: {
- $_internalKeyStringValue:
- {input: "$a", collation: {locale: "en", strength: 3}}
- }
- }
- }
- ])
- .toArray();
-assert.eq(docs.length, results.length, results);
-assert(results.every(result => bsonWoCompare(result.c, results[0].c) === 0), results);
-
-// Testing behavior for strings under case-insensitive collation that doesn't match.
-assert(coll.drop());
-docs = [
- {_id: 0, a: "aAa"},
- {_id: 1, a: "aBa"},
-];
-assert.commandWorked(coll.insert(docs));
-
-results = coll.aggregate([
- {$sort: {_id: 1}},
- {
- $addFields: {
- b: {
- $_internalKeyStringValue:
- {input: "$a", collation: {locale: "en", strength: 1}}
- }
- }
- }
- ])
- .toArray();
-assert.eq(2, results.length, results);
-assert(bsonWoCompare(results[0].b, results[1].b) !== 0, results);
-
-// Testing behavior for strings under case-insensitive collation that matches.
-assert(coll.drop());
-docs = [
- {_id: 0, a: "aAa"},
- {_id: 1, a: "AaA"},
-];
-assert.commandWorked(coll.insert(docs));
-
-results = coll.aggregate([
- {$sort: {_id: 1}},
- {
- $addFields: {
- b: {
- $_internalKeyStringValue:
- {input: "$a", collation: {locale: "en", strength: 1}}
- }
- }
- }
- ])
- .toArray();
-assert.eq(docs.length, results.length, results);
-assert(results.every(result => bsonWoCompare(result.b, results[0].b) === 0), results);
-})();
diff --git a/jstests/aggregation/expressions/subtract.js b/jstests/aggregation/expressions/subtract.js
deleted file mode 100644
index 1be9f731cbb..00000000000
--- a/jstests/aggregation/expressions/subtract.js
+++ /dev/null
@@ -1,81 +0,0 @@
-load("jstests/aggregation/extras/utils.js"); // For assertErrorCode and assertErrMsgContains.
-load("jstests/libs/sbe_assert_error_override.js"); // Override error-code-checking APIs.
-
-// Tests for $subtract aggregation expression
-(function() {
-"use strict";
-
-const coll = db.subtract_coll;
-coll.drop();
-
-assert.commandWorked(coll.insert({_id: 0, lhs: 1, rhs: 1}));
-assert.commandWorked(coll.insert({_id: 1, lhs: -2000000000, rhs: 2000000000}));
-assert.commandWorked(
- coll.insert({_id: 2, lhs: NumberLong(-20000000000), rhs: NumberLong(20000000000)}));
-assert.commandWorked(coll.insert({_id: 3, lhs: 10.5, rhs: 0.5}));
-assert.commandWorked(
- coll.insert({_id: 4, lhs: NumberDecimal("10000.12345"), rhs: NumberDecimal("10.1234")}));
-assert.commandWorked(coll.insert({_id: 5, lhs: new Date(1912392670000), rhs: 70000}));
-assert.commandWorked(
- coll.insert({_id: 6, lhs: new Date(1912392670000), rhs: new Date(1912392600000)}));
-// Doubles are rounded to int64 when subtracted from Date
-assert.commandWorked(coll.insert({_id: 7, lhs: new Date(1683794065002), rhs: 0.5}));
-assert.commandWorked(coll.insert({_id: 8, lhs: new Date(1683794065002), rhs: 1.4}));
-assert.commandWorked(coll.insert({_id: 9, lhs: new Date(1683794065002), rhs: 1.5}));
-assert.commandWorked(coll.insert({_id: 10, lhs: new Date(1683794065002), rhs: 1.7}));
-// Decimals are rounded to int64, when tie rounded to even, when subtracted from Date
-assert.commandWorked(
- coll.insert({_id: 11, lhs: new Date(1683794065002), rhs: new NumberDecimal("1.4")}));
-assert.commandWorked(
- coll.insert({_id: 12, lhs: new Date(1683794065002), rhs: new NumberDecimal("1.5")}));
-assert.commandWorked(
- coll.insert({_id: 13, lhs: new Date(1683794065002), rhs: new NumberDecimal("1.7")}));
-assert.commandWorked(
- coll.insert({_id: 14, lhs: new Date(1683794065002), rhs: new NumberDecimal("2.5")}));
-
-const result =
- coll.aggregate([{$project: {diff: {$subtract: ["$lhs", "$rhs"]}}}, {$sort: {_id: 1}}])
- .toArray();
-assert.eq(result[0].diff, 0);
-assert.eq(result[1].diff, NumberLong("-4000000000"));
-assert.eq(result[2].diff, NumberLong("-40000000000"));
-assert.eq(result[3].diff, 10.0);
-assert.eq(result[4].diff, NumberDecimal("9990.00005"));
-assert.eq(result[5].diff, new Date(1912392600000));
-assert.eq(result[6].diff, 70000);
-assert.eq(result[7].diff, new Date(1683794065001));
-assert.eq(result[8].diff, new Date(1683794065001));
-assert.eq(result[9].diff, new Date(1683794065000));
-assert.eq(result[10].diff, new Date(1683794065000));
-assert.eq(result[11].diff, new Date(1683794065001));
-assert.eq(result[12].diff, new Date(1683794065000));
-assert.eq(result[13].diff, new Date(1683794065000));
-assert.eq(result[14].diff, new Date(1683794065000));
-
-// Following cases will report overflow error
-coll.drop();
-
-assert.commandWorked(coll.insert([{
- _id: 0,
- veryBigNegativeLong: NumberLong("-9223372036854775808"),
- veryBigNegativeDouble: -9223372036854775808,
- veryBigNegativeDecimal: NumberDecimal("-9223372036854775808"),
- doubleNaN: NaN,
- decimalNaN: NumberDecimal("NaN"),
-}]));
-
-let pipeline = [{$project: {res: {$subtract: [new Date(10), "$veryBigNegativeLong"]}}}];
-assertErrCodeAndErrMsgContains(coll, pipeline, ErrorCodes.Overflow, "date overflow");
-
-pipeline = [{$project: {res: {$subtract: [new Date(10), "$veryBigNegativeDouble"]}}}];
-assertErrCodeAndErrMsgContains(coll, pipeline, ErrorCodes.Overflow, "date overflow");
-
-pipeline = [{$project: {res: {$subtract: [new Date(10), "$veryBigNegativeDecimal"]}}}];
-assertErrCodeAndErrMsgContains(coll, pipeline, ErrorCodes.Overflow, "date overflow");
-
-pipeline = [{$project: {res: {$subtract: [new Date(-1), "$doubleNaN"]}}}];
-assertErrCodeAndErrMsgContains(coll, pipeline, ErrorCodes.Overflow, "date overflow");
-
-pipeline = [{$project: {res: {$subtract: [new Date(-1), "$decimalNaN"]}}}];
-assertErrCodeAndErrMsgContains(coll, pipeline, ErrorCodes.Overflow, "date overflow");
-}());
diff --git a/jstests/aggregation/expressions/switch_errors.js b/jstests/aggregation/expressions/switch_errors.js
index 17ccd53f9d5..9701bc4b019 100644
--- a/jstests/aggregation/expressions/switch_errors.js
+++ b/jstests/aggregation/expressions/switch_errors.js
@@ -60,26 +60,9 @@ pipeline = {
};
assertErrorCode(coll, pipeline, 40068, "$switch requires at least one branch");
-assert.commandWorked(coll.insert({x: 1}));
+coll.insert({x: 1});
pipeline = {
"$project": {"output": {"$switch": {"branches": [{"case": {"$eq": ["$x", 0]}, "then": 1}]}}}
};
assertErrorCode(coll, pipeline, 40066, "$switch has no default and an input matched no case");
-
-// This query was designed to reproduce SERVER-70190. The first branch of the $switch can be
-// optimized away and the $ifNull can be optimized to 2. If the field "x" exists in the input
-// document and is truthy, then the expression should return 2. Otherwise it should throw because no
-// case statement matched and there is no "default" expression.
-pipeline = [{
- $sortByCount: {
- $switch: {
- branches:
- [{case: {$literal: false}, then: 1}, {case: "$x", then: {$ifNull: [2, "$y"]}}]
- }
- }
-}];
-assert.eq([{"_id": 2, "count": 1}], coll.aggregate(pipeline).toArray());
-assert.commandWorked(coll.remove({x: 1}));
-assert.commandWorked(coll.insert({z: 1}));
-assertErrorCode(coll, pipeline, 40066, "$switch has no default and an input matched no case");
}());
diff --git a/jstests/aggregation/extras/utils.js b/jstests/aggregation/extras/utils.js
index febd852c253..6face031533 100644
--- a/jstests/aggregation/extras/utils.js
+++ b/jstests/aggregation/extras/utils.js
@@ -342,19 +342,6 @@ function assertErrCodeAndErrMsgContains(coll, pipe, code, expectedMessage) {
}
/**
- * Assert that an aggregation ran on admin DB fails with a specific code and the error message
- * contains the given string. Note that 'code' can be an array of possible codes.
- */
-function assertAdminDBErrCodeAndErrMsgContains(coll, pipe, code, expectedMessage) {
- const response = assert.commandFailedWithCode(
- coll.getDB().adminCommand({aggregate: 1, pipeline: pipe, cursor: {}}), code);
- assert.neq(
- -1,
- response.errmsg.indexOf(expectedMessage),
- "Error message did not contain '" + expectedMessage + "', found:\n" + tojson(response));
-}
-
-/**
* Assert that an aggregation fails with any code and the error message contains the given
* string.
*/
diff --git a/jstests/aggregation/group_by_objectid.js b/jstests/aggregation/group_by_objectid.js
deleted file mode 100644
index 4d7b638b6e5..00000000000
--- a/jstests/aggregation/group_by_objectid.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// Tests that $group aggregation works with group key of type ObjectId.
-// @tags: [
-// # Some in memory variants will error because this test uses too much memory. As such, we do not
-// # run this test on in-memory variants.
-// requires_persistence,
-// ]
-const collName = jsTestName();
-const coll = db[collName];
-coll.drop();
-
-const bigStr = Array(100 * 1000).toString(); // ~ 100KB of ','
-const bigStr2 = bigStr + "2";
-const nDocs = 1000;
-const nGroups = 10;
-
-let objectIds = [];
-for (let i = 0; i < nGroups; i++) {
- objectIds.push(new ObjectId());
-}
-
-const bulk = coll.initializeUnorderedBulkOp();
-for (let i = 1; i <= nDocs; i++) {
- bulk.insert({b: objectIds[i % nGroups], bigStr: bigStr, b2: bigStr2, c: i});
-}
-assert.commandWorked(bulk.execute());
-
-const pipeline = [
- {
- $sort: {
- "c": NumberInt(-1),
- }
- },
- {$group: {"_id": "$b", "doc": {"$first": "$$ROOT"}}},
-];
-
-assert.commandWorked(db.runCommand({aggregate: collName, pipeline: pipeline, cursor: {}}));
diff --git a/jstests/aggregation/group_conversion_to_distinct_scan.js b/jstests/aggregation/group_conversion_to_distinct_scan.js
index ba2f55ec64b..95a55fec612 100644
--- a/jstests/aggregation/group_conversion_to_distinct_scan.js
+++ b/jstests/aggregation/group_conversion_to_distinct_scan.js
@@ -42,7 +42,7 @@ function createIndexes() {
}
createIndexes();
-const documents = [
+assert.commandWorked(coll.insert([
{_id: 0, a: 1, b: 1, c: 1},
{_id: 1, a: 1, b: 2, c: 2},
{_id: 2, a: 1, b: 2, c: 3},
@@ -71,8 +71,7 @@ const documents = [
{_id: 21, str: "FoO", d: 2},
{_id: 22, str: "bar", d: 4},
{_id: 23, str: "bAr", d: 3}
-];
-assert.commandWorked(coll.insert(documents));
+]));
// Helper for dropping an index and removing it from the list of indexes.
function removeIndex(pattern) {
@@ -742,24 +741,4 @@ assertResultsMatchWithAndWithoutHintandIndexes(
explain = coll.explain().aggregate(pipeline, collationOption);
assert.neq(null, getAggPlanStage(explain, "DISTINCT_SCAN"), explain);
assert.eq({str: 1, d: 1}, getAggPlanStage(explain, "DISTINCT_SCAN").keyPattern);
-
-//
-// Verify that a $sort-$_internalStreamingGroup pipeline can use DISTINCT_SCAN
-//
-pipeline = [
- {$sort: {_id: 1}},
- {$_internalStreamingGroup: {_id: "$_id", value: {$first: "$a"}, $monotonicIdFields: ["_id"]}}
-];
-const expectedResult = [];
-for (let i = 0; i <= 23; i++) {
- let resultDocument = {_id: i, value: null};
- if (documents[i].hasOwnProperty("a")) {
- resultDocument["value"] = documents[i].a;
- }
- expectedResult.push(resultDocument);
-}
-assertResultsMatchWithAndWithoutHintandIndexes(pipeline, expectedResult);
-explain = coll.explain().aggregate(pipeline);
-assert.neq(null, getAggPlanStage(explain, "DISTINCT_SCAN"), explain);
-assert.eq({_id: 1}, getAggPlanStage(explain, "DISTINCT_SCAN").keyPattern);
}());
diff --git a/jstests/aggregation/ifnull.js b/jstests/aggregation/ifnull.js
index 86d4ad2f85d..f147111a259 100644
--- a/jstests/aggregation/ifnull.js
+++ b/jstests/aggregation/ifnull.js
@@ -15,8 +15,7 @@ assert.commandWorked(t.insertOne({
my_null: null,
my_undefined: undefined,
my_obj: {},
- my_list: [],
- my_nested: {zero: 0, null: null, undefined: undefined}
+ my_list: []
}));
function assertError(expectedErrorCode, ifNullSpec) {
@@ -24,12 +23,8 @@ function assertError(expectedErrorCode, ifNullSpec) {
}
function assertResult(expectedResult, ifNullSpec) {
- let res = t.aggregate({$project: {_id: 0, a: {$ifNull: ifNullSpec}}}).toArray()[0];
+ const res = t.aggregate({$project: {_id: 0, a: {$ifNull: ifNullSpec}}}).toArray()[0];
assert.docEq({a: expectedResult}, res);
- res = t.aggregate({
- $group: {_id: 0, a: {$push: {$let: {vars: {x: {$ifNull: ifNullSpec}}, in : "$$x"}}}}
- }).toArray()[0];
- assert.docEq({_id: 0, a: [expectedResult]}, res);
}
// Wrong number of args.
@@ -38,7 +33,6 @@ assertError(1257300, ['$one']);
assertError(1257300, ['$my_null']);
// First arg non null.
-assertResult(0, ['$my_nested.zero', '$one']);
assertResult(1, ['$one', '$two']);
assertResult(2, ['$two', '$one']);
assertResult(false, ['$my_false', '$one']);
@@ -54,7 +48,6 @@ assertResult(1, ['$one', '$two', null]);
assertResult(2, ['$two', '$my_undefined', null]);
// First arg null.
-assertResult(0, ['$my_nested.null', '$my_nested.zero']);
assertResult(2, ['$my_null', '$two']);
assertResult(1, ['$my_null', '$one']);
assertResult(null, ['$my_null', '$my_null']);
@@ -66,7 +59,6 @@ assertResult(null, ['$my_null', '$my_null', null]);
assertResult(undefined, ['$my_null', '$my_null', undefined]);
// First arg undefined.
-assertResult(0, ['$my_nested.undefined', '$my_nested.zero']);
assertResult(2, ['$my_undefined', '$two']);
assertResult(1, ['$my_undefined', '$one']);
assertResult(null, ['$my_undefined', '$my_null']);
diff --git a/jstests/aggregation/large_bson_mid_pipeline.js b/jstests/aggregation/large_bson_mid_pipeline.js
deleted file mode 100644
index 0604bff86c2..00000000000
--- a/jstests/aggregation/large_bson_mid_pipeline.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Tests that extra-large BSON objects (>16MB) can be materialized for the '$match' stage in the
- * middle of the query plan without throwing 'BSONObjectTooLarge' exception.
- */
-(function() {
-"use strict";
-
-load("jstests/libs/analyze_plan.js"); // For 'getAggPlanStage()'.
-
-const testDB = db.getSiblingDB("jsTestName");
-assert.commandWorked(testDB.dropDatabase());
-
-const coll = testDB.coll;
-const largeString = 'x'.repeat(10 * 1024 * 1024);
-assert.commandWorked(coll.insert({a: 1, b: largeString}));
-
-// Use '$addFields' to create extra-large documents in the middle of the pipeline followed by
-// '$match'. Use '$_internalInhibitOptimization' to ensure '$match' is not removed by the optimizer.
-const pipeline = [
- {$_internalInhibitOptimization: {}},
- {$addFields: {c: {$concat: ["$b", "-"]}}},
- {$match: {c: {$exists: true}}},
- {$project: {a: 1}}
-];
-
-assert.doesNotThrow(() => coll.aggregate(pipeline).toArray());
-})();
diff --git a/jstests/aggregation/match_no_swap_rand.js b/jstests/aggregation/match_no_swap_rand.js
deleted file mode 100644
index 508056e97f4..00000000000
--- a/jstests/aggregation/match_no_swap_rand.js
+++ /dev/null
@@ -1,164 +0,0 @@
-/**
- * Test that $rand (and by extension, $sampleRate) doesn't get pushed down.
- * @tags: [
- * # Tests the 'stages' field of the explain output which is hidden beneath each shard's name when
- * # run against sharded collections.
- * assumes_unsharded_collection,
- * # Tests the explain output, so does not work when wrapped in a facet.
- * do_not_wrap_aggregations_in_facets,
- * # Explicitly testing optimization.
- * requires_pipeline_optimization,
- * ]
- */
-(function() {
-"use strict";
-
-load("jstests/libs/analyze_plan.js");
-load("jstests/libs/feature_flag_util.js");
-
-function getWinningPlanForPipeline({coll, pipeline}) {
- const explain = assert.commandWorked(coll.explain().aggregate(pipeline));
- if ("queryPlanner" in explain) {
- return getWinningPlan(explain.queryPlanner);
- }
- return getWinningPlan(explain.stages[0].$cursor.queryPlanner);
-}
-
-function assertScanFilterEq({coll, pipeline, filter}) {
- const winningPlan = getWinningPlanForPipeline({coll, pipeline});
- const collScan = getPlanStage(winningPlan, "COLLSCAN");
- assert(collScan);
- // Sometimes explain will have 'filter' set to an empty object, other times there will be no
- // 'filter'. If we are expecting there to be no filter on the COLLSCAN, either is acceptable.
- if (filter) {
- assert.docEq(collScan.filter, filter);
- } else {
- assert(!collScan.filter || Object.keys(collScan.filter).length == 0);
- }
-}
-
-// Test that a $match with a random expression should not be pushed past a $group.
-{
- const coll = db[jsTestName()];
- coll.drop();
-
- let bulk = coll.initializeUnorderedBulkOp();
- for (var i = 0; i < 100; i++) {
- bulk.insert({a: {b: i % 5, c: i}});
- }
- assert.commandWorked(bulk.execute());
-
- assertScanFilterEq({
- coll,
- pipeline:
- [{$group: {_id: "$a.b", first: {$first: "$$CURRENT"}}}, {$match: {$sampleRate: 0.25}}]
- });
-
- assertScanFilterEq({
- coll,
- pipeline: [
- {$group: {_id: "$a.b", first: {$first: "$$CURRENT"}}},
- {$replaceRoot: {newRoot: '$first'}},
- {$match: {$sampleRate: 0.25}},
- ]
- });
-
- assertScanFilterEq({
- coll,
- pipeline: [
- {$group: {_id: "$a.b", first: {$first: "$$CURRENT"}}},
- {$match: {$sampleRate: 0.25}},
- {$replaceRoot: {newRoot: '$first'}},
- ]
- });
-
- assertScanFilterEq({
- coll,
- pipeline: [
- {$match: {c: {$gt: 500}}},
- {$group: {_id: "$a.b", first: {$first: "$$CURRENT"}}},
- {$match: {$sampleRate: 0.25}},
- ],
- filter: {c: {$gt: 500}}
- });
-
- assertScanFilterEq({
- coll,
- pipeline: [
- {$match: {c: {$gt: 500}}},
- // A $lookup that split $match exprs can push down past.
- {
- $lookup: {
- as: "joinedC",
- from: coll.getName(),
- localField: "c",
- foreignField: "c",
- }
- },
- {
- $match: {
- $and: [
- {$expr: {$lt: ["$c", 800]}}, // Should split me out.
- {$expr: {$lt: [{$rand: {}}, {$const: 0.25}]}} // Can't split me out.
- ]
- }
- },
- ],
- filter: {
- $and: [
- {c: {$gt: 500}},
- {$expr: {$lt: ["$c", {$const: 800}]}},
- {c: {$_internalExprLt: 800}},
- ]
- }
- });
-}
-
-// Test that a $match with a random expression should not be pushed past $_internalUnpackBucket.
-{
- const collName = jsTestName() + "_ts";
- db[collName].drop();
- assert.commandWorked(db.createCollection(collName, {
- timeseries: {
- timeField: "t",
- metaField: "m",
- }
- }));
- const coll = db[collName];
-
- let bulk = coll.initializeUnorderedBulkOp();
- for (var i = 0; i < 100; i++) {
- bulk.insert({t: new Date(), m: i});
- }
- assert.commandWorked(bulk.execute());
-
- assertScanFilterEq({
- coll,
- pipeline: [
- {$match: {$sampleRate: 0.25}},
- ]
- });
-
- if (!FeatureFlagUtil.isEnabled(db, "TimeseriesScalabilityImprovements")) {
- assertScanFilterEq({
- coll,
- pipeline: [
- {
- $match: {
- $and: [
- {$expr: {$lt: ["$m", 50]}}, // Should split me out.
- {$expr: {$lt: [{$rand: {}}, {$const: 0.25}]}} // Can't split me out.
- ]
- }
- },
- ],
- filter: {
- $and: [
- {$expr: {$lt: ["$meta", {$const: 50}]}},
- {meta: {$_internalExprLt: 50}},
- ]
- }
- });
- }
-}
-}());
diff --git a/jstests/aggregation/optimize_away_pipeline.js b/jstests/aggregation/optimize_away_pipeline.js
index d7aa2ce0b15..cd5ba0a5258 100644
--- a/jstests/aggregation/optimize_away_pipeline.js
+++ b/jstests/aggregation/optimize_away_pipeline.js
@@ -656,9 +656,6 @@ assertProjectionIsNotRemoved([{$project: {a: 1}}, {$group: {_id: "$a", s: {$sum:
assertProjectionIsNotRemoved(
[{$project: {a: 1, b: 1}}, {$group: {_id: "$a.b", s: {$sum: "$b.c"}}}]);
-// Test that an inclusion projection is NOT optimized away if group depends on the entire document.
-assertProjectionIsNotRemoved([{$project: {a: 1}}, {$group: {_id: "$$ROOT"}}]);
-
// Spinoff on the one above: Without supporting this kind of prefixing analysis, we can confuse
// ourselves with our dependency analysis. If the $group depends on both "path" and "path.subpath"
// then it will generate a $project on only "path" to express its dependency set. We then fail to
diff --git a/jstests/aggregation/sources/densify/explicit_range.js b/jstests/aggregation/sources/densify/explicit_range.js
index a2c2edd92f7..842ae79d5bf 100644
--- a/jstests/aggregation/sources/densify/explicit_range.js
+++ b/jstests/aggregation/sources/densify/explicit_range.js
@@ -16,77 +16,66 @@ coll.drop();
// Run all tests for each date unit and on numeric values.
for (let i = 0; i < densifyUnits.length; i++) {
- coll.drop();
-
const unit = densifyUnits[i];
+ coll.drop();
const base = unit ? new ISODate("2021-01-01") : 0;
const {add} = getArithmeticFunctionsForUnit(unit);
- const getBounds = (lower, upper) => {
- return [add(base, lower), add(base, upper)];
- };
+ const runDensifyRangeTest = ({step, bounds}, msg) => testDensifyStage({
+ field: "val",
+ range: {step, bounds: [add(base, bounds[0]), add(base, bounds[1])], unit: unit}
+ },
+ coll,
+ msg);
// Run all tests for different step values.
for (let i = 0; i < interestingSteps.length; i++) {
const step = interestingSteps[i];
-
// Generate documents in an empty collection.
- let stage = {field: "val", range: {step: step, bounds: getBounds(0, 10), unit: unit}};
- testDensifyStage(stage, coll);
+ runDensifyRangeTest({step, bounds: [0, 10]});
// Fill in some documents between existing docs.
coll.drop();
coll.insert({val: base});
- coll.insert({val: add(base, 30)});
- // Checking that the upper bound is exclusive.
- stage = {field: "val", range: {step: step, bounds: getBounds(10, 25), unit: unit}};
- testDensifyStage(stage, coll);
+ coll.insert({val: add(base, 99)});
+ runDensifyRangeTest(
+ {step, bounds: [10, 25]}); // Checking that the upper bound is exclusive.
// Fill in odd documents.
coll.drop();
- insertDocumentsOnStep({base, min: 2, max: 11, step: 2, addFunc: add, coll: coll});
-
- stage = {field: "val", range: {step: step, bounds: getBounds(1, 12), unit: unit}};
- testDensifyStage(stage, coll);
- stage = {field: "val", range: {step: step, bounds: getBounds(1, 11), unit: unit}};
- testDensifyStage(stage, coll);
- stage = {field: "val", range: {step: step, bounds: getBounds(1, 10), unit: unit}};
- testDensifyStage(stage, coll);
+ insertDocumentsOnStep({base, min: 2, max: 21, step: 2, addFunc: add, coll: coll});
+ runDensifyRangeTest({step, bounds: [1, 22]});
+ runDensifyRangeTest({step, bounds: [1, 21]});
+ runDensifyRangeTest({step, bounds: [1, 20]});
// Negative numbers.
coll.drop();
- insertDocumentsOnStep({base, min: -20, max: -1, step: 2, addFunc: add, coll: coll});
- stage = {field: "val", range: {step: step, bounds: getBounds(-10, -1), unit: unit}};
- testDensifyStage(stage, coll);
- stage = {field: "val", range: {step: step, bounds: getBounds(-10, 0), unit: unit}};
- testDensifyStage(stage, coll);
- stage = {field: "val", range: {step: step, bounds: getBounds(-10, -2), unit: unit}};
- testDensifyStage(stage, coll);
+ insertDocumentsOnStep({base, min: -100, max: -1, step: 2, addFunc: add, coll: coll});
+ runDensifyRangeTest({step, bounds: [-40, -5]});
+ runDensifyRangeTest({step, bounds: [-60, 0]});
+ runDensifyRangeTest({step, bounds: [-40, -6]});
// Extend range past collection.
coll.drop();
- insertDocumentsOnStep({base, min: 0, max: 10, step: 3, addFunc: add, coll: coll});
- stage = {field: "val", range: {step: step, bounds: getBounds(5, 15), unit: unit}};
- testDensifyStage(stage, coll);
+ insertDocumentsOnStep({base, min: 0, max: 50, step: 3, addFunc: add, coll: coll});
+ runDensifyRangeTest({step, bounds: [30, 75]});
// Start range before collection.
coll.drop();
- insertDocumentsOnStep({base, min: 20, max: 30, step: 2, addFunc: add, coll: coll});
- stage = {field: "val", range: {step: step, bounds: getBounds(10, 25), unit: unit}};
- testDensifyStage(stage, coll);
+ insertDocumentsOnStep({base, min: 20, max: 40, step: 2, addFunc: add, coll: coll});
+ runDensifyRangeTest({step, bounds: [10, 25]});
// Extend range in both directions past collection bounds.
coll.drop();
- insertDocumentsOnStep({base, min: 20, max: 30, step: 2, addFunc: add, coll: coll});
- stage = {field: "val", range: {step: step, bounds: getBounds(10, 35), unit: unit}};
- testDensifyStage(stage, coll);
+ insertDocumentsOnStep({base, min: 20, max: 40, step: 2, addFunc: add, coll: coll});
+ runDensifyRangeTest({step, bounds: [10, 45]});
// Different off-step documents.
coll.drop();
insertDocumentsOnPredicate(
- {base, min: 0, max: 25, pred: i => i % 3 == 0 || i % 7 == 0, addFunc: add, coll: coll});
- stage = {field: "val", range: {step: step, bounds: getBounds(10, 20), unit: unit}};
- testDensifyStage(stage, coll);
+ {base, min: 0, max: 50, pred: i => i % 3 == 0 || i % 7 == 0, addFunc: add, coll: coll});
+
+ runDensifyRangeTest({step, bounds: [10, 45]});
// Lots of off-step documents with nulls sprinkled in to confirm that a null value is
// treated the same as a missing value.
@@ -107,33 +96,12 @@ for (let i = 0; i < densifyUnits.length; i++) {
insertDocumentsOnPredicate({
base,
min: 20,
- max: 30,
+ max: 50,
pred: i => i % 3 == 0 || i % 7 == 0,
addFunc: add,
coll: coll
});
- stage = {field: "val", range: {step: step, bounds: getBounds(10, 25), unit: unit}};
- testDensifyStage(stage, coll);
+ runDensifyRangeTest({step, bounds: [10, 45]});
}
}
-
-// Run a test where there are no documents in the range to ensure we don't generate anything before
-// the range.
-coll.drop();
-let documents = [
- {"date": ISODate("2022-10-29T23:00:00Z")},
-];
-coll.insert(documents);
-let stage = {
- field: "date",
- range: {
- step: 1,
- unit: "month",
- bounds: [
- ISODate("2022-10-31T23:00:00.000Z"),
- ISODate("2022-11-30T23:00:00.000Z"),
- ],
- },
-};
-testDensifyStage(stage, coll, "Ensure no docs before range");
})();
diff --git a/jstests/aggregation/sources/densify/full_range.js b/jstests/aggregation/sources/densify/full_range.js
index f84a8e7fe5a..66f13540019 100644
--- a/jstests/aggregation/sources/densify/full_range.js
+++ b/jstests/aggregation/sources/densify/full_range.js
@@ -16,33 +16,33 @@ coll.drop();
// Run all tests for each date unit and on numeric values.
for (let i = 0; i < densifyUnits.length; i++) {
- coll.drop();
-
const unit = densifyUnits[i];
+ coll.drop();
const base = unit ? new ISODate("2021-01-01") : 0;
const {add} = getArithmeticFunctionsForUnit(unit);
// Run all tests for different step values.
for (let i = 0; i < interestingSteps.length; i++) {
const step = interestingSteps[i];
- const stage = {field: "val", range: {step: step, bounds: "full", unit: unit}};
+ const runDensifyFullTest = (msg) =>
+ testDensifyStage({field: "val", range: {step, bounds: "full", unit: unit}}, coll, msg);
- // Fill in docs between 1 and 10.
+ // Fill in docs between 1 and 99.
coll.drop();
coll.insert({val: base});
- coll.insert({val: add(base, 10)});
- testDensifyStage(stage, coll);
+ coll.insert({val: add(base, 99)});
+ runDensifyFullTest();
// Negative numbers and dates before the epoch.
coll.drop();
- insertDocumentsOnStep({base, min: -10, max: -1, step: 2, addFunc: add, coll: coll});
- testDensifyStage(stage, coll);
+ insertDocumentsOnStep({base, min: -100, max: -1, step: 2, addFunc: add, coll: coll});
+ runDensifyFullTest();
// Lots of off-step documents.
coll.drop();
insertDocumentsOnPredicate(
- {base, min: 0, max: 10, pred: i => i % 3 == 0 || i % 7 == 0, addFunc: add, coll: coll});
- testDensifyStage(stage, coll);
+ {base, min: 0, max: 50, pred: i => i % 3 == 0 || i % 7 == 0, addFunc: add, coll: coll});
+ runDensifyFullTest();
// Lots of off-step documents with nulls sprinkled in to confirm that a null value is
// treated the same as a missing value.
@@ -69,17 +69,7 @@ for (let i = 0; i < densifyUnits.length; i++) {
coll: coll
});
- testDensifyStage(stage, coll);
+ runDensifyFullTest();
}
}
-
-// Test that full range does not fail if there's only one document in the collection.
-coll.drop();
-coll.insert({_id: 1, val: 1, orig: true});
-let result = coll.aggregate([
- {"$densify": {"field": "val", "range": {"step": 2, "bounds": "full"}}},
-]);
-const expected = [{_id: 1, val: 1, orig: true}];
-const resultArray = result.toArray();
-assert.sameMembers(resultArray, expected);
})();
diff --git a/jstests/aggregation/sources/densify/libs/densify_in_js.js b/jstests/aggregation/sources/densify/libs/densify_in_js.js
index 7a919e9afeb..4c92bd572bc 100644
--- a/jstests/aggregation/sources/densify/libs/densify_in_js.js
+++ b/jstests/aggregation/sources/densify/libs/densify_in_js.js
@@ -2,9 +2,7 @@
* This file implements densification in JavaScript to compare with the output from the $densify
* stage.
*/
-
-const densifyUnits = [null, "millisecond", "second", "day", "month", "quarter", "year"];
-const interestingSteps = [1, 2, 3, 4, 5, 7];
+load("jstests/aggregation/extras/utils.js"); // arrayEq
/**
* The code is made a lot shorter by relying on accessing properties on Date objects with
@@ -14,7 +12,7 @@ const interestingSteps = [1, 2, 3, 4, 5, 7];
* @returns functions to immutably add/subtract a specific duration with a date.
*/
const makeArithmeticHelpers = (unitName, factor) => {
- const getTimeInUnits = date => {
+ const getter = date => {
const newDate = new ISODate(date.toISOString());
// Calling the proper function on the passed in date object. If the unitName was "Seconds"
// would be equivalent to `newDate.getSeconds()`.
@@ -24,16 +22,27 @@ const makeArithmeticHelpers = (unitName, factor) => {
// Return a new date with the proper unit adjusted with the second parameter.
// Dates and the setter helpers are generally mutable, but this function will make sure
// the arithmetic helpers won't mutate their inputs.
- const setTimeInUnits = (date, newComponent) => {
+ const setter = (date, newComponent) => {
const newDate = new ISODate(date.toISOString());
newDate["setUTC" + unitName](newComponent);
return newDate;
};
- const add = (date, step) => setTimeInUnits(date, getTimeInUnits(date) + (step * factor));
- const sub = (date, step) => setTimeInUnits(date, getTimeInUnits(date) - (step * factor));
+ const add = (val, step) => setter(val, getter(val) + (step * factor));
+ const sub = (val, step) => setter(val, getter(val) - (step * factor));
+
+ // Explicit ranges always generate on-step relative to the lower-bound of the range,
+ // this function encapsulates the logic to do that for dates (requires a loop since steps aren't
+ // always constant sized).
+ const getNextStepFromBase = (val, base, step) => {
+ let nextStep = base;
+ while (nextStep <= val) {
+ nextStep = add(nextStep, step);
+ }
+ return nextStep;
+ };
- return {add: add, sub: sub};
+ return {add: add, sub: sub, getNextStepFromBase: getNextStepFromBase};
};
/**
@@ -46,7 +55,7 @@ const getArithmeticFunctionsForUnit = (unitName) => {
case "millisecond":
return makeArithmeticHelpers("Milliseconds", 1);
case "second":
- return makeArithmeticHelpers("Seconds", 1);
+ return makeArithmeticHelpers("Milliseconds", 1000);
case "minute":
return makeArithmeticHelpers("Minutes", 1);
case "hour":
@@ -63,7 +72,17 @@ const getArithmeticFunctionsForUnit = (unitName) => {
return makeArithmeticHelpers("FullYear", 1);
case null: // missing unit means that we're dealing with numbers rather than dates.
case undefined:
- return {add: (val, step) => val + step, sub: (val, step) => val - step};
+ return {
+ add: (val, step) => val + step,
+ sub: (val, step) => val - step,
+ getNextStepFromBase: (val, base, step) => {
+ let nextStep = base;
+ while (nextStep <= val) {
+ nextStep = nextStep + step;
+ }
+ return nextStep;
+ }
+ };
}
};
@@ -87,7 +106,7 @@ function densifyInJS(stage, docs) {
});
const docsWithoutNulls = docs.filter(doc => doc[field] != null);
- const {add, sub} = getArithmeticFunctionsForUnit(unit);
+ const {add, sub, getNextStepFromBase} = getArithmeticFunctionsForUnit(unit);
function generateDocuments(min, max, pred) {
const docs = [];
@@ -100,25 +119,15 @@ function densifyInJS(stage, docs) {
return docs;
}
- // Explicit ranges always generate on-step relative to the lower-bound of the range,
- // this function encapsulates the logic to do that for dates (requires a loop since steps aren't
- // always constant sized).
- const getNextStepFromBase = (val, base, step) => {
- let nextStep = base;
- while (nextStep <= val) {
- nextStep = add(nextStep, step);
- }
- return nextStep;
- };
-
if (bounds === "full") {
if (docs.length == 0) {
return stream;
}
const minValue = docsWithoutNulls[0][field];
const maxValue = docsWithoutNulls[docsWithoutNulls.length - 1][field];
- return densifyInJS({field: stage.field, range: {step, bounds: [minValue, maxValue], unit}},
+ return densifyInJS({field: stage.field, range: {step, unit, bounds: [minValue, maxValue]}},
docs);
+
} else if (bounds === "partition") {
throw new Error("Partitioning not supported by JS densify.");
} else if (bounds.length == 2) {
@@ -169,17 +178,20 @@ const insertDocumentsOnStep = ({base, min, max, step, addFunc, coll, field}) =>
insertDocumentsOnPredicate(
{base, min, max, pred: i => ((i - min) % step) === 0, addFunc, coll, field});
+const densifyUnits = [null, "millisecond", "second", "day", "month", "quarter", "year"];
+
+const interestingSteps = [1, 2, 3, 4, 5, 7, 11, 13];
+
function buildErrorString(found, expected) {
return "Expected:\n" + tojson(expected) + "\nGot:\n" + tojson(found);
}
-// Assert that densification in JavaScript and the $densify stage output the same documents.
-function testDensifyStage(stage, coll, msg = "") {
+function testDensifyStage(stage, coll, msg) {
if (stage.range.unit === null) {
delete stage.range.unit;
}
const result = coll.aggregate([{"$densify": stage}]).toArray();
const expected = densifyInJS(stage, coll.find({}).toArray());
- const newMsg = msg + " | stage: " + tojson(stage);
- assert.sameMembers(expected, result, newMsg + buildErrorString(result, expected));
+ const newMsg = (msg || "") + " | stage: " + tojson(stage);
+ assert(arrayEq(expected, result), newMsg + buildErrorString(result, expected));
}
diff --git a/jstests/aggregation/sources/geonear/requires_geo_index.js b/jstests/aggregation/sources/geonear/requires_geo_index.js
index b56c24b45ef..cd8a75c4e29 100644
--- a/jstests/aggregation/sources/geonear/requires_geo_index.js
+++ b/jstests/aggregation/sources/geonear/requires_geo_index.js
@@ -38,8 +38,8 @@ const geonearWithinLookupPipeline = [
},
];
-assert.commandWorked(coll.insert({_id: 5, x: 5, geo: [1, 1]}));
-assert.commandWorked(from.insert({_id: 1, x: 5, geo: [0, 0]}));
+assert.commandWorked(coll.insert({_id: 5, x: 5}));
+assert.commandWorked(from.insert({_id: 1, geo: [0, 0]}));
// Fail without index.
assertErrorCode(from, geonearPipeline, ErrorCodes.IndexNotFound);
@@ -50,12 +50,4 @@ assert.commandWorked(from.createIndex({geo: "2dsphere"}));
// Run successfully when you have the geospatial index.
assert.eq(from.aggregate(geonearPipeline).itcount(), 1);
assert.eq(coll.aggregate(geonearWithinLookupPipeline).itcount(), 1);
-
-// Test that we can run a pipeline with a $geoNear stage followed by a $lookup.
-const geonearThenLookupPipeline = [
- {$geoNear: {near: [0, 1], distanceField: "distance", spherical: true}},
- {$lookup: {from: from.getName(), localField: "x", foreignField: "x", as: "new"}},
-];
-assert.commandWorked(coll.createIndex({geo: "2dsphere"}));
-assert.eq(coll.aggregate(geonearThenLookupPipeline).itcount(), 1);
}());
diff --git a/jstests/aggregation/sources/graphLookup/filter.js b/jstests/aggregation/sources/graphLookup/filter.js
index 98433c195ba..c43d849a3a5 100644
--- a/jstests/aggregation/sources/graphLookup/filter.js
+++ b/jstests/aggregation/sources/graphLookup/filter.js
@@ -6,8 +6,8 @@
load("jstests/libs/fixture_helpers.js"); // For isSharded.
-let local = db.local;
-let foreign = db.foreign;
+var local = db.local;
+var foreign = db.foreign;
local.drop();
foreign.drop();
@@ -21,15 +21,15 @@ if (FixtureHelpers.isSharded(foreign) && !isShardedLookupEnabled) {
return;
}
-let bulk = foreign.initializeUnorderedBulkOp();
-for (let i = 0; i < 100; i++) {
+var bulk = foreign.initializeUnorderedBulkOp();
+for (var i = 0; i < 100; i++) {
bulk.insert({_id: i, neighbors: [i - 1, i + 1]});
}
assert.commandWorked(bulk.execute());
-assert.commandWorked(local.insert([{starting: 0, foo: 1}, {starting: 1, foo: 2}]));
+assert.commandWorked(local.insert({starting: 0}));
// Assert that the graphLookup only retrieves ten documents, with _id from 0 to 9.
-let res = local
+var res = local
.aggregate({
$graphLookup: {
from: "foreign",
@@ -69,7 +69,7 @@ assert.commandWorked(foreign.insert({from: 2, to: 3, shouldBeIncluded: true}));
// Assert that the $graphLookup stops exploring when it finds a document that doesn't match the
// filter.
res = local
- .aggregate([{
+ .aggregate({
$graphLookup: {
from: "foreign",
startWith: "$starting",
@@ -78,14 +78,14 @@ res = local
as: "results",
restrictSearchWithMatch: {shouldBeIncluded: true}
}
- }, {$match: {starting: 0}}])
- .toArray();
+ })
+ .toArray()[0];
-assert.eq(res[0].results.length, 1, tojson(res));
+assert.eq(res.results.length, 1);
// $expr is allowed inside the 'restrictSearchWithMatch' match expression.
res = local
- .aggregate([{
+ .aggregate({
$graphLookup: {
from: "foreign",
startWith: "$starting",
@@ -94,14 +94,14 @@ res = local
as: "results",
restrictSearchWithMatch: {$expr: {$eq: ["$shouldBeIncluded", true]}}
}
- }, {$match: {starting: 0}}])
- .toArray();
+ })
+ .toArray()[0];
-assert.eq(res[0].results.length, 1, tojson(res));
+assert.eq(res.results.length, 1);
// $expr within `restrictSearchWithMatch` has access to variables declared at a higher level.
res = local
- .aggregate([{$sort: {starting: 1}}, {
+ .aggregate([{
$lookup: {
from: "local",
let : {foo: true},
@@ -115,36 +115,11 @@ res = local
restrictSearchWithMatch:
{$expr: {$eq: ["$shouldBeIncluded", "$$foo"]}}
}
- }, {$sort: {starting: 1}}],
+ }],
as: "array"
}
- }, {$match: {starting: 0}}])
- .toArray();
-
-assert.eq(res[0].array[0].results.length, 1, tojson(res));
-
-// $graphLookup which references a let variable defined by $lookup should be treated as correlated.
-res = local.aggregate([{
- $lookup: {
- from: "local",
- let : {foo: "$foo"},
- pipeline: [{
- $graphLookup: {
- from: "foreign",
- startWith: "$starting",
- connectFromField: "to",
- connectToField: "from",
- as: "results",
- restrictSearchWithMatch:
- {$expr: {$eq: ["$from", "$$foo"]}}
- }
- }],
- as: "array"
- }
-}, {$sort: {starting: 1}}]).toArray();
-assert.eq(2, res.length);
-assert.eq(1, res[1].starting, tojson(res));
-assert.eq(2, res[1].array.length, tojson(res));
-assert.eq(0, res[1].array[0].results.length, tojson(res));
-assert.eq(0, res[1].array[1].results.length, tojson(res));
+ }])
+ .toArray()[0];
+
+assert.eq(res.array[0].results.length, 1);
})();
diff --git a/jstests/aggregation/sources/indexStats/verify_index_stats_output.js b/jstests/aggregation/sources/indexStats/verify_index_stats_output.js
index 15cc007672d..0b2dbeba8f9 100644
--- a/jstests/aggregation/sources/indexStats/verify_index_stats_output.js
+++ b/jstests/aggregation/sources/indexStats/verify_index_stats_output.js
@@ -69,7 +69,6 @@ let shardsFound = [];
db.getSiblingDB("config").shards.find().forEach(function(shard) {
allShards.push(shard._id);
});
-const isShardedCluster = !!allShards.length;
for (const indexStats of pausedOutput) {
assert.hasFields(indexStats, ["building", "spec"]);
@@ -85,8 +84,6 @@ for (const indexStats of pausedOutput) {
// names of known shards.
if (indexStats.hasOwnProperty("shard")) {
shardsFound.push(indexStats["shard"]);
- } else {
- assert(!isShardedCluster);
}
}
diff --git a/jstests/aggregation/sources/lookup/lookup_non_correlated_prefix.js b/jstests/aggregation/sources/lookup/lookup_non_correlated_prefix.js
index a5169ced199..4c497a9d5b6 100644
--- a/jstests/aggregation/sources/lookup/lookup_non_correlated_prefix.js
+++ b/jstests/aggregation/sources/lookup/lookup_non_correlated_prefix.js
@@ -7,8 +7,7 @@
(function() {
"use strict";
-load("jstests/libs/fixture_helpers.js"); // For isSharded.
-load("jstests/aggregation/extras/utils.js"); // for arrayEq
+load("jstests/libs/fixture_helpers.js"); // For isSharded.
const testColl = db.lookup_non_correlated_prefix;
testColl.drop();
@@ -89,39 +88,6 @@ cursor.toArray().forEach(user => {
assert.eq(user['_id'], joinedDocs[0].owner);
});
-// Test for a non-correlated prefix followed by a $facet pipeline that contains a correlated
-// variable reference.
-cursor = testColl.aggregate([
- {
- $lookup: {
- as: 'items_check',
- from: joinColl.getName(),
- let : {id: '$_id'},
- pipeline: [
- {$match: {owner: "user_1"}},
- {
- $facet: {
- all: [{
- $redact: {
- $cond:
- {if: {$eq: ["$$id", "user_1"]}, then: "$$KEEP", else: "$$PRUNE"}
- }
- }],
- },
- },
- ],
- },
- },
-]);
-res = cursor.toArray();
-assert(
- arrayEq(res,
- [
- {"_id": "user_1", "items_check": [{"all": [{"_id": "item_1", "owner": "user_1"}]}]},
- {"_id": "user_2", "items_check": [{"all": []}]}
- ]),
- res);
-
// SERVER-57000: Test handling of lack of correlation (addFields with empty set of columns)
assert.doesNotThrow(() => testColl.aggregate([
{
diff --git a/jstests/aggregation/sources/lookup/lookup_query_stats.js b/jstests/aggregation/sources/lookup/lookup_query_stats.js
index 86574988184..afe418ab555 100644
--- a/jstests/aggregation/sources/lookup/lookup_query_stats.js
+++ b/jstests/aggregation/sources/lookup/lookup_query_stats.js
@@ -236,12 +236,7 @@ let testQueryExecutorStatsWithCollectionScan = function() {
checkExplainOutputForAllVerbosityLevels(
localColl,
fromColl,
- {
- totalDocsExamined: localDocCount * foreignDocCount,
- totalKeysExamined: 0,
- collectionScans: localDocCount,
- indexesUsed: []
- },
+ {totalDocsExamined: 20, totalKeysExamined: 0, collectionScans: 4, indexesUsed: []},
{allowDiskUse: false});
}
};
diff --git a/jstests/aggregation/sources/merge/merge_with_dollar_fields.js b/jstests/aggregation/sources/merge/merge_with_dollar_fields.js
deleted file mode 100644
index b08383fd73c..00000000000
--- a/jstests/aggregation/sources/merge/merge_with_dollar_fields.js
+++ /dev/null
@@ -1,132 +0,0 @@
-// Tests $merge over documents with $-field in it.
-//
-// Sharded collections have special requirements on the join field.
-// @tags: [assumes_unsharded_collection]
-
-(function() {
-"use strict";
-
-load("jstests/libs/collection_drop_recreate.js"); // For assertDropCollection.
-
-const sourceName = 'merge_with_dollar_fields_source';
-const source = db[sourceName];
-const targetName = 'merge_with_dollar_fields_target';
-const target = db[targetName];
-
-const joinField = 'joinField';
-const sourceDoc = {
- $dollar: 1,
- joinField
-};
-const targetDoc = {
- a: 1,
- joinField
-};
-assertDropCollection(db, sourceName);
-assert.commandWorked(source.insert(sourceDoc));
-
-function runTest({whenMatched, whenNotMatched}, targetDocs) {
- assertDropCollection(db, targetName);
- assert.commandWorked(target.createIndex({joinField: 1}, {unique: true}));
- assert.commandWorked(target.insert(targetDocs));
- source.aggregate([
- {$project: {_id: 0}},
- {
- $merge: {
- into: targetName,
- on: joinField,
- whenMatched,
- whenNotMatched,
- }
- }
- ]);
- return target.findOne({}, {_id: 0});
-}
-
-function runTestMatched(mode) {
- return runTest(mode, [targetDoc]);
-}
-
-function runTestNotMatched(mode) {
- return runTest(mode, []);
-}
-
-// TODO: SERVER-76999: Currently $merge may throw 'FailedToParse' error due to non-local updates.
-// We should return consistent results for dollar field documents.
-
-// whenMatched: 'replace', whenNotMatched: 'insert'
-assert.throwsWithCode(() => runTestMatched({whenMatched: 'replace', whenNotMatched: 'insert'}),
- [ErrorCodes.DollarPrefixedFieldName, ErrorCodes.FailedToParse]);
-
-try {
- assert.docEq(sourceDoc, runTestNotMatched({whenMatched: 'replace', whenNotMatched: 'insert'}));
-} catch (error) {
- assert.commandFailedWithCode(error, ErrorCodes.FailedToParse);
-}
-
-// whenMatched: 'replace', whenNotMatched: 'fail'
-assert.throwsWithCode(() => runTestMatched({whenMatched: 'replace', whenNotMatched: 'fail'}),
- [ErrorCodes.DollarPrefixedFieldName, ErrorCodes.FailedToParse]);
-
-assert.throwsWithCode(() => runTestNotMatched({whenMatched: 'replace', whenNotMatched: 'fail'}),
- [ErrorCodes.MergeStageNoMatchingDocument, ErrorCodes.FailedToParse]);
-
-// whenMatched: 'replace', whenNotMatched: 'discard'
-assert.throwsWithCode(() => runTestMatched({whenMatched: 'replace', whenNotMatched: 'discard'}),
- [ErrorCodes.DollarPrefixedFieldName, ErrorCodes.FailedToParse]);
-
-try {
- assert.eq(null, runTestNotMatched({whenMatched: 'replace', whenNotMatched: 'discard'}));
-} catch (error) {
- assert.commandFailedWithCode(error, ErrorCodes.FailedToParse);
-}
-
-// whenMatched: 'merge', whenNotMatched: 'insert'
-assert.throwsWithCode(() => runTestMatched({whenMatched: 'merge', whenNotMatched: 'insert'}),
- ErrorCodes.DollarPrefixedFieldName);
-
-assert.docEq(sourceDoc, runTestNotMatched({whenMatched: 'merge', whenNotMatched: 'insert'}));
-
-// whenMatched: 'merge', whenNotMatched: 'fail'
-assert.throwsWithCode(() => runTestMatched({whenMatched: 'merge', whenNotMatched: 'fail'}),
- ErrorCodes.DollarPrefixedFieldName);
-
-assert.throwsWithCode(() => runTestNotMatched({whenMatched: 'merge', whenNotMatched: 'fail'}),
- ErrorCodes.MergeStageNoMatchingDocument);
-
-// whenMatched: 'merge', whenNotMatched: 'discard'
-assert.throwsWithCode(() => runTestMatched({whenMatched: 'merge', whenNotMatched: 'discard'}),
- ErrorCodes.DollarPrefixedFieldName);
-
-assert.eq(null, runTestNotMatched({whenMatched: 'merge', whenNotMatched: 'discard'}));
-
-// whenMatched: 'keepExisting', whenNotMatched: 'insert'
-assert.docEq(targetDoc, runTestMatched({whenMatched: 'keepExisting', whenNotMatched: 'insert'}));
-
-assert.docEq(sourceDoc, runTestNotMatched({whenMatched: 'keepExisting', whenNotMatched: 'insert'}));
-
-// whenMatched: 'fail', whenNotMatched: 'insert'
-assert.throwsWithCode(() => runTestMatched({whenMatched: 'fail', whenNotMatched: 'insert'}),
- ErrorCodes.DuplicateKey);
-
-assert.docEq(sourceDoc, runTestNotMatched({whenMatched: 'fail', whenNotMatched: 'insert'}));
-
-// whenMatched: 'pipeline', whenNotMatched: 'insert'
-const pipeline = [{$addFields: {b: 1}}];
-const targetDocAddFields = Object.assign({}, targetDoc, {b: 1});
-assert.docEq(targetDocAddFields, runTestMatched({whenMatched: pipeline, whenNotMatched: 'insert'}));
-
-assert.docEq(sourceDoc, runTestNotMatched({whenMatched: pipeline, whenNotMatched: 'insert'}));
-
-// whenMatched: 'pipeline', whenNotMatched: 'fail'
-assert.docEq(targetDocAddFields, runTestMatched({whenMatched: pipeline, whenNotMatched: 'fail'}));
-
-assert.throwsWithCode(() => runTestNotMatched({whenMatched: pipeline, whenNotMatched: 'fail'}),
- ErrorCodes.MergeStageNoMatchingDocument);
-
-// whenMatched: 'pipeline', whenNotMatched: 'discard'
-assert.docEq(targetDocAddFields,
- runTestMatched({whenMatched: pipeline, whenNotMatched: 'discard'}));
-
-assert.eq(null, runTestNotMatched({whenMatched: pipeline, whenNotMatched: 'discard'}));
-}());
diff --git a/jstests/aggregation/sources/merge/mode_merge_fail.js b/jstests/aggregation/sources/merge/mode_merge_fail.js
index fcc13f8e871..7235c8e1c7e 100644
--- a/jstests/aggregation/sources/merge/mode_merge_fail.js
+++ b/jstests/aggregation/sources/merge/mode_merge_fail.js
@@ -97,13 +97,9 @@ const pipeline = [mergeStage];
// and updated.
(function testMergeUnorderedBatchUpdate() {
const maxBatchSize = 16 * 1024 * 1024; // 16MB
-
- // Each document is just under 1MB in order to allow for some extra space for writes that need
- // to be serialized over the wire in certain cluster configurations. Otherwise, the number of
- // modified/unmodified documents would be off by one depending on how our cluster is configured.
- const docSize = 1024 * 1023;
+ const docSize = 1024 * 1024; // 1MB
const numDocs = 20;
- const maxDocsInBatch = Math.floor(maxBatchSize / docSize);
+ const maxDocsInBatch = maxBatchSize / docSize;
assert(source.drop());
dropWithoutImplicitRecreate(target.getName());
diff --git a/jstests/aggregation/sources/merge/mode_replace_fail.js b/jstests/aggregation/sources/merge/mode_replace_fail.js
index cc3df1b1ce7..88582238c8f 100644
--- a/jstests/aggregation/sources/merge/mode_replace_fail.js
+++ b/jstests/aggregation/sources/merge/mode_replace_fail.js
@@ -90,13 +90,9 @@ const pipeline = [mergeStage];
// and updated.
(function testMergeUnorderedBatchUpdate() {
const maxBatchSize = 16 * 1024 * 1024; // 16MB
-
- // Each document is just under 1MB in order to allow for some extra space for writes that need
- // to be serialized over the wire in certain cluster configurations. Otherwise, the number of
- // modified/unmodified documents would be off by one depending on how our cluster is configured.
- const docSize = 1024 * 1023; // 1MB
+ const docSize = 1024 * 1024; // 1MB
const numDocs = 20;
- const maxDocsInBatch = Math.floor(maxBatchSize / docSize);
+ const maxDocsInBatch = maxBatchSize / docSize;
assert(source.drop());
dropWithoutImplicitRecreate(target.getName());
diff --git a/jstests/aggregation/sources/multiple_unpack_bucket_error.js b/jstests/aggregation/sources/multiple_unpack_bucket_error.js
index b00c6265f9b..5c835b5e16e 100644
--- a/jstests/aggregation/sources/multiple_unpack_bucket_error.js
+++ b/jstests/aggregation/sources/multiple_unpack_bucket_error.js
@@ -13,48 +13,36 @@ coll.drop();
assert.commandFailedWithCode(db.runCommand({
aggregate: coll.getName(),
pipeline: [
- {
- $_internalUnpackBucket:
- {exclude: [], timeField: 'time', bucketMaxSpanSeconds: NumberInt(3600)}
- },
- {
- $_internalUnpackBucket:
- {exclude: [], timeField: 'time', bucketMaxSpanSeconds: NumberInt(3600)}
- }
+ {$_internalUnpackBucket: {exclude: [], timeField: 'time', bucketMaxSpanSeconds: 3600}},
+ {$_internalUnpackBucket: {exclude: [], timeField: 'time', bucketMaxSpanSeconds: 3600}}
],
cursor: {}
}),
- 7183900);
+ 5348302);
// $_unpackBucket is an alias of $_internalUnpackBucket, the same restriction should apply.
assert.commandFailedWithCode(db.runCommand({
aggregate: coll.getName(),
pipeline: [
{$_unpackBucket: {timeField: 'time'}},
- {
- $_internalUnpackBucket:
- {exclude: [], timeField: 'time', bucketMaxSpanSeconds: NumberInt(3600)}
- }
+ {$_internalUnpackBucket: {exclude: [], timeField: 'time', bucketMaxSpanSeconds: 3600}}
],
cursor: {}
}),
- 7183900);
+ 5348302);
assert.commandFailedWithCode(db.runCommand({
aggregate: coll.getName(),
pipeline: [
- {
- $_internalUnpackBucket:
- {exclude: [], timeField: 'time', bucketMaxSpanSeconds: NumberInt(3600)}
- },
+ {$_internalUnpackBucket: {exclude: [], timeField: 'time', bucketMaxSpanSeconds: 3600}},
{$_unpackBucket: {timeField: 'time'}}
],
cursor: {}
}),
- 7183900);
+ 5348302);
assert.commandFailedWithCode(db.runCommand({
aggregate: coll.getName(),
pipeline: [{$_unpackBucket: {timeField: 'time'}}, {$_unpackBucket: {timeField: 'time'}}],
cursor: {}
}),
- 7183900);
+ 5348302);
})();
diff --git a/jstests/aggregation/sources/search_stage_error.js b/jstests/aggregation/sources/search_stage_error.js
deleted file mode 100644
index d85631cd999..00000000000
--- a/jstests/aggregation/sources/search_stage_error.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Verify that the $search stage errors correctly if enterprise is not enabled.
- * @tags: [
- * # $search/$searchMeta cannot be used within a facet
- * do_not_wrap_aggregations_in_facets,
- * # $search/$searchMeta do not support any read concern other than "local"
- * assumes_read_concern_unchanged
- * ]
- */
-(function() {
-"use strict";
-
-const coll = db.searchCollector;
-coll.drop();
-const buildInfo = assert.commandWorked(db.runCommand({"buildInfo": 1}));
-if (buildInfo["modules"].includes("enterprise")) {
- // This is a test of behavior without enterprise.
- return;
-}
-assert.commandWorked(coll.insert({"_id": 1, "title": "cakes"}));
-
-// Check that a query with a $search stage errors without enterprise.
-assert.commandFailedWithCode(
- db.runCommand({aggregate: coll.getName(), cursor: {}, pipeline: [{$search: {}}]}), [6047401]);
-
-// Check that a query with a $searchMeta stage errors without enterprise.
-assert.commandFailedWithCode(
- db.runCommand({aggregate: coll.getName(), cursor: {}, pipeline: [{$searchMeta: {}}]}),
- [6047401]);
-
-// Check that a query with a $listSearchIndexes stage errors without enterprise.
-assert.commandFailedWithCode(
- coll.runCommand({aggregate: coll.getName(), pipeline: [{$listSearchIndexes: {}}], cursor: {}}),
- [6047401]);
-
-// Check that a query with a $vectorSearch stage errors without enterprise.
-assert.commandFailedWithCode(
- coll.runCommand({aggregate: coll.getName(), cursor: {}, pipeline: [{$vectorSearch: {}}]}),
- [6047401]);
-})();
diff --git a/jstests/aggregation/sources/setWindowFields/comprehensive_parse.js b/jstests/aggregation/sources/setWindowFields/comprehensive_parse.js
index 4dd4cc90565..dbe509e2c59 100644
--- a/jstests/aggregation/sources/setWindowFields/comprehensive_parse.js
+++ b/jstests/aggregation/sources/setWindowFields/comprehensive_parse.js
@@ -65,7 +65,6 @@ const windows = {
// The list of sort definitions to test.
const sortBys = {
none: null,
- expr: {partitionSeq: {$meta: "randVal"}},
asc: {partitionSeq: 1},
desc: {partitionSeq: -1},
asc_date: {date: 1},
@@ -105,11 +104,18 @@ function constructQuery(wf, window, sortBy, partitionBy) {
}
// Given an element of each of the lists above, what is the expected
-// result. The output should be 'OK' or the expected integer
+// result. The output should be 'SKIP', 'OK' or the expected integer
// error code.
function expectedResult(wfType, windowType, sortType, partitionType) {
// Static errors all come first.
+ // Skip range windows over dates or that are over descending windows.
+ if (windowType.endsWith('range')) {
+ if (sortType.endsWith('date') || sortType.startsWith('desc')) {
+ return 'SKIP';
+ }
+ }
+
// Derivative and integral require an ascending sort
// and an explicit window.
if (wfType.startsWith('derivative')) {
@@ -123,11 +129,6 @@ function expectedResult(wfType, windowType, sortType, partitionType) {
return ErrorCodes.FailedToParse;
}
- // '$derivative requires a non-expression sortBy'.
- if (sortType == "expr") {
- return ErrorCodes.FailedToParse;
- }
-
} else if (wfType.startsWith('integral')) {
// Integral requires a sort.
if (sortType == 'none') {
@@ -139,11 +140,6 @@ function expectedResult(wfType, windowType, sortType, partitionType) {
return ErrorCodes.FailedToParse;
}
- // '$integral requires a non-expression sortBy'.
- if (sortType == "expr") {
- return ErrorCodes.FailedToParse;
- }
-
} else if (wfType.startsWith('expMovingAvg')) {
// $expMovingAvg doesn't accept a window.
if (windowType != 'none') {
@@ -183,23 +179,9 @@ function expectedResult(wfType, windowType, sortType, partitionType) {
}
// Range based windows require a sort over a single field.
- if (windowType.endsWith('range')) {
- if (sortType == 'none' || sortType == 'multi') {
- // 'Range-based window require sortBy a single field'.
- return 5339902;
- }
- if (sortType == 'expr') {
- // 'Range-based bounds require a non-expression sortBy'
- return 8947400;
- }
- if (sortType.startsWith('desc')) {
- // 'Range-based bounds require an ascending sortBy'.
- return 8947401;
- }
- if (sortType.endsWith('date') && !partitionType.endsWith('array')) {
- // 'For windows that involve date or time ranges, a unit must be provided.'
- return 5429413;
- }
+ if (windowType.endsWith('range') && (sortType == 'none' || sortType == 'multi')) {
+ // 'Range-based window require sortBy a single field'.
+ return 5339902;
}
if (partitionType === 'static_array') {
@@ -256,6 +238,10 @@ function* makeTests() {
expectedResult: expectedResult(wfType, windowType, sortType, partitionType)
};
+ if (test.expectedResult == 'SKIP') {
+ continue;
+ }
+
yield test;
}
}
@@ -265,16 +251,13 @@ function* makeTests() {
// Run all the combinations generated in makeTests.
for (const test of makeTests()) {
- const errorMsg = "Command was: " + tojson(test.query);
if (test.expectedResult == ErrorCodes.OK) {
assert.commandWorked(
- coll.runCommand({aggregate: coll.getName(), pipeline: [test.query], cursor: {}}),
- errorMsg);
+ coll.runCommand({aggregate: coll.getName(), pipeline: [test.query], cursor: {}}));
} else {
assert.commandFailedWithCode(
coll.runCommand({aggregate: coll.getName(), pipeline: [test.query], cursor: {}}),
- test.expectedResult,
- errorMsg);
+ test.expectedResult);
}
}
})();
diff --git a/jstests/aggregation/sources/setWindowFields/derivative.js b/jstests/aggregation/sources/setWindowFields/derivative.js
index a57af814b30..b457a1fa097 100644
--- a/jstests/aggregation/sources/setWindowFields/derivative.js
+++ b/jstests/aggregation/sources/setWindowFields/derivative.js
@@ -192,9 +192,9 @@ function explainUnit(unit) {
return coll.runCommand(
{explain: {aggregate: coll.getName(), cursor: {}, pipeline: [derivativeStage(unit)]}});
}
-assert.commandFailedWithCode(explainUnit('year'), 5490710);
-assert.commandFailedWithCode(explainUnit('quarter'), 5490710);
-assert.commandFailedWithCode(explainUnit('month'), 5490710);
+assert.commandFailedWithCode(explainUnit('year'), 5490704);
+assert.commandFailedWithCode(explainUnit('quarter'), 5490704);
+assert.commandFailedWithCode(explainUnit('month'), 5490704);
assert.commandWorked(explainUnit('week'));
assert.commandWorked(explainUnit('day'));
assert.commandWorked(explainUnit('hour'));
diff --git a/jstests/aggregation/sources/setWindowFields/explain.js b/jstests/aggregation/sources/setWindowFields/explain.js
index cae112cb9e5..a070aae7b0e 100644
--- a/jstests/aggregation/sources/setWindowFields/explain.js
+++ b/jstests/aggregation/sources/setWindowFields/explain.js
@@ -16,8 +16,9 @@ coll.drop();
const bigStr = Array(1025).toString(); // 1KB of ','
const nDocs = 1000;
const nPartitions = 50;
-// Size was found through logging in 'SpillableCache' class.
-const docSize = 1292;
+// Initial docSize is 1292, after fields are loaded into Document's cache they are 2332.
+// Not const because post-cache doc size changes based on the number of fields accessed.
+let docSize = 2332;
let bulk = coll.initializeUnorderedBulkOp();
for (let i = 1; i <= nDocs; i++) {
@@ -137,6 +138,8 @@ function checkExplainResult(pipeline, expectedFunctionMemUsages, expectedTotalMe
// The partition iterator will only hold five documents at once. After they are added to the
// removable document executor they will be released.
const numDocsHeld = 5;
+ // This test accesses fewer fields, reduce docSize accordingly.
+ docSize = 1292;
let pipeline = [
{
$setWindowFields: {
diff --git a/jstests/aggregation/sources/setWindowFields/integral.js b/jstests/aggregation/sources/setWindowFields/integral.js
index bb0e962c504..a62033e2b4f 100644
--- a/jstests/aggregation/sources/setWindowFields/integral.js
+++ b/jstests/aggregation/sources/setWindowFields/integral.js
@@ -115,9 +115,9 @@ function explainUnit(unit) {
}
});
}
-assert.commandFailedWithCode(explainUnit('year'), 5490710);
-assert.commandFailedWithCode(explainUnit('quarter'), 5490710);
-assert.commandFailedWithCode(explainUnit('month'), 5490710);
+assert.commandFailedWithCode(explainUnit('year'), 5490704);
+assert.commandFailedWithCode(explainUnit('quarter'), 5490704);
+assert.commandFailedWithCode(explainUnit('month'), 5490704);
assert.commandWorked(explainUnit('week'));
assert.commandWorked(explainUnit('day'));
assert.commandWorked(explainUnit('hour'));
diff --git a/jstests/aggregation/sources/setWindowFields/memory_limit.js b/jstests/aggregation/sources/setWindowFields/memory_limit.js
index a88717d0331..e64781d3eca 100644
--- a/jstests/aggregation/sources/setWindowFields/memory_limit.js
+++ b/jstests/aggregation/sources/setWindowFields/memory_limit.js
@@ -61,10 +61,6 @@ assert.commandWorked(coll.runCommand({
allowDiskUse: false
}));
-setParameterOnAllHosts(nonConfigNodes,
- "internalDocumentSourceSetWindowFieldsMaxMemoryBytes",
- (perDocSize * docsPerPartition) + 1024);
-
// Test that the query fails with a window function that stores documents.
assert.commandFailedWithCode(coll.runCommand({
aggregate: coll.getName(),
@@ -78,7 +74,7 @@ assert.commandFailedWithCode(coll.runCommand({
cursor: {},
allowDiskUse: false
}),
- [5643011, 5414201]);
+ 5414201);
// Reset limit for other tests.
setParameterOnAllHosts(
nonConfigNodes, "internalDocumentSourceSetWindowFieldsMaxMemoryBytes", 100 * 1024 * 1024);
diff --git a/jstests/aggregation/sources/setWindowFields/output_overwrites_existing_data.js b/jstests/aggregation/sources/setWindowFields/output_overwrites_existing_data.js
index 3ec0290054f..8301f78bbfc 100644
--- a/jstests/aggregation/sources/setWindowFields/output_overwrites_existing_data.js
+++ b/jstests/aggregation/sources/setWindowFields/output_overwrites_existing_data.js
@@ -9,6 +9,8 @@
(function() {
"use strict";
+// TODO SERVER-63811 Ensure the database exists so we get back non-empty results even in a sharded
+// cluster.
assert.commandWorked(db[jsTestName()].insert({dummy: 1}));
let windowResults = db.aggregate([
diff --git a/jstests/aggregation/sources/setWindowFields/range_wrong_type.js b/jstests/aggregation/sources/setWindowFields/range_wrong_type.js
deleted file mode 100644
index 26001c40888..00000000000
--- a/jstests/aggregation/sources/setWindowFields/range_wrong_type.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * Test that a window of the form [+N, unbounded] does not trigger a tassert
- * on mixed-type input.
- *
- * Originally intended to reproduce SERVER-71387.
- */
-(function() {
-"use strict";
-
-const coll = db.set_window_fields_range_wrong_type;
-coll.drop();
-assert.commandWorked(coll.insert([
- // Numbers sort before strings, so we'll scan {a: 2} first.
- {a: 2},
- {a: 'xyz'},
-]));
-
-const err = assert.throws(() => {
- return coll
- .aggregate({
- $setWindowFields: {
- sortBy: {a: 1},
- output: {
- // The lower bound +3 excludes the current document {a: 2}.
- // The only remaining document is {a: 'xyz'}.
- // We wrongly consider {a: 'xyz'} to be 'within' the lower bound.
- // Then, when we search for the upper bound, we are surprised
- // to be starting from 'xyz' which is the wrong type.
- b: {$max: 5, window: {range: [+3, 'unbounded']}},
- },
- }
- })
- .toArray();
-});
-assert.eq(err.code, 5429414, err);
-})();
diff --git a/jstests/aggregation/sources/setWindowFields/spill_to_disk.js b/jstests/aggregation/sources/setWindowFields/spill_to_disk.js
index b006c1e66d2..24d03721d03 100644
--- a/jstests/aggregation/sources/setWindowFields/spill_to_disk.js
+++ b/jstests/aggregation/sources/setWindowFields/spill_to_disk.js
@@ -24,12 +24,12 @@ const coll = db[jsTestName()];
coll.drop();
// Doc size was found through logging the size in the SpillableCache. Partition sizes were chosen
// arbitrarily.
-let avgDocSize = 171;
+let avgDocSize = 274;
let smallPartitionSize = 6;
let largePartitionSize = 21;
setParameterOnAllHosts(DiscoverTopology.findNonConfigNodes(db.getMongo()),
"internalDocumentSourceSetWindowFieldsMaxMemoryBytes",
- avgDocSize * smallPartitionSize + 50);
+ avgDocSize * smallPartitionSize + 1);
seedWithTickerData(coll, 10);
diff --git a/jstests/aggregation/sources/shred_documents.js b/jstests/aggregation/sources/shred_documents.js
deleted file mode 100644
index 3705cda6391..00000000000
--- a/jstests/aggregation/sources/shred_documents.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Test $_internalShredDocuments parsing and make sure it doesn't modify documents.
- * this test assumes {$meta: "indexKey"} will not be missing.
- * @tags: [
- * do_not_wrap_aggregations_in_facets
- * ]
- */
-"use strict";
-
-load("jstests/aggregation/extras/utils.js");
-
-(function() {
-const coll = db[jsTestName()];
-coll.insertMany(
- [{a: 1, obj: {a: 1}, arr: [{a: 1}]}, {a: 2, obj: {a: 1}, arr: [{a: 1}]}, {}, {a: 3}]);
-coll.createIndex({a: 1});
-assert.commandFailedWithCode(assert.throws(() => coll.aggregate({$_internalShredDocuments: 1})),
- 7997500);
-assert.commandFailedWithCode(
- assert.throws(() => coll.aggregate(
- {$_internalShredDocuments: {burnEvidence: true, paperType: "legal"}})),
- 7997501);
-const assertNoop = (pipeline, position) => {
- const expected = coll.aggregate(pipeline).toArray();
- pipeline.splice(position, 0, {$_internalShredDocuments: {}});
- const actual = coll.aggregate(pipeline).toArray();
- assertArrayEq({expected, actual});
-};
-assertNoop([], 0);
-assertNoop([{$project: {a: 1}}], 1);
-assertNoop([{$addFields: {b: 2, "obj.b": 2, "arr.0.b": 2}}], 1);
-
-const addFieldGroup = [
- {$addFields: {b: 2, "obj.b": 2, "arr.0.b": 2}},
- {$group: {_id: {a: "$a", b: "$b", obj: "$obj"}}}
-];
-assertNoop(addFieldGroup, 0);
-assertNoop(addFieldGroup, 1);
-assertNoop(addFieldGroup, 2);
-
-const matchExcludeGroup =
- [{$match: {a: 1}}, {$project: {obj: 0}}, {$group: {_id: {a: "$a", b: "$b", obj: "$obj"}}}];
-assertNoop(matchExcludeGroup, 0);
-assertNoop(matchExcludeGroup, 1);
-assertNoop(matchExcludeGroup, 2);
-assertNoop(matchExcludeGroup, 3);
-
-assertNoop([{$match: {a: 1}}, {$addFields: {key: {$meta: "indexKey"}}}], 1);
-
-// The shred() function is also used by set windowFields so lets test that too.
-const res = coll.aggregate([
- {$match: {a: 1}},
- {$addFields: {key: {$meta: "indexKey"}}},
- {$setWindowFields: {sortBy: {a: 1}, output: {w: {$rank: {}}}}},
- {$limit: 1}
- ])
- .toArray();
-assert.eq(1, res.length);
-assert(res[0].hasOwnProperty("key"));
-assert.eq({a: 1}, res[0]["key"]);
-})();
diff --git a/jstests/aggregation/sources/unionWith/unionWith_explain.js b/jstests/aggregation/sources/unionWith/unionWith_explain.js
index a600b7fbe0f..60d6a7ae4a4 100644
--- a/jstests/aggregation/sources/unionWith/unionWith_explain.js
+++ b/jstests/aggregation/sources/unionWith/unionWith_explain.js
@@ -105,18 +105,8 @@ function assertExplainEq(union, regular) {
executionStatsIngoredFields),
buildErrorString(unionStats, regularStats, "executionStages"));
} else if ("stages" in regular) {
- // For explains run with the runCommand({explain: ...}) format.
- if (regular.stages.length > 1 && "$cursor" in regular.stages[0] &&
- "executionStats" in regular.stages[0]["$cursor"]) {
- assert(
- arrayEqWithIgnoredFields(union,
- regular.stages,
- [...stagesIgnoredFields, ...executionStatsIngoredFields]),
- buildErrorString(union, regular, "stages with executionStats"));
- } else {
- assert(arrayEqWithIgnoredFields(union, regular.stages, stagesIgnoredFields),
- buildErrorString(union, regular, "stages"));
- }
+ assert(arrayEqWithIgnoredFields(union, regular.stages, stagesIgnoredFields),
+ buildErrorString(union, regular, "stages"));
} else if ("queryPlanner" in regular) {
assert.eq(union.length, 1, "Expected single union stage");
const unionCursor = union[0].$cursor;
@@ -133,7 +123,7 @@ function assertExplainEq(union, regular) {
function assertExplainMatch(unionExplain, regularExplain) {
const unionStage = getUnionWithStage(unionExplain);
- assert(unionStage, unionExplain);
+ assert(unionStage);
const unionSubExplain = unionStage.$unionWith.pipeline;
assertExplainEq(unionSubExplain, regularExplain);
}
@@ -143,20 +133,6 @@ function testPipeline(pipeline) {
{explain: true});
let queryResult = collB.aggregate(pipeline, {explain: true});
assertExplainMatch(unionResult, queryResult);
-
- // Alternative explain invocation. This is a regression test for SERVER-89344.
- if (!FixtureHelpers.isMongos(db)) {
- unionResult = db.runCommand({
- explain: {
- "aggregate": collA.getName(),
- "pipeline": [{$unionWith: {coll: collB.getName(), pipeline: pipeline}}],
- "cursor": {}
- }
- });
- queryResult = db.runCommand(
- {explain: {"aggregate": collB.getName(), "pipeline": pipeline, "cursor": {}}});
- assertExplainMatch(unionResult, queryResult);
- }
}
testPipeline([{$addFields: {bump: true}}]);
diff --git a/jstests/aggregation/spill_to_disk.js b/jstests/aggregation/spill_to_disk.js
index 568d973e988..e89a1a8ef31 100644
--- a/jstests/aggregation/spill_to_disk.js
+++ b/jstests/aggregation/spill_to_disk.js
@@ -10,7 +10,6 @@
// @tags: [
// requires_collstats,
// requires_pipeline_optimization,
-// requires_persistence,
// ]
(function() {
'use strict';
@@ -24,37 +23,11 @@ load("jstests/libs/sbe_util.js"); // For checkSBEEnabled.
const coll = db.spill_to_disk;
coll.drop();
-// Sets the set parameter named 'paramName' to the given 'memoryLimit' on each primary node in the
-// cluster, and returns the old value.
-function setMemoryParamHelper(paramName, memoryLimit) {
- const commandResArr = FixtureHelpers.runCommandOnEachPrimary({
- db: db.getSiblingDB("admin"),
- cmdObj: {
- setParameter: 1,
- [paramName]: memoryLimit,
- }
- });
- assert.gt(commandResArr.length, 0, "Setting memory limit on primaries failed");
- const oldMemoryLimit = assert.commandWorked(commandResArr[0]).was;
- return oldMemoryLimit;
-}
-
-// Verifies that the given 'groupStats' (an extract from SBE "executionStats" explain output) show
-// evidence of spilling to disk.
-function assertSpillingOccurredInSbeExplain(groupStats) {
- assert(groupStats);
- assert(groupStats.hasOwnProperty("usedDisk"), groupStats);
- assert(groupStats.usedDisk, groupStats);
- assert.gt(groupStats.numSpills, 0, groupStats);
- assert.gt(groupStats.spilledRecords, 0, groupStats);
- assert.gt(groupStats.spilledDataStorageSize, 0, groupStats);
-}
-
const sharded = FixtureHelpers.isSharded(coll);
const memoryLimitMB = sharded ? 200 : 100;
-const isSbeEnabled = checkSBEEnabled(db);
+const isSBELookupEnabled = checkSBEEnabled(db, ["featureFlagSBELookupPushdown"]);
const bigStr = Array(1024 * 1024 + 1).toString(); // 1MB of ','
for (let i = 0; i < memoryLimitMB + 1; i++)
@@ -77,7 +50,7 @@ function test({pipeline, expectedCodes, canSpillToDisk}) {
assert.eq(new DBCommandCursor(coll.getDB(), res).itcount(),
coll.count()); // all tests output one doc per input doc
- if (isSbeEnabled) {
+ if (isSBELookupEnabled) {
const explain = db.runCommand({
explain:
{aggregate: coll.getName(), pipeline: pipeline, cursor: {}, allowDiskUse: true}
@@ -86,7 +59,11 @@ function test({pipeline, expectedCodes, canSpillToDisk}) {
if (hashAggGroups.length > 0) {
assert.eq(hashAggGroups.length, 1, explain);
const hashAggGroup = hashAggGroups[0];
- assertSpillingOccurredInSbeExplain(hashAggGroup);
+ assert(hashAggGroup, explain);
+ assert(hashAggGroup.hasOwnProperty("usedDisk"), hashAggGroup);
+ assert(hashAggGroup.usedDisk, hashAggGroup);
+ assert.gt(hashAggGroup.spilledRecords, 0, hashAggGroup);
+ assert.gt(hashAggGroup.spilledBytesApprox, 0, hashAggGroup);
}
}
} else {
@@ -232,178 +209,6 @@ for (const op of ['$firstN', '$lastN', '$minN', '$maxN', '$topN', '$bottomN']) {
// don't leave large collection laying around
assert(coll.drop());
-// Test spilling to disk for various accumulators in a $group stage . The data has 5 groups of 10
-// documents each. We configure a low memory limit for SBE's hash aggregation stage in order to
-// encourage spilling.
-const numGroups = 5;
-const docsPerGroup = 10;
-let counter = 0;
-for (let i = 0; i < numGroups; ++i) {
- for (let j = 0; j < docsPerGroup; ++j) {
- const doc = {
- _id: counter++,
- a: i,
- b: 100 * i + j,
- c: 100 * i + j % 5,
- obj: {a: i, b: j},
- random: Math.random()
- };
- assert.commandWorked(coll.insert(doc));
- }
-}
-
-function setHashGroupMemoryParameters(memoryLimit) {
- return setMemoryParamHelper(
- "internalQuerySlotBasedExecutionHashAggApproxMemoryUseInBytesBeforeSpill", memoryLimit);
-}
-
-// Runs a group query containing the given 'accumulator' after sorting the data by the given
-// 'sortInputBy' field. Then verifies that the query results are equal to 'expectedOutput'. If SBE
-// is enabled, also runs explain and checks that the execution stats show that spilling occurred.
-function testAccumulator({accumulator, sortInputBy, expectedOutput, ignoreArrayOrder = false}) {
- const pipeline =
- [{$sort: {[sortInputBy]: 1}}, {$group: {_id: "$a", acc: accumulator}}, {$sort: {_id: 1}}];
- const results = coll.aggregate(pipeline).toArray();
-
- if (ignoreArrayOrder) {
- assert(arrayEq(results, expectedOutput));
- } else {
- assert.eq(results, expectedOutput);
- }
-
- if (isSbeEnabled) {
- const explain = coll.explain("executionStats").aggregate(pipeline);
- const groupStages = getSbePlanStages(explain, "group");
- assert.eq(groupStages.length, 1, groupStages);
- assertSpillingOccurredInSbeExplain(groupStages[0]);
- }
-}
-
-function testSpillingForVariousAccumulators() {
- testAccumulator({
- accumulator: {$first: "$b"},
- sortInputBy: "_id",
- expectedOutput: [
- {_id: 0, acc: 0},
- {_id: 1, acc: 100},
- {_id: 2, acc: 200},
- {_id: 3, acc: 300},
- {_id: 4, acc: 400}
- ]
-
- });
-
- testAccumulator({
- accumulator: {$last: "$b"},
- sortInputBy: "_id",
- expectedOutput: [
- {_id: 0, acc: 9},
- {_id: 1, acc: 109},
- {_id: 2, acc: 209},
- {_id: 3, acc: 309},
- {_id: 4, acc: 409}
- ]
- });
-
- testAccumulator({
- accumulator: {$min: "$b"},
- sortInputBy: "random",
- expectedOutput: [
- {_id: 0, acc: 0},
- {_id: 1, acc: 100},
- {_id: 2, acc: 200},
- {_id: 3, acc: 300},
- {_id: 4, acc: 400}
- ]
- });
-
- testAccumulator({
- accumulator: {$max: "$b"},
- sortInputBy: "random",
- expectedOutput: [
- {_id: 0, acc: 9},
- {_id: 1, acc: 109},
- {_id: 2, acc: 209},
- {_id: 3, acc: 309},
- {_id: 4, acc: 409}
- ]
- });
-
- testAccumulator({
- accumulator: {$sum: "$b"},
- sortInputBy: "random",
- expectedOutput: [
- {_id: 0, acc: 45},
- {_id: 1, acc: 1045},
- {_id: 2, acc: 2045},
- {_id: 3, acc: 3045},
- {_id: 4, acc: 4045}
- ]
- });
-
- testAccumulator({
- accumulator: {$avg: "$b"},
- sortInputBy: "random",
- expectedOutput: [
- {_id: 0, acc: 4.5},
- {_id: 1, acc: 104.5},
- {_id: 2, acc: 204.5},
- {_id: 3, acc: 304.5},
- {_id: 4, acc: 404.5}
- ]
- });
-
- testAccumulator({
- accumulator: {$addToSet: "$c"},
- sortInputBy: "random",
- expectedOutput: [
- {_id: 0, acc: [0, 1, 2, 3, 4]},
- {_id: 1, acc: [100, 101, 102, 103, 104]},
- {_id: 2, acc: [200, 201, 202, 203, 204]},
- {_id: 3, acc: [300, 301, 302, 303, 304]},
- {_id: 4, acc: [400, 401, 402, 403, 404]},
- ],
- // Since the accumulator produces sets, the resulting arrays may be in any order.
- ignoreArrayOrder: true,
- });
-
- testAccumulator({
- accumulator: {$push: "$c"},
- sortInputBy: "_id",
- expectedOutput: [
- {_id: 0, acc: [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]},
- {_id: 1, acc: [100, 101, 102, 103, 104, 100, 101, 102, 103, 104]},
- {_id: 2, acc: [200, 201, 202, 203, 204, 200, 201, 202, 203, 204]},
- {_id: 3, acc: [300, 301, 302, 303, 304, 300, 301, 302, 303, 304]},
- {_id: 4, acc: [400, 401, 402, 403, 404, 400, 401, 402, 403, 404]},
- ],
- });
-
- testAccumulator({
- accumulator: {$mergeObjects: "$obj"},
- sortInputBy: "_id",
- expectedOutput: [
- {_id: 0, acc: {a: 0, b: 9}},
- {_id: 1, acc: {a: 1, b: 9}},
- {_id: 2, acc: {a: 2, b: 9}},
- {_id: 3, acc: {a: 3, b: 9}},
- {_id: 4, acc: {a: 4, b: 9}}
- ],
- });
-}
-
-(function() {
-const kMemLimit = 100;
-let oldMemSettings = setHashGroupMemoryParameters(kMemLimit);
-try {
- testSpillingForVariousAccumulators();
-} finally {
- setHashGroupMemoryParameters(oldMemSettings);
-}
-})();
-
-assert(coll.drop());
-
// Test spill to disk for $lookup
const localColl = db.lookup_spill_local_hj;
const foreignColl = db.lookup_spill_foreign_hj;
@@ -416,8 +221,16 @@ function setupCollections(localRecords, foreignRecords, foreignField) {
}
function setHashLookupParameters(memoryLimit) {
- return setMemoryParamHelper(
- "internalQuerySlotBasedExecutionHashLookupApproxMemoryUseInBytesBeforeSpill", memoryLimit);
+ const commandResArr = FixtureHelpers.runCommandOnEachPrimary({
+ db: db.getSiblingDB("admin"),
+ cmdObj: {
+ setParameter: 1,
+ internalQuerySlotBasedExecutionHashLookupApproxMemoryUseInBytesBeforeSpill: memoryLimit,
+ }
+ });
+ assert.gt(commandResArr.length, 0, "Setting memory limit on primaries failed.");
+ const oldMemoryLimit = assert.commandWorked(commandResArr[0]).was;
+ return oldMemoryLimit;
}
/**
@@ -445,7 +258,7 @@ function runTest_MultipleLocalForeignRecords({
const results = localColl.aggregate(pipeline, {allowDiskUse: true}).toArray();
const explain = localColl.explain('executionStats').aggregate(pipeline, {allowDiskUse: true});
// If sharding is enabled, '$lookup' is not pushed down to SBE.
- if (isSbeEnabled && !sharded) {
+ if (isSBELookupEnabled && !sharded) {
const hLookups = getSbePlanStages(explain, 'hash_lookup');
assert.eq(hLookups.length, 1, explain);
const hLookup = hLookups[0];
diff --git a/jstests/aggregation/unwind_sort.js b/jstests/aggregation/unwind_sort.js
deleted file mode 100644
index 858320bec0d..00000000000
--- a/jstests/aggregation/unwind_sort.js
+++ /dev/null
@@ -1,27 +0,0 @@
-// Test that we can sort on fields, produces by unwind
-
-const coll = db.agg_unwind_sort;
-coll.drop();
-assert.commandWorked(coll.insertOne({a: [3, 4, 5]}));
-assert.commandWorked(coll.insertOne({a: [1, 2]}));
-
-let result = coll.aggregate([{$unwind: "$a"}, {$sort: {a: 1}}]).toArray();
-assert.eq([1, 2, 3, 4, 5], result.map(function(z) {
- return z.a;
-}));
-result = coll.aggregate([{$unwind: "$a"}, {$sort: {a: -1}}]).toArray();
-assert.eq([5, 4, 3, 2, 1], result.map(function(z) {
- return z.a;
-}));
-result =
- coll.aggregate([{$unwind: {path: "$a", includeArrayIndex: "i"}}, {$sort: {i: 1}}]).toArray();
-assert.eq([NumberLong(0), NumberLong(0), NumberLong(1), NumberLong(1), NumberLong(2)],
- result.map(function(z) {
- return z.i;
- }));
-result =
- coll.aggregate([{$unwind: {path: "$a", includeArrayIndex: "i"}}, {$sort: {i: -1}}]).toArray();
-assert.eq([NumberLong(2), NumberLong(1), NumberLong(1), NumberLong(0), NumberLong(0)],
- result.map(function(z) {
- return z.i;
- }));
diff --git a/jstests/aggregation/variables/search_meta.js b/jstests/aggregation/variables/search_meta.js
index bcbeef233ed..ba9fe1430ae 100644
--- a/jstests/aggregation/variables/search_meta.js
+++ b/jstests/aggregation/variables/search_meta.js
@@ -46,7 +46,7 @@ const response = db.runCommand({
cursor: {}
});
if (!response.ok) {
- assert.commandFailedWithCode(response, [31082, 6047401] /* mongos or community */);
+ assert.commandFailedWithCode(response, [31082, 40324] /* community or mongos */);
} else {
assert.eq(response.cursor.firstBatch, []);
}