diff options
Diffstat (limited to 'jstests/aggregation/sources')
23 files changed, 152 insertions, 586 deletions
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}}]); |
