Files
mongo/jstests/core/command_let_variables_expressions.js
Erwin Pe dbf3bd8432 SERVER-90234 Allow jsTestName() for SELinux tests (#22535)
GitOrigin-RevId: 2413ea8f3bec5aa5c87bb9879aefa803585d5d37
2024-05-30 21:15:11 +00:00

210 lines
5.8 KiB
JavaScript

// 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
// ]
import {assertArrayEq} from "jstests/aggregation/extras/utils.js";
const coll = db.getCollection(jsTestName());
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}]},
],
});
}