summaryrefslogtreecommitdiff
path: root/jstests/core/update_with_pipeline.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/core/update_with_pipeline.js')
-rw-r--r--jstests/core/update_with_pipeline.js417
1 files changed, 202 insertions, 215 deletions
diff --git a/jstests/core/update_with_pipeline.js b/jstests/core/update_with_pipeline.js
index 07d92c718df..963d72b6592 100644
--- a/jstests/core/update_with_pipeline.js
+++ b/jstests/core/update_with_pipeline.js
@@ -7,227 +7,214 @@
* @tags: [requires_find_command, requires_non_retryable_writes]
*/
(function() {
- "use strict";
-
- load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers.
-
- const collName = "update_with_pipeline";
- const coll = db[collName];
-
- assert.commandWorked(coll.createIndex({x: 1}));
- assert.commandWorked(coll.createIndex({"y.$**": 1}));
-
- /**
- * Confirms that an update returns the expected set of documents. 'nModified' documents from
- * 'resultDocList' must match. 'nModified' may be smaller then the number of elements in
- * 'resultDocList'. This allows for the case where there are multiple documents that could be
- * updated, but only one is actually updated due to a 'multi: false' argument. Constant values
- * to the update command are passed in the 'constants' argument.
- */
- function testUpdate({
- query,
- initialDocumentList,
- update,
- resultDocList,
- nModified,
- options = {},
- constants = undefined
- }) {
- assert.eq(initialDocumentList.length, resultDocList.length);
- assert.commandWorked(coll.remove({}));
- assert.commandWorked(coll.insert(initialDocumentList));
- const upd = Object.assign({q: query, u: update}, options);
- if (constants !== undefined) {
- upd.c = constants;
- }
- const res = assert.commandWorked(db.runCommand({update: collName, updates: [upd]}));
- assert.eq(nModified, res.nModified);
-
- let nMatched = 0;
- for (let i = 0; i < resultDocList.length; ++i) {
- if (0 === bsonWoCompare(coll.findOne(resultDocList[i]), resultDocList[i])) {
- ++nMatched;
- }
- }
- assert.eq(
- nModified, nMatched, `actual=${coll.find().toArray()}, expected=${resultDocList}`);
- }
+"use strict";
- function testUpsertDoesInsert(query, update, resultDoc) {
- assert.commandWorked(coll.remove({}));
- assert.commandWorked(coll.update(query, update, {upsert: true}));
- assert.eq(coll.findOne({}), resultDoc, coll.find({}).toArray());
- }
+load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers.
- // Update with existing document.
- testUpdate({
- query: {_id: 1},
- initialDocumentList: [{_id: 1, x: 1}],
- update: [{$set: {foo: 4}}],
- resultDocList: [{_id: 1, x: 1, foo: 4}],
- nModified: 1
- });
- testUpdate({
- query: {_id: 1},
- initialDocumentList: [{_id: 1, x: 1, y: 1}],
- update: [{$project: {x: 1}}],
- resultDocList: [{_id: 1, x: 1}],
- nModified: 1
- });
- testUpdate({
- query: {_id: 1},
- initialDocumentList: [{_id: 1, x: 1, y: [{z: 1, foo: 1}]}],
- update: [{$unset: ["x", "y.z"]}],
- resultDocList: [{_id: 1, y: [{foo: 1}]}],
- nModified: 1
- });
- testUpdate({
- query: {_id: 1},
- initialDocumentList: [{_id: 1, x: 1, t: {u: {v: 1}}}],
- update: [{$replaceWith: "$t"}],
- resultDocList: [{_id: 1, u: {v: 1}}],
- nModified: 1
- });
+const collName = "update_with_pipeline";
+const coll = db[collName];
- // Multi-update.
- testUpdate({
- query: {x: 1},
- initialDocumentList: [{_id: 1, x: 1}, {_id: 2, x: 1}],
- update: [{$set: {bar: 4}}],
- resultDocList: [{_id: 1, x: 1, bar: 4}, {_id: 2, x: 1, bar: 4}],
- nModified: 2,
- options: {multi: true}
- });
+assert.commandWorked(coll.createIndex({x: 1}));
+assert.commandWorked(coll.createIndex({"y.$**": 1}));
- // This test will fail in a sharded cluster when the 2 initial documents live on different
- // shards.
- if (!FixtureHelpers.isMongos(db)) {
- testUpdate({
- query: {_id: {$in: [1, 2]}},
- initialDocumentList: [{_id: 1, x: 1}, {_id: 2, x: 2}],
- update: [{$set: {bar: 4}}],
- resultDocList: [{_id: 1, x: 1, bar: 4}, {_id: 2, x: 2, bar: 4}],
- nModified: 1,
- options: {multi: false}
- });
+/**
+ * Confirms that an update returns the expected set of documents. 'nModified' documents from
+ * 'resultDocList' must match. 'nModified' may be smaller then the number of elements in
+ * 'resultDocList'. This allows for the case where there are multiple documents that could be
+ * updated, but only one is actually updated due to a 'multi: false' argument. Constant values
+ * to the update command are passed in the 'constants' argument.
+ */
+function testUpdate({
+ query,
+ initialDocumentList,
+ update,
+ resultDocList,
+ nModified,
+ options = {},
+ constants = undefined
+}) {
+ assert.eq(initialDocumentList.length, resultDocList.length);
+ assert.commandWorked(coll.remove({}));
+ assert.commandWorked(coll.insert(initialDocumentList));
+ const upd = Object.assign({q: query, u: update}, options);
+ if (constants !== undefined) {
+ upd.c = constants;
}
+ const res = assert.commandWorked(db.runCommand({update: collName, updates: [upd]}));
+ assert.eq(nModified, res.nModified);
- // Upsert performs insert.
- 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"});
- testUpsertDoesInsert({_id: 1, x: 1, y: 1}, [{$unset: ["x"]}], {_id: 1, y: 1});
-
- // Update fails when invalid stage is specified. This is a sanity check rather than an
- // exhaustive test of all stages.
- assert.commandFailedWithCode(coll.update({x: 1}, [{$match: {x: 1}}]),
- ErrorCodes.InvalidOptions);
- assert.commandFailedWithCode(coll.update({x: 1}, [{$sort: {x: 1}}]), ErrorCodes.InvalidOptions);
- assert.commandFailedWithCode(coll.update({x: 1}, [{$facet: {a: [{$match: {x: 1}}]}}]),
- ErrorCodes.InvalidOptions);
- assert.commandFailedWithCode(coll.update({x: 1}, [{$indexStats: {}}]),
- ErrorCodes.InvalidOptions);
- assert.commandFailedWithCode(coll.update({x: 1}, [{
- $bucket: {
- groupBy: "$a",
- boundaries: [0, 1],
- default: "foo",
- output: {count: {$sum: 1}}
- }
- }]),
- ErrorCodes.InvalidOptions);
- assert.commandFailedWithCode(
- coll.update({x: 1},
- [{$lookup: {from: "foo", as: "as", localField: "a", foreignField: "b"}}]),
- ErrorCodes.InvalidOptions);
- assert.commandFailedWithCode(coll.update({x: 1}, [{
- $graphLookup: {
- from: "foo",
- startWith: "$a",
- connectFromField: "a",
- connectToField: "b",
- as: "as"
- }
- }]),
- ErrorCodes.InvalidOptions);
-
- // Update fails when supported agg stage is specified outside of pipeline.
- assert.commandFailedWithCode(coll.update({_id: 1}, {$addFields: {x: 1}}),
- ErrorCodes.FailedToParse);
-
- // The 'arrayFilters' option is not valid for pipeline updates.
- assert.commandFailedWithCode(
- coll.update({_id: 1}, [{$set: {x: 1}}], {arrayFilters: [{x: {$eq: 1}}]}),
- ErrorCodes.FailedToParse);
-
- // Constants can be specified with pipeline-style updates.
- testUpdate({
- query: {_id: 1},
- initialDocumentList: [{_id: 1, x: 1}],
- useUpdateCommand: true,
- constants: {foo: "bar"},
- update: [{$set: {foo: "$$foo"}}],
- resultDocList: [{_id: 1, x: 1, foo: "bar"}],
- nModified: 1
- });
- testUpdate({
- query: {_id: 1},
- initialDocumentList: [{_id: 1, x: 1}],
- useUpdateCommand: true,
- constants: {foo: {a: {b: {c: "bar"}}}},
- update: [{$set: {foo: "$$foo"}}],
- resultDocList: [{_id: 1, x: 1, foo: {a: {b: {c: "bar"}}}}],
- nModified: 1
- });
- testUpdate({
- query: {_id: 1},
- initialDocumentList: [{_id: 1, x: 1}],
- useUpdateCommand: true,
- constants: {foo: [1, 2, 3]},
- update: [{$set: {foo: {$arrayElemAt: ["$$foo", 2]}}}],
- resultDocList: [{_id: 1, x: 1, foo: 3}],
- nModified: 1
- });
-
- const largeStr = "x".repeat(1000);
- testUpdate({
- query: {_id: 1},
- initialDocumentList: [{_id: 1, x: 1}],
- useUpdateCommand: true,
- constants: {largeStr: largeStr},
- update: [{$set: {foo: "$$largeStr"}}],
- resultDocList: [{_id: 1, x: 1, foo: largeStr}],
- nModified: 1
- });
-
- // References to document fields are not resolved in constants.
+ let nMatched = 0;
+ for (let i = 0; i < resultDocList.length; ++i) {
+ if (0 === bsonWoCompare(coll.findOne(resultDocList[i]), resultDocList[i])) {
+ ++nMatched;
+ }
+ }
+ assert.eq(nModified, nMatched, `actual=${coll.find().toArray()}, expected=${resultDocList}`);
+}
+
+function testUpsertDoesInsert(query, update, resultDoc) {
+ assert.commandWorked(coll.remove({}));
+ assert.commandWorked(coll.update(query, update, {upsert: true}));
+ assert.eq(coll.findOne({}), resultDoc, coll.find({}).toArray());
+}
+
+// Update with existing document.
+testUpdate({
+ query: {_id: 1},
+ initialDocumentList: [{_id: 1, x: 1}],
+ update: [{$set: {foo: 4}}],
+ resultDocList: [{_id: 1, x: 1, foo: 4}],
+ nModified: 1
+});
+testUpdate({
+ query: {_id: 1},
+ initialDocumentList: [{_id: 1, x: 1, y: 1}],
+ update: [{$project: {x: 1}}],
+ resultDocList: [{_id: 1, x: 1}],
+ nModified: 1
+});
+testUpdate({
+ query: {_id: 1},
+ initialDocumentList: [{_id: 1, x: 1, y: [{z: 1, foo: 1}]}],
+ update: [{$unset: ["x", "y.z"]}],
+ resultDocList: [{_id: 1, y: [{foo: 1}]}],
+ nModified: 1
+});
+testUpdate({
+ query: {_id: 1},
+ initialDocumentList: [{_id: 1, x: 1, t: {u: {v: 1}}}],
+ update: [{$replaceWith: "$t"}],
+ resultDocList: [{_id: 1, u: {v: 1}}],
+ nModified: 1
+});
+
+// Multi-update.
+testUpdate({
+ query: {x: 1},
+ initialDocumentList: [{_id: 1, x: 1}, {_id: 2, x: 1}],
+ update: [{$set: {bar: 4}}],
+ resultDocList: [{_id: 1, x: 1, bar: 4}, {_id: 2, x: 1, bar: 4}],
+ nModified: 2,
+ options: {multi: true}
+});
+
+// This test will fail in a sharded cluster when the 2 initial documents live on different
+// shards.
+if (!FixtureHelpers.isMongos(db)) {
testUpdate({
- query: {_id: 1},
- initialDocumentList: [{_id: 1, x: 1}],
- useUpdateCommand: true,
- constants: {foo: "$x"},
- update: [{$set: {foo: "$$foo"}}],
- resultDocList: [{_id: 1, x: 1, foo: "$x"}],
- nModified: 1
+ query: {_id: {$in: [1, 2]}},
+ initialDocumentList: [{_id: 1, x: 1}, {_id: 2, x: 2}],
+ update: [{$set: {bar: 4}}],
+ resultDocList: [{_id: 1, x: 1, bar: 4}, {_id: 2, x: 2, bar: 4}],
+ nModified: 1,
+ options: {multi: false}
});
-
- // Cannot use expressions in constants.
- assert.commandFailedWithCode(db.runCommand({
- update: collName,
- updates: [{q: {_id: 1}, u: [{$set: {x: "$$foo"}}], c: {foo: {$add: [1, 2]}}}]
- }),
- ErrorCodes.DollarPrefixedFieldName);
-
- // Cannot use constants with regular updates.
- assert.commandFailedWithCode(
- db.runCommand(
- {update: collName, updates: [{q: {_id: 1}, u: {x: "$$foo"}, c: {foo: "bar"}}]}),
- 51198);
- assert.commandFailedWithCode(
- db.runCommand(
- {update: collName, updates: [{q: {_id: 1}, u: {$set: {x: "$$foo"}}, c: {foo: "bar"}}]}),
- 51198);
- assert.commandFailedWithCode(
- db.runCommand({update: collName, updates: [{q: {_id: 1}, u: {$set: {x: "1"}}, c: {}}]}),
- 51198);
+}
+
+// Upsert performs insert.
+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"});
+testUpsertDoesInsert({_id: 1, x: 1, y: 1}, [{$unset: ["x"]}], {_id: 1, y: 1});
+
+// Update fails when invalid stage is specified. This is a sanity check rather than an
+// exhaustive test of all stages.
+assert.commandFailedWithCode(coll.update({x: 1}, [{$match: {x: 1}}]), ErrorCodes.InvalidOptions);
+assert.commandFailedWithCode(coll.update({x: 1}, [{$sort: {x: 1}}]), ErrorCodes.InvalidOptions);
+assert.commandFailedWithCode(coll.update({x: 1}, [{$facet: {a: [{$match: {x: 1}}]}}]),
+ ErrorCodes.InvalidOptions);
+assert.commandFailedWithCode(coll.update({x: 1}, [{$indexStats: {}}]), ErrorCodes.InvalidOptions);
+assert.commandFailedWithCode(
+ coll.update(
+ {x: 1}, [{
+ $bucket: {groupBy: "$a", boundaries: [0, 1], default: "foo", output: {count: {$sum: 1}}}
+ }]),
+ ErrorCodes.InvalidOptions);
+assert.commandFailedWithCode(
+ coll.update({x: 1}, [{$lookup: {from: "foo", as: "as", localField: "a", foreignField: "b"}}]),
+ ErrorCodes.InvalidOptions);
+assert.commandFailedWithCode(
+ coll.update(
+ {x: 1}, [{
+ $graphLookup:
+ {from: "foo", startWith: "$a", connectFromField: "a", connectToField: "b", as: "as"}
+ }]),
+ ErrorCodes.InvalidOptions);
+
+// Update fails when supported agg stage is specified outside of pipeline.
+assert.commandFailedWithCode(coll.update({_id: 1}, {$addFields: {x: 1}}), ErrorCodes.FailedToParse);
+
+// The 'arrayFilters' option is not valid for pipeline updates.
+assert.commandFailedWithCode(
+ coll.update({_id: 1}, [{$set: {x: 1}}], {arrayFilters: [{x: {$eq: 1}}]}),
+ ErrorCodes.FailedToParse);
+
+// Constants can be specified with pipeline-style updates.
+testUpdate({
+ query: {_id: 1},
+ initialDocumentList: [{_id: 1, x: 1}],
+ useUpdateCommand: true,
+ constants: {foo: "bar"},
+ update: [{$set: {foo: "$$foo"}}],
+ resultDocList: [{_id: 1, x: 1, foo: "bar"}],
+ nModified: 1
+});
+testUpdate({
+ query: {_id: 1},
+ initialDocumentList: [{_id: 1, x: 1}],
+ useUpdateCommand: true,
+ constants: {foo: {a: {b: {c: "bar"}}}},
+ update: [{$set: {foo: "$$foo"}}],
+ resultDocList: [{_id: 1, x: 1, foo: {a: {b: {c: "bar"}}}}],
+ nModified: 1
+});
+testUpdate({
+ query: {_id: 1},
+ initialDocumentList: [{_id: 1, x: 1}],
+ useUpdateCommand: true,
+ constants: {foo: [1, 2, 3]},
+ update: [{$set: {foo: {$arrayElemAt: ["$$foo", 2]}}}],
+ resultDocList: [{_id: 1, x: 1, foo: 3}],
+ nModified: 1
+});
+
+const largeStr = "x".repeat(1000);
+testUpdate({
+ query: {_id: 1},
+ initialDocumentList: [{_id: 1, x: 1}],
+ useUpdateCommand: true,
+ constants: {largeStr: largeStr},
+ update: [{$set: {foo: "$$largeStr"}}],
+ resultDocList: [{_id: 1, x: 1, foo: largeStr}],
+ nModified: 1
+});
+
+// References to document fields are not resolved in constants.
+testUpdate({
+ query: {_id: 1},
+ initialDocumentList: [{_id: 1, x: 1}],
+ useUpdateCommand: true,
+ constants: {foo: "$x"},
+ update: [{$set: {foo: "$$foo"}}],
+ resultDocList: [{_id: 1, x: 1, foo: "$x"}],
+ nModified: 1
+});
+
+// Cannot use expressions in constants.
+assert.commandFailedWithCode(db.runCommand({
+ update: collName,
+ updates: [{q: {_id: 1}, u: [{$set: {x: "$$foo"}}], c: {foo: {$add: [1, 2]}}}]
+}),
+ ErrorCodes.DollarPrefixedFieldName);
+
+// Cannot use constants with regular updates.
+assert.commandFailedWithCode(
+ db.runCommand({update: collName, updates: [{q: {_id: 1}, u: {x: "$$foo"}, c: {foo: "bar"}}]}),
+ 51198);
+assert.commandFailedWithCode(
+ db.runCommand(
+ {update: collName, updates: [{q: {_id: 1}, u: {$set: {x: "$$foo"}}, c: {foo: "bar"}}]}),
+ 51198);
+assert.commandFailedWithCode(
+ db.runCommand({update: collName, updates: [{q: {_id: 1}, u: {$set: {x: "1"}}, c: {}}]}), 51198);
})();