diff options
| author | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-18 17:02:53 -0300 |
|---|---|---|
| committer | Lucas de Castro Borges <lucas@gnuabordo.com.br> | 2025-02-18 17:02:53 -0300 |
| commit | 959575a5ca598bf5f37fb5cebe7ed1d80d3d71f7 (patch) | |
| tree | acc8d60aedb12b70048e676e8a7349deb0010db8 /jstests/core/command_let_variables_expressions.js | |
| parent | 76588293975fc059cf076779e4283e6ffaf8afff (diff) | |
New upstream version 6.0.20upstream
Diffstat (limited to 'jstests/core/command_let_variables_expressions.js')
| -rw-r--r-- | jstests/core/command_let_variables_expressions.js | 208 |
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}]}, + ], + }); +} +}()); |
