summaryrefslogtreecommitdiff
path: root/jstests/core/command_let_variables_expressions.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/core/command_let_variables_expressions.js')
-rw-r--r--jstests/core/command_let_variables_expressions.js208
1 files changed, 208 insertions, 0 deletions
diff --git a/jstests/core/command_let_variables_expressions.js b/jstests/core/command_let_variables_expressions.js
new file mode 100644
index 00000000000..c551c30be48
--- /dev/null
+++ b/jstests/core/command_let_variables_expressions.js
@@ -0,0 +1,208 @@
+// Tests that commands like find, aggregate and update accept a 'let' parameter which defines
+// variables for use in expressions within the command. Specifically covers cases where the
+// parameter definitions contain expressions.
+// @tags: [
+// requires_non_retryable_writes,
+// does_not_support_transactions,
+// tenant_migration_incompatible
+// ]
+(function() {
+"use strict";
+
+load('jstests/aggregation/extras/utils.js'); // For assertArrayEq().
+
+const coll = db.getCollection('command_let_variables_expressions');
+
+function setupColl() {
+ coll.drop();
+ assert.commandWorked(coll.insert([
+ {_id: 2, a: {}},
+ {_id: 3, b: 1},
+ {_id: 4, a: "$notAFieldPath"},
+ ]));
+}
+setupColl();
+
+const missingLetParam = {
+ $getField: {field: "c", input: {a: 1}}
+};
+const literalLetParam = {
+ $literal: "$notAFieldPath"
+};
+
+// Run find commands with 'let' parameters containing expressions.
+{
+ // 'let' parameter expression that resolves to a missing value should not cause a query error.
+ let result = assert.commandWorked(db.runCommand({
+ find: coll.getName(),
+ filter: {$expr: {$eq: ["$a", "$$c"]}},
+ let : {c: missingLetParam},
+ }));
+ assertArrayEq({
+ actual: result.cursor.firstBatch,
+ expected: [{_id: 3, b: 1}],
+ });
+
+ // 'let' parameter expression that includes $literal and a $-prefixed string should not be
+ // misinterpreted as a field path.
+ result = assert.commandWorked(db.runCommand({
+ find: coll.getName(),
+ filter: {$expr: {$eq: ["$a", "$$c"]}},
+ let : {c: literalLetParam},
+ }));
+ assertArrayEq({
+ actual: result.cursor.firstBatch,
+ expected: [{_id: 4, a: "$notAFieldPath"}],
+ });
+}
+
+// Run aggregate commands with 'let' parameters containing expressions.
+{
+ let result =
+ coll.aggregate([{$match: {$expr: {$eq: ["$a", "$$c"]}}}], {let : {c: missingLetParam}})
+ .toArray();
+ assertArrayEq({
+ actual: result,
+ expected: [{_id: 3, b: 1}],
+ });
+
+ result = coll.aggregate([{$match: {$expr: {$eq: ["$a", "$$c"]}}}], {let : {c: literalLetParam}})
+ .toArray();
+ assertArrayEq({
+ actual: result,
+ expected: [{_id: 4, a: "$notAFieldPath"}],
+ });
+}
+
+// Same thing but for update.
+{
+ assert.commandWorked(db.runCommand({
+ update: coll.getName(),
+ updates: [{
+ q: {$expr: {$eq: ["$a", "$$c"]}},
+ u: [{$set: {c: "updated"}}],
+ multi: true,
+ }],
+ let : {c: missingLetParam}
+ }));
+ assertArrayEq({
+ actual: coll.find().toArray(),
+ expected: [{_id: 2, a: {}}, {_id: 3, b: 1, c: "updated"}, {_id: 4, a: "$notAFieldPath"}],
+ });
+
+ // Undo changes.
+ setupColl();
+
+ assert.commandWorked(db.runCommand({
+ update: coll.getName(),
+ updates: [{
+ q: {$expr: {$eq: ["$a", "$$c"]}},
+ u: [{$set: {c: "updated"}}],
+ multi: true,
+ }],
+ let : {c: literalLetParam}
+ }));
+ assertArrayEq({
+ actual: coll.find().toArray(),
+ expected: [
+ {_id: 2, a: {}},
+ {_id: 3, b: 1},
+ {_id: 4, a: "$notAFieldPath", c: "updated"},
+ ],
+ });
+}
+
+// Run findAndModify commands with 'let' parameters containing expressions.
+{
+ setupColl();
+
+ assert.commandWorked(db.runCommand({
+ findAndModify: coll.getName(),
+ query: {_id: 3, $expr: {$eq: ["$a", "$$c"]}},
+ update: [{$set: {c: "updated"}}],
+ let : {c: missingLetParam}
+ }));
+ assertArrayEq({
+ actual: coll.find().toArray(),
+ expected: [{_id: 2, a: {}}, {_id: 3, b: 1, c: "updated"}, {_id: 4, a: "$notAFieldPath"}],
+ });
+
+ // Undo changes.
+ setupColl();
+
+ assert.commandWorked(db.runCommand({
+ findAndModify: coll.getName(),
+ query: {_id: 4, $expr: {$eq: ["$a", "$$c"]}},
+ update: [{$set: {c: "updated"}}],
+ let : {c: literalLetParam}
+ }));
+ assertArrayEq({
+ actual: coll.find().toArray(),
+ expected: [
+ {_id: 2, a: {}},
+ {_id: 3, b: 1},
+ {_id: 4, a: "$notAFieldPath", c: "updated"},
+ ],
+ });
+}
+
+// Run delete commands with 'let' parameters containing expressions.
+{
+ setupColl();
+
+ assert.commandWorked(db.runCommand({
+ delete: coll.getName(),
+ deletes: [{
+ q: {$expr: {$eq: ["$a", "$$c"]}},
+ limit: 0, // multi
+ }],
+ let : {c: missingLetParam}
+ }));
+ assertArrayEq({
+ actual: coll.find().toArray(),
+ expected: [{_id: 2, a: {}}, {_id: 4, a: "$notAFieldPath"}],
+ });
+
+ // Undo changes.
+ setupColl();
+
+ assert.commandWorked(db.runCommand({
+ delete: coll.getName(),
+ deletes: [{
+ q: {$expr: {$eq: ["$a", "$$c"]}},
+ limit: 0, // multi
+ }],
+ let : {c: literalLetParam}
+ }));
+ assertArrayEq({
+ actual: coll.find().toArray(),
+ expected: [{_id: 2, a: {}}, {_id: 3, b: 1}],
+ });
+}
+
+// Run a $lookup with let/pipeline syntax. In sharded environments, this will require serializing
+// 'let' variables to send to the shards. One of the documents in the collection is missing the
+// local field ("$a"), which will cause a missing 'let' variable to be serialized.
+{
+ setupColl();
+
+ const result = coll.aggregate([
+ {
+ $lookup: {
+ from: coll.getName(),
+ as: "res",
+ let: {local_a: "$a"},
+ pipeline: [{$match: {$expr: {$eq: ["$$local_a", "$a"]},}}, {$project: {_id: 1}}]
+ }
+ }
+ ]).toArray();
+ assertArrayEq({
+ actual: result,
+ expected: [
+ {_id: 2, a: {}, res: [{_id: 2}]},
+ {_id: 3, b: 1, res: [{_id: 3}]},
+ {_id: 4, a: "$notAFieldPath", res: [{_id: 4}]},
+ ],
+ });
+}
+}());