summaryrefslogtreecommitdiff
path: root/jstests/aggregation/sources/merge/merge_with_dollar_fields.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/aggregation/sources/merge/merge_with_dollar_fields.js')
-rw-r--r--jstests/aggregation/sources/merge/merge_with_dollar_fields.js135
1 files changed, 135 insertions, 0 deletions
diff --git a/jstests/aggregation/sources/merge/merge_with_dollar_fields.js b/jstests/aggregation/sources/merge/merge_with_dollar_fields.js
new file mode 100644
index 00000000000..b79b3a278dd
--- /dev/null
+++ b/jstests/aggregation/sources/merge/merge_with_dollar_fields.js
@@ -0,0 +1,135 @@
+// 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 = {
+ ...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'}));
+}());