diff options
author | Charlie Swanson <charlie.swanson@mongodb.com> | 2019-05-13 17:05:50 -0400 |
---|---|---|
committer | Charlie Swanson <charlie.swanson@mongodb.com> | 2019-05-15 12:17:39 -0400 |
commit | 30c603ed5efc62a97b42419a92b1210e14932361 (patch) | |
tree | 9f28478800c244a14b9e623e84f26e7abbf37a6e | |
parent | a54c7ffd8086bc6d08b255db1293067b588eba41 (diff) | |
download | mongo-30c603ed5efc62a97b42419a92b1210e14932361.tar.gz |
SERVER-40238 New stage alias: $set => $addFields
This reverts commit c34bc93a15cbb6fe8b222f12afac5adbab6f0737.
16 files changed, 111 insertions, 72 deletions
diff --git a/jstests/change_streams/pipeline_cannot_modify_id_field.js b/jstests/change_streams/pipeline_cannot_modify_id_field.js index 3db6c0990eb..815dad648ae 100644 --- a/jstests/change_streams/pipeline_cannot_modify_id_field.js +++ b/jstests/change_streams/pipeline_cannot_modify_id_field.js @@ -34,30 +34,30 @@ {$project: {_id: {data: "$_id._data", typeBits: "$_id._typeBits"}}}, // Fields renamed. {$project: {_id: {_typeBits: "$_id._typeBits", _data: "$_id._data"}}}, // Fields reordered. {$project: {_id: {_data: "$_id._typeBits", _typeBits: "$_id._data"}}}, // Fields swapped. - {$addFields: {_id: "newValue"}}, - {$addFields: {_id: "$otherField"}}, - {$addFields: {"_id._data": "newValue"}}, - {$addFields: {"_id._data": "$otherField"}}, - {$addFields: {"_id.otherField": "newValue"}}, // New subfield added to _id. + {$set: {_id: "newValue"}}, + {$set: {_id: "$otherField"}}, + {$set: {"_id._data": "newValue"}}, + {$set: {"_id._data": "$otherField"}}, + {$set: {"_id.otherField": "newValue"}}, // New subfield added to _id. [ {$addFields: {otherField: "$_id"}}, - {$addFields: {otherField: "newValue"}}, - {$addFields: {_id: "$otherField"}} + {$set: {otherField: "newValue"}}, + {$set: {_id: "$otherField"}} ], [ // Fields renamed. {$addFields: {newId: {data: "$_id._data", typeBits: "$_id._typeBits"}}}, - {$addFields: {_id: "$newId"}} + {$set: {_id: "$newId"}} ], [ // Fields reordered. {$addFields: {newId: {_typeBits: "$_id._typeBits", _data: "$_id._data"}}}, - {$addFields: {_id: "$newId"}} + {$set: {_id: "$newId"}} ], [ // Fields swapped. {$addFields: {newId: {_data: "$_id._typeBits", _typeBits: "$_id._data"}}}, - {$addFields: {_id: "$newId"}} + {$set: {_id: "$newId"}} ], {$replaceRoot: {newRoot: {otherField: "$_id"}}}, {$redact: {$cond: {if: {$gt: ["$_id", {}]}, then: "$$DESCEND", else: "$$PRUNE"}}} // _id:0 @@ -82,13 +82,13 @@ ], {$project: {"_id._data": 1, "_id._typeBits": 1}}, {$project: {_id: {_data: "$_id._data", _typeBits: "$_id._typeBits"}}}, - {$addFields: {_id: "$_id"}}, + {$set: {_id: "$_id"}}, {$addFields: {otherField: "newValue"}}, - {$addFields: {_id: {_data: "$_id._data", _typeBits: "$_id._typeBits"}}}, - [{$addFields: {otherField: "$_id"}}, {$addFields: {_id: "$otherField"}}], + {$set: {_id: {_data: "$_id._data", _typeBits: "$_id._typeBits"}}}, + [{$addFields: {otherField: "$_id"}}, {$set: {_id: "$otherField"}}], [ {$addFields: {newId: {_data: "$_id._data", _typeBits: "$_id._typeBits"}}}, - {$addFields: {_id: "$newId"}} + {$set: {_id: "$newId"}} ], {$replaceRoot: {newRoot: {_id: "$_id"}}}, { @@ -141,4 +141,4 @@ getMoreRes, ErrorCodes.ChangeStreamFatalError, transform); }, transform); } -}());
\ No newline at end of file +}()); diff --git a/jstests/change_streams/whitelist.js b/jstests/change_streams/whitelist.js index 12848e1e9e7..16b6ca93145 100644 --- a/jstests/change_streams/whitelist.js +++ b/jstests/change_streams/whitelist.js @@ -18,6 +18,7 @@ {$match: {_id: {$exists: true}}}, {$project: {_id: 1}}, {$addFields: {newField: 1}}, + {$set: {newField: 1}}, {$replaceRoot: {newRoot: {_id: "$_id"}}}, {$redact: "$$DESCEND"} ]; @@ -60,4 +61,4 @@ for (let bannedStage of blacklist) { assertErrorCode(coll, changeStream.concat(bannedStage), ErrorCodes.IllegalOperation); } -}());
\ No newline at end of file +}()); diff --git a/jstests/concurrency/fsm_workloads/update_inc_pipeline.js b/jstests/concurrency/fsm_workloads/update_inc_pipeline.js index 554b56c824d..bb4d78d7bcd 100644 --- a/jstests/concurrency/fsm_workloads/update_inc_pipeline.js +++ b/jstests/concurrency/fsm_workloads/update_inc_pipeline.js @@ -11,9 +11,7 @@ load('jstests/concurrency/fsm_workloads/update_inc.js'); // for $config var $config = extendWorkload($config, function($config, $super) { $config.data.getUpdateArgument = function getUpdateArgument(fieldName) { - const updateDoc = [{$addFields: {}}]; - updateDoc[0].$addFields[fieldName] = {$add: ["$" + fieldName, 1]}; - return updateDoc; + return [{$set: {[fieldName]: {$add: ["$" + fieldName, 1]}}}]; }; $config.data.update_inc = "update_inc_pipeline"; diff --git a/jstests/core/benchrun_pipeline_updates.js b/jstests/core/benchrun_pipeline_updates.js index ef1ef719a0c..a9b684c4d96 100644 --- a/jstests/core/benchrun_pipeline_updates.js +++ b/jstests/core/benchrun_pipeline_updates.js @@ -18,7 +18,7 @@ ns: coll.getFullName(), query: {_id: 0}, writeCmd: true, - update: [{$addFields: {x: {$add: ["$x", 1]}}}] + update: [{$set: {x: {$add: ["$x", 1]}}}] }, ], parallel: 2, diff --git a/jstests/core/bypass_doc_validation.js b/jstests/core/bypass_doc_validation.js index 67394b30450..d2b41c531c2 100644 --- a/jstests/core/bypass_doc_validation.js +++ b/jstests/core/bypass_doc_validation.js @@ -151,7 +151,7 @@ // differentiate between an update object and an array. res = myDb.runCommand({ update: collName, - updates: [{q: {}, u: [{$addFields: {pipeline: 1}}]}], + updates: [{q: {}, u: [{$set: {pipeline: 1}}]}], bypassDocumentValidation: false }); assertFailsValidation(BulkWriteResult(res)); @@ -159,21 +159,21 @@ assert.commandWorked(myDb.runCommand({ update: collName, - updates: [{q: {}, u: [{$addFields: {pipeline: 1}}]}], + updates: [{q: {}, u: [{$set: {pipeline: 1}}]}], bypassDocumentValidation: true })); assert.eq(1, coll.count({pipeline: 1})); assert.commandFailed(myDb.runCommand({ findAndModify: collName, - update: [{$addFields: {findAndModifyPipeline: 1}}], + update: [{$set: {findAndModifyPipeline: 1}}], bypassDocumentValidation: false })); assert.eq(0, coll.count({findAndModifyPipeline: 1})); assert.commandWorked(myDb.runCommand({ findAndModify: collName, - update: [{$addFields: {findAndModifyPipeline: 1}}], + update: [{$set: {findAndModifyPipeline: 1}}], bypassDocumentValidation: true })); assert.eq(1, coll.count({findAndModifyPipeline: 1})); diff --git a/jstests/core/collation_find_and_modify.js b/jstests/core/collation_find_and_modify.js index 5b014414580..c66a8005870 100644 --- a/jstests/core/collation_find_and_modify.js +++ b/jstests/core/collation_find_and_modify.js @@ -21,7 +21,7 @@ assert.commandWorked(db.createCollection(coll.getName(), {collation: caseInsensitive})); assert.commandWorked(coll.insert({x: [1, 2, "a", "b", "c", "B"]})); let doc = coll.findAndModify( - {update: [{$addFields: {newField: {$indexOfArray: ["$x", "B"]}}}], new: true}); + {update: [{$set: {newField: {$indexOfArray: ["$x", "B"]}}}], new: true}); assert.eq(doc.newField, 3, doc); // @@ -32,7 +32,7 @@ assert(coll.drop()); assert.commandWorked(coll.insert({x: [1, 2, "a", "b", "c", "B"]})); doc = coll.findAndModify({ - update: [{$addFields: {newField: {$indexOfArray: ["$x", "B"]}}}], + update: [{$set: {newField: {$indexOfArray: ["$x", "B"]}}}], collation: caseSensitive, new: true }); @@ -60,7 +60,7 @@ assert(coll.drop()); assert.commandWorked(coll.insert({x: [1, 2, "a", "b", "c", "B"]})); doc = coll.findAndModify({ - update: [{$addFields: {newField: {$indexOfArray: ["$x", "B"]}}}], + update: [{$set: {newField: {$indexOfArray: ["$x", "B"]}}}], collation: caseInsensitive, new: true }); diff --git a/jstests/core/find_and_modify_pipeline_update.js b/jstests/core/find_and_modify_pipeline_update.js index d97ace94777..1f7d229b7bc 100644 --- a/jstests/core/find_and_modify_pipeline_update.js +++ b/jstests/core/find_and_modify_pipeline_update.js @@ -12,27 +12,27 @@ // Test that it generally works. assert.commandWorked(coll.insert([{_id: 0}, {_id: 1}, {_id: 2}, {_id: 3}, {_id: 4}])); - let found = coll.findAndModify({query: {_id: 0}, update: [{$addFields: {y: 1}}]}); + let found = coll.findAndModify({query: {_id: 0}, update: [{$set: {y: 1}}]}); assert.eq(found, {_id: 0}); - found = coll.findAndModify({query: {_id: 0}, update: [{$addFields: {z: 2}}], new: true}); + found = coll.findAndModify({query: {_id: 0}, update: [{$set: {z: 2}}], new: true}); assert.eq(found, {_id: 0, y: 1, z: 2}); // Test that pipeline-style update supports the 'fields' argument. assert(coll.drop()); assert.commandWorked( coll.insert([{_id: 0, x: 0}, {_id: 1, x: 1}, {_id: 2, x: 2}, {_id: 3, x: 3}])); - found = coll.findAndModify({query: {_id: 0}, update: [{$addFields: {y: 0}}], fields: {x: 0}}); + found = coll.findAndModify({query: {_id: 0}, update: [{$set: {y: 0}}], fields: {x: 0}}); assert.eq(found, {_id: 0}); - found = coll.findAndModify({query: {_id: 1}, update: [{$addFields: {y: 1}}], fields: {x: 1}}); + found = coll.findAndModify({query: {_id: 1}, update: [{$set: {y: 1}}], fields: {x: 1}}); assert.eq(found, {_id: 1, x: 1}); - found = coll.findAndModify( - {query: {_id: 2}, update: [{$addFields: {y: 2}}], fields: {x: 0}, new: true}); + found = + coll.findAndModify({query: {_id: 2}, update: [{$set: {y: 2}}], fields: {x: 0}, new: true}); assert.eq(found, {_id: 2, y: 2}); - found = coll.findAndModify( - {query: {_id: 3}, update: [{$addFields: {y: 3}}], fields: {x: 1}, new: true}); + found = + coll.findAndModify({query: {_id: 3}, update: [{$set: {y: 3}}], fields: {x: 1}, new: true}); assert.eq(found, {_id: 3, x: 3}); // We skip the following test for sharded fixtures as it will fail as the query for @@ -42,14 +42,13 @@ assert(coll.drop()); assert.commandWorked( coll.insert([{_id: 0, x: 'b'}, {_id: 1, x: 'd'}, {_id: 2, x: 'a'}, {_id: 3, x: 'c'}])); - found = - coll.findAndModify({update: [{$addFields: {foo: "bar"}}], sort: {x: -1}, new: true}); + found = coll.findAndModify({update: [{$set: {foo: "bar"}}], sort: {x: -1}, new: true}); assert.eq(found, {_id: 1, x: 'd', foo: "bar"}); } // Test that it rejects the combination of arrayFilters and a pipeline-style update. - let err = assert.throws( - () => coll.findAndModify( - {query: {_id: 1}, update: [{$addFields: {y: 1}}], arrayFilters: [{"i.x": 4}]})); + let err = + assert.throws(() => coll.findAndModify( + {query: {_id: 1}, update: [{$set: {y: 1}}], arrayFilters: [{"i.x": 4}]})); assert.eq(err.code, ErrorCodes.FailedToParse); }()); diff --git a/jstests/core/update_pipeline_shell_helpers.js b/jstests/core/update_pipeline_shell_helpers.js index c5369befde2..36f373f7811 100644 --- a/jstests/core/update_pipeline_shell_helpers.js +++ b/jstests/core/update_pipeline_shell_helpers.js @@ -20,22 +20,22 @@ assert.commandWorked(testColl.insert({_id: 2, a: 2, b: 3})); // Test that each of the update shell helpers permits pipeline-style updates. - assert.commandWorked(testColl.update({_id: 1}, [{$addFields: {update: true}}])); - assert.commandWorked(testColl.update({}, [{$addFields: {updateMulti: true}}], {multi: true})); - assert.commandWorked(testColl.updateOne({_id: 1}, [{$addFields: {updateOne: true}}])); + assert.commandWorked(testColl.update({_id: 1}, [{$set: {update: true}}])); + assert.commandWorked(testColl.update({}, [{$set: {updateMulti: true}}], {multi: true})); + assert.commandWorked(testColl.updateOne({_id: 1}, [{$set: {updateOne: true}}])); assert.commandWorked(testColl.bulkWrite([ - {updateOne: {filter: {_id: 1}, update: [{$addFields: {bulkWriteUpdateOne: true}}]}}, - {updateMany: {filter: {}, update: [{$addFields: {bulkWriteUpdateMany: true}}]}} + {updateOne: {filter: {_id: 1}, update: [{$set: {bulkWriteUpdateOne: true}}]}}, + {updateMany: {filter: {}, update: [{$set: {bulkWriteUpdateMany: true}}]}} ])); // Test that each of the Bulk API update functions correctly handle pipeline syntax. const unorderedBulkOp = testColl.initializeUnorderedBulkOp(); const orderedBulkOp = testColl.initializeOrderedBulkOp(); - unorderedBulkOp.find({_id: 1}).updateOne([{$addFields: {unorderedBulkOpUpdateOne: true}}]); - unorderedBulkOp.find({}).update([{$addFields: {unorderedBulkOpUpdateMulti: true}}]); - orderedBulkOp.find({_id: 1}).updateOne([{$addFields: {orderedBulkOpUpdateOne: true}}]); - orderedBulkOp.find({}).update([{$addFields: {orderedBulkOpUpdateMulti: true}}]); + unorderedBulkOp.find({_id: 1}).updateOne([{$set: {unorderedBulkOpUpdateOne: true}}]); + unorderedBulkOp.find({}).update([{$set: {unorderedBulkOpUpdateMulti: true}}]); + orderedBulkOp.find({_id: 1}).updateOne([{$set: {orderedBulkOpUpdateOne: true}}]); + orderedBulkOp.find({}).update([{$set: {orderedBulkOpUpdateMulti: true}}]); assert.commandWorked(unorderedBulkOp.execute()); assert.commandWorked(orderedBulkOp.execute()); @@ -73,10 +73,10 @@ const expectedFindOneAndUpdatePostImage = Object.merge(expectedFindAndModifyPostImage, {findOneAndUpdate: true}); const findAndModifyPostImage = testColl.findAndModify( - {query: {_id: 1}, update: [{$addFields: {findAndModify: true}}], new: true}); + {query: {_id: 1}, update: [{$set: {findAndModify: true}}], new: true}); assert.docEq(findAndModifyPostImage, expectedFindAndModifyPostImage); const findOneAndUpdatePostImage = testColl.findOneAndUpdate( - {_id: 1}, [{$addFields: {findOneAndUpdate: true}}], {returnNewDocument: true}); + {_id: 1}, [{$set: {findOneAndUpdate: true}}], {returnNewDocument: true}); assert.docEq(findOneAndUpdatePostImage, expectedFindOneAndUpdatePostImage); // Shell helpers for replacement updates should reject pipeline-style updates. diff --git a/jstests/core/update_with_pipeline.js b/jstests/core/update_with_pipeline.js index d9dae091835..e0b6774a786 100644 --- a/jstests/core/update_with_pipeline.js +++ b/jstests/core/update_with_pipeline.js @@ -50,7 +50,7 @@ testUpdate({ query: {_id: 1}, initialDocumentList: [{_id: 1, x: 1}], - update: [{$addFields: {foo: 4}}], + update: [{$set: {foo: 4}}], resultDocList: [{_id: 1, x: 1, foo: 4}], nModified: 1 }); @@ -73,7 +73,7 @@ testUpdate({ query: {x: 1}, initialDocumentList: [{_id: 1, x: 1}, {_id: 2, x: 1}], - update: [{$addFields: {bar: 4}}], + update: [{$set: {bar: 4}}], resultDocList: [{_id: 1, x: 1, bar: 4}, {_id: 2, x: 1, bar: 4}], nModified: 2, options: {multi: true} @@ -85,7 +85,7 @@ testUpdate({ query: {_id: {$in: [1, 2]}}, initialDocumentList: [{_id: 1, x: 1}, {_id: 2, x: 2}], - update: [{$addFields: {bar: 4}}], + update: [{$set: {bar: 4}}], resultDocList: [{_id: 1, x: 1, bar: 4}, {_id: 2, x: 2, bar: 4}], nModified: 1, options: {multi: false} @@ -93,7 +93,7 @@ } // Upsert performs insert. - testUpsertDoesInsert({_id: 1, x: 1}, [{$addFields: {foo: 4}}], {_id: 1, x: 1, foo: 4}); + testUpsertDoesInsert({_id: 1, x: 1}, [{$set: {foo: 4}}], {_id: 1, x: 1, foo: 4}); testUpsertDoesInsert({_id: 1, x: 1}, [{$project: {x: 1}}], {_id: 1, x: 1}); testUpsertDoesInsert({_id: 1, x: 1}, [{$project: {x: "foo"}}], {_id: 1, x: "foo"}); @@ -136,6 +136,6 @@ // The 'arrayFilters' option is not valid for pipeline updates. assert.commandFailedWithCode( - coll.update({_id: 1}, [{$addFields: {x: 1}}], {arrayFilters: [{x: {$eq: 1}}]}), + coll.update({_id: 1}, [{$set: {x: 1}}], {arrayFilters: [{x: {$eq: 1}}]}), ErrorCodes.FailedToParse); })(); diff --git a/jstests/noPassthrough/pipeline_update_gated_by_enable_test_commands.js b/jstests/noPassthrough/pipeline_update_gated_by_enable_test_commands.js index ae97830267e..210a1ed9b16 100644 --- a/jstests/noPassthrough/pipeline_update_gated_by_enable_test_commands.js +++ b/jstests/noPassthrough/pipeline_update_gated_by_enable_test_commands.js @@ -10,10 +10,10 @@ const conn = MongoRunner.runMongod(); const db = conn.getDB("test"); - assert.commandFailedWithCode(db.coll.update({}, [{$addFields: {x: 1}}]), + assert.commandFailedWithCode(db.coll.update({}, [{$set: {x: 1}}]), ErrorCodes.FailedToParse); const error = - assert.throws(() => db.coll.findAndModify({query: {}, update: [{$addFields: {x: 1}}]})); + assert.throws(() => db.coll.findAndModify({query: {}, update: [{$set: {x: 1}}]})); assert.eq(error.code, ErrorCodes.FailedToParse); MongoRunner.stopMongod(conn); @@ -24,9 +24,8 @@ const conn = MongoRunner.runMongod(); const db = conn.getDB("test"); - assert.commandWorked(db.coll.update({}, [{$addFields: {x: 1}}])); - assert.doesNotThrow(() => - db.coll.findAndModify({query: {}, update: [{$addFields: {x: 1}}]})); + assert.commandWorked(db.coll.update({}, [{$set: {x: 1}}])); + assert.doesNotThrow(() => db.coll.findAndModify({query: {}, update: [{$set: {x: 1}}]})); MongoRunner.stopMongod(conn); }()); diff --git a/jstests/noPassthroughWithMongod/commands_that_write_accept_wc_standalone.js b/jstests/noPassthroughWithMongod/commands_that_write_accept_wc_standalone.js index aab4ba9b30b..0e12eb05a97 100644 --- a/jstests/noPassthroughWithMongod/commands_that_write_accept_wc_standalone.js +++ b/jstests/noPassthroughWithMongod/commands_that_write_accept_wc_standalone.js @@ -36,7 +36,7 @@ update: collName, updates: [{ q: {type: 'oak'}, - u: [{$addFields: {type: 'ginkgo'}}], + u: [{$set: {type: 'ginkgo'}}], }], writeConcern: {w: 'majority'} }, @@ -73,7 +73,7 @@ req: { findAndModify: collName, query: {type: 'oak'}, - update: [{$addFields: {type: 'ginkgo'}}], + update: [{$set: {type: 'ginkgo'}}], writeConcern: {w: 'majority'} }, setupFunc: function() { diff --git a/jstests/replsets/commands_that_write_accept_wc.js b/jstests/replsets/commands_that_write_accept_wc.js index e18d43c50df..e99ef63ddba 100644 --- a/jstests/replsets/commands_that_write_accept_wc.js +++ b/jstests/replsets/commands_that_write_accept_wc.js @@ -57,7 +57,7 @@ update: collName, updates: [{ q: {type: 'oak'}, - u: [{$addFields: {type: 'ginkgo'}}], + u: [{$set: {type: 'ginkgo'}}], }], writeConcern: {w: 'majority'} }, @@ -94,7 +94,7 @@ req: { findAndModify: collName, query: {type: 'oak'}, - update: [{$addFields: {type: 'ginkgo'}}], + update: [{$set: {type: 'ginkgo'}}], writeConcern: {w: 'majority'} }, setupFunc: function() { diff --git a/jstests/sharding/commands_that_write_accept_wc_shards.js b/jstests/sharding/commands_that_write_accept_wc_shards.js index 2e3659eb6d1..80ac26b36e7 100644 --- a/jstests/sharding/commands_that_write_accept_wc_shards.js +++ b/jstests/sharding/commands_that_write_accept_wc_shards.js @@ -113,7 +113,7 @@ load('jstests/libs/write_concern_util.js'); update: collName, updates: [{ q: {type: 'oak'}, - u: [{$addFields: {type: 'ginkgo'}}], + u: [{$set: {type: 'ginkgo'}}], }], writeConcern: {w: 'majority'} }, @@ -152,7 +152,7 @@ load('jstests/libs/write_concern_util.js'); req: { findAndModify: collName, query: {type: 'oak'}, - update: [{$addFields: {type: 'ginkgo'}}], + update: [{$set: {type: 'ginkgo'}}], writeConcern: {w: 'majority'} }, setupFunc: function() { diff --git a/src/mongo/db/pipeline/document_source_add_fields.cpp b/src/mongo/db/pipeline/document_source_add_fields.cpp index e95d420ac4d..319ef9776c6 100644 --- a/src/mongo/db/pipeline/document_source_add_fields.cpp +++ b/src/mongo/db/pipeline/document_source_add_fields.cpp @@ -45,27 +45,33 @@ using parsed_aggregation_projection::ParsedAddFields; REGISTER_DOCUMENT_SOURCE(addFields, LiteParsedDocumentSourceDefault::parse, DocumentSourceAddFields::createFromBson); +REGISTER_DOCUMENT_SOURCE(set, + LiteParsedDocumentSourceDefault::parse, + DocumentSourceAddFields::createFromBson); intrusive_ptr<DocumentSource> DocumentSourceAddFields::create( - BSONObj addFieldsSpec, const intrusive_ptr<ExpressionContext>& expCtx) { + BSONObj addFieldsSpec, const intrusive_ptr<ExpressionContext>& expCtx, StringData stageName) { const bool isIndependentOfAnyCollection = false; intrusive_ptr<DocumentSourceSingleDocumentTransformation> addFields( new DocumentSourceSingleDocumentTransformation( expCtx, ParsedAddFields::create(expCtx, addFieldsSpec), - "$addFields", + stageName.toString(), isIndependentOfAnyCollection)); return addFields; } intrusive_ptr<DocumentSource> DocumentSourceAddFields::createFromBson( BSONElement elem, const intrusive_ptr<ExpressionContext>& expCtx) { + const auto specifiedName = elem.fieldNameStringData(); + invariant(specifiedName == kStageName || specifiedName == kAliasNameSet); + uassert(40272, - str::stream() << "$addFields specification stage must be an object, got " + str::stream() << specifiedName << " specification stage must be an object, got " << typeName(elem.type()), elem.type() == Object); - return DocumentSourceAddFields::create(elem.Obj(), expCtx); + return DocumentSourceAddFields::create(elem.Obj(), expCtx, specifiedName); } } diff --git a/src/mongo/db/pipeline/document_source_add_fields.h b/src/mongo/db/pipeline/document_source_add_fields.h index cf24246676e..5c99a3790bb 100644 --- a/src/mongo/db/pipeline/document_source_add_fields.h +++ b/src/mongo/db/pipeline/document_source_add_fields.h @@ -36,14 +36,21 @@ namespace mongo { /** * $addFields adds or replaces the specified fields to/in the document while preserving the original * document. It is modeled on and throws the same errors as $project. + * + * This stage is also aliased as $set and functions the same way. */ class DocumentSourceAddFields final { public: + static constexpr StringData kStageName = "$addFields"_sd; + static constexpr StringData kAliasNameSet = "$set"_sd; // An alternate name for this stage. + /** * Convenience method for creating a $addFields stage from 'addFieldsSpec'. */ static boost::intrusive_ptr<DocumentSource> create( - BSONObj addFieldsSpec, const boost::intrusive_ptr<ExpressionContext>& expCtx); + BSONObj addFieldsSpec, + const boost::intrusive_ptr<ExpressionContext>& expCtx, + StringData stageName = kStageName); /** * Parses a $addFields stage from the user-supplied BSON. diff --git a/src/mongo/db/pipeline/document_source_add_fields_test.cpp b/src/mongo/db/pipeline/document_source_add_fields_test.cpp index c0b896d973a..bd8c9483ea7 100644 --- a/src/mongo/db/pipeline/document_source_add_fields_test.cpp +++ b/src/mongo/db/pipeline/document_source_add_fields_test.cpp @@ -73,6 +73,35 @@ TEST_F(AddFieldsTest, ShouldKeepUnspecifiedFieldsReplaceExistingFieldsAndAddNewF ASSERT_TRUE(addFields->getNext().isEOF()); } +TEST_F(AddFieldsTest, ShouldSerializeAndParse) { + auto addFields = DocumentSourceAddFields::create(BSON("a" << BSON("$const" + << "new")), + getExpCtx()); + ASSERT(addFields->getSourceName() == DocumentSourceAddFields::kStageName); + vector<Value> serializedArray; + addFields->serializeToArray(serializedArray); + auto serializedBson = serializedArray[0].getDocument().toBson(); + ASSERT_BSONOBJ_EQ(serializedBson, fromjson("{$addFields: {a: {$const: 'new'}}}")); + addFields = DocumentSourceAddFields::createFromBson(serializedBson.firstElement(), getExpCtx()); + ASSERT(addFields != nullptr); + ASSERT(addFields->getSourceName() == DocumentSourceAddFields::kStageName); +} + +TEST_F(AddFieldsTest, SetAliasShouldSerializeAndParse) { + auto setStage = DocumentSourceAddFields::create(BSON("a" << BSON("$const" + << "new")), + getExpCtx(), + DocumentSourceAddFields::kAliasNameSet); + ASSERT(setStage->getSourceName() == DocumentSourceAddFields::kAliasNameSet); + vector<Value> serializedArray; + setStage->serializeToArray(serializedArray); + auto serializedBson = serializedArray[0].getDocument().toBson(); + ASSERT_BSONOBJ_EQ(serializedBson, fromjson("{$set: {a: {$const: 'new'}}}")); + setStage = DocumentSourceAddFields::createFromBson(serializedBson.firstElement(), getExpCtx()); + ASSERT(setStage != nullptr); + ASSERT(setStage->getSourceName() == DocumentSourceAddFields::kAliasNameSet); +} + TEST_F(AddFieldsTest, ShouldOptimizeInnerExpressions) { auto addFields = DocumentSourceAddFields::create( BSON("a" << BSON("$and" << BSON_ARRAY(BSON("$const" << true)))), getExpCtx()); |