summaryrefslogtreecommitdiff
path: root/jstests/aggregation
diff options
context:
space:
mode:
authorNick Zolnierz <nicholas.zolnierz@mongodb.com>2018-07-26 16:00:20 -0400
committerNick Zolnierz <nicholas.zolnierz@mongodb.com>2018-08-02 12:23:03 -0400
commitafbe688f0f18c5cb474fb1bcd933d6e06c0c5291 (patch)
treea7c1a3192f335c5e2947e395159eaa03782c3a50 /jstests/aggregation
parentafaf46687eb3930ddbfc8b528bd68295b6a09676 (diff)
downloadmongo-afbe688f0f18c5cb474fb1bcd933d6e06c0c5291.tar.gz
SERVER-35896: Support 'replaceDocuments' mode in $out
Diffstat (limited to 'jstests/aggregation')
-rw-r--r--jstests/aggregation/sources/out/batch_writes.js61
-rw-r--r--jstests/aggregation/sources/out/mode_replace_documents.js93
2 files changed, 154 insertions, 0 deletions
diff --git a/jstests/aggregation/sources/out/batch_writes.js b/jstests/aggregation/sources/out/batch_writes.js
new file mode 100644
index 00000000000..6bbaac7737a
--- /dev/null
+++ b/jstests/aggregation/sources/out/batch_writes.js
@@ -0,0 +1,61 @@
+// Tests the behavior of an $out stage which encounters an error in the middle of processing. We
+// don't guarantee any particular behavior in this scenario, but this test exists to make sure
+// nothing horrendous happens and to characterize the current behavior.
+// @tags: [assumes_unsharded_collection]
+(function() {
+ "use strict";
+
+ load("jstests/aggregation/extras/utils.js"); // For assertErrorCode.
+
+ const coll = db.batch_writes;
+ const outColl = db.batch_writes_out;
+ coll.drop();
+ outColl.drop();
+
+ // Test with 2 very large documents that do not fit into a single batch.
+ const kSize15MB = 15 * 1024 * 1024;
+ const largeArray = new Array(kSize15MB).join("a");
+ assert.commandWorked(coll.insert({_id: 0, a: largeArray}));
+ assert.commandWorked(coll.insert({_id: 1, a: largeArray}));
+
+ // Make sure the $out succeeds without any duplicate keys.
+ ["replaceCollection", "insertDocuments", "replaceDocuments"].forEach(mode => {
+ coll.aggregate([{$out: {to: outColl.getName(), mode: mode}}]);
+ assert.eq(2, outColl.find().itcount());
+ outColl.drop();
+ });
+
+ coll.drop();
+ for (let i = 0; i < 10; i++) {
+ assert.commandWorked(coll.insert({_id: i, a: i}));
+ }
+
+ // Create a unique index on 'a' in the output collection to create a unique key violation when
+ // running the $out. The second document to be written ({_id: 1, a: 1}) will conflict with the
+ // existing document in the output collection. We use a unique index on a field other than _id
+ // because "replaceDocuments" mode will not change _id when one already exists.
+ outColl.drop();
+ assert.commandWorked(outColl.insert({_id: 2, a: 1}));
+ assert.commandWorked(outColl.createIndex({a: 1}, {unique: true}));
+
+ // Test that both batched updates and inserts will successfully write the first document but
+ // fail on the second. We don't guarantee any particular behavior in this case, but this test is
+ // meant to characterize the current behavior.
+ assertErrorCode(coll, [{$out: {to: outColl.getName(), mode: "insertDocuments"}}], 16996);
+ assert.soon(() => {
+ return outColl.find().itcount() == 2;
+ });
+
+ assertErrorCode(coll, [{$out: {to: outColl.getName(), mode: "replaceDocuments"}}], 50904);
+ assert.soon(() => {
+ return outColl.find().itcount() == 2;
+ });
+
+ // Mode "replaceCollection" will drop the contents of the output collection, so there is no
+ // duplicate key error.
+ outColl.drop();
+ assert.commandWorked(outColl.insert({_id: 2, a: 1}));
+ assert.commandWorked(outColl.createIndex({a: 1}, {unique: true}));
+ coll.aggregate([{$out: {to: outColl.getName(), mode: "replaceCollection"}}]);
+ assert.eq(10, outColl.find().itcount());
+}());
diff --git a/jstests/aggregation/sources/out/mode_replace_documents.js b/jstests/aggregation/sources/out/mode_replace_documents.js
new file mode 100644
index 00000000000..8335981a1ff
--- /dev/null
+++ b/jstests/aggregation/sources/out/mode_replace_documents.js
@@ -0,0 +1,93 @@
+// Tests for the $out stage with mode set to "replaceDocuments".
+// @tags: [assumes_unsharded_collection]
+(function() {
+ "use strict";
+
+ load("jstests/aggregation/extras/utils.js"); // For assertErrorCode.
+
+ const coll = db.replace_docs;
+ const outColl = db.replace_docs_out;
+ coll.drop();
+ outColl.drop();
+
+ const nDocs = 10;
+ for (let i = 0; i < nDocs; i++) {
+ assert.commandWorked(coll.insert({_id: i, a: i}));
+ }
+
+ // Test that a $out with 'replaceDocuments' mode will default the unique key to "_id".
+ coll.aggregate([{$out: {to: outColl.getName(), mode: "replaceDocuments"}}]);
+ assert.eq(nDocs, outColl.find().itcount());
+
+ // Test that 'replaceDocuments' mode will update existing documents that match the unique key.
+ const nDocsReplaced = 5;
+ coll.aggregate([
+ {$project: {_id: {$mod: ["$_id", nDocsReplaced]}}},
+ {$out: {to: outColl.getName(), mode: "replaceDocuments", uniqueKey: {_id: 1}}}
+ ]);
+ assert.eq(nDocsReplaced, outColl.find({a: {$exists: false}}).itcount());
+
+ // Test 'replaceDocuments' mode with a dotted path unique key.
+ coll.drop();
+ outColl.drop();
+ assert.commandWorked(coll.insert({_id: 0, a: {b: 1}}));
+ assert.commandWorked(coll.insert({_id: 1, a: {b: 1}, c: 1}));
+ coll.aggregate([
+ {$addFields: {_id: 0}},
+ {$out: {to: outColl.getName(), mode: "replaceDocuments", uniqueKey: {_id: 1, "a.b": 1}}}
+ ]);
+ assert.eq([{_id: 0, a: {b: 1}, c: 1}], outColl.find().toArray());
+
+ // TODO SERVER-36100: 'replaceDocuments' mode should allow a missing "_id" unique key.
+ assertErrorCode(coll,
+ [
+ {$project: {_id: 0}},
+ {
+ $out: {
+ to: outColl.getName(),
+ mode: "replaceDocuments",
+ }
+ }
+ ],
+ 50905);
+
+ // Test that 'replaceDocuments' mode with a missing non-id unique key fails.
+ assertErrorCode(
+ coll,
+ [{$out: {to: outColl.getName(), mode: "replaceDocuments", uniqueKey: {missing: 1}}}],
+ 50905);
+
+ // Test that a replace fails to insert a document if it violates a unique index constraint. In
+ // this example, $out will attempt to insert multiple documents with {a: 0} which is not allowed
+ // with the unique index on {a: 1}.
+ coll.drop();
+ assert.commandWorked(coll.insert([{_id: 0}, {_id: 1}]));
+
+ outColl.drop();
+ assert.commandWorked(outColl.createIndex({a: 1}, {unique: true}));
+ assertErrorCode(
+ coll,
+ [{$addFields: {a: 0}}, {$out: {to: outColl.getName(), mode: "replaceDocuments"}}],
+ 50904);
+
+ // Test that $out fails if the unique key contains an array.
+ coll.drop();
+ assert.commandWorked(coll.insert({_id: 0, a: [1, 2]}));
+ assertErrorCode(
+ coll,
+ [
+ {$addFields: {_id: 0}},
+ {$out: {to: outColl.getName(), mode: "replaceDocuments", uniqueKey: {_id: 1, "a.b": 1}}}
+ ],
+ 50905);
+
+ coll.drop();
+ assert.commandWorked(coll.insert({_id: 0, a: [{b: 1}]}));
+ assertErrorCode(
+ coll,
+ [
+ {$addFields: {_id: 0}},
+ {$out: {to: outColl.getName(), mode: "replaceDocuments", uniqueKey: {_id: 1, "a.b": 1}}}
+ ],
+ 50905);
+}());