mongo/jstests/core/index/express_write.js

228 lines
7.3 KiB
JavaScript

/**
* Tests the express code path, which bypasses regular query planning and execution, for writes.
* Verifies some basic eligibility restrictions such as match expression shape and index options,
* and checks the query results.
* @tags: [
* requires_fcv_81,
* requires_non_retryable_commands,
* requires_non_retryable_writes,
* requires_getmore,
* ]
*/
import {assertArrayEq} from "jstests/aggregation/extras/utils.js";
import {isExpress} from "jstests/libs/query/analyze_plan.js";
const collName = "express_write_coll";
const coll = db.getCollection(collName);
const docs = [
{_id: 0, a: 0},
{_id: "str", a: 1},
];
function runExpressTest({command, expectedDocs, usesExpress}) {
// Reset the collection docs.
assert.commandWorked(coll.remove({}));
assert.commandWorked(coll.insert(docs));
// Run the command to make sure it succeeds.
assert.commandWorked(db.runCommand(command));
assertArrayEq({
actual: coll.find().toArray(),
expected: expectedDocs,
extraErrorMsg: "Result set comparison failed for command: " + tojson(command),
});
// Reset the collection docs then run explain.
assert.commandWorked(coll.remove({}));
assert.commandWorked(coll.insert(docs));
const explain = assert.commandWorked(db.runCommand({explain: command, verbosity: "executionStats"}));
assert.eq(
usesExpress,
isExpress(db, explain),
"Expected the query to " + (usesExpress ? "" : "not ") + "use express: " + tojson(explain),
);
}
coll.drop();
// Cannot use express path when predicate is not a single equality or when a projection is present.
runExpressTest({
command: {delete: collName, deletes: [{q: {_id: 0, a: 0}, limit: 0}]},
usesExpress: false,
expectedDocs: [docs[1]],
});
runExpressTest({
command: {delete: collName, deletes: [{q: {_id: 0, a: 0}, limit: 1}]},
usesExpress: false,
expectedDocs: [docs[1]],
});
runExpressTest({
command: {findAndModify: collName, query: {_id: "str", a: 1}, update: [{$set: {c: 1}}]},
usesExpress: false,
expectedDocs: [docs[0], {_id: "str", a: 1, c: 1}],
});
runExpressTest({
command: {findAndModify: collName, query: {_id: 0}, remove: true, fields: {_id: 1, a: 1}},
usesExpress: false,
expectedDocs: [docs[1]],
});
runExpressTest({
command: {
findAndModify: collName,
query: {_id: "str"},
update: {$set: {c: 1}},
fields: {_id: 1, a: 1, c: 1},
},
usesExpress: false,
expectedDocs: [docs[0], {_id: "str", a: 1, c: 1}],
});
runExpressTest({
command: {update: collName, updates: [{q: {_id: "str", a: 1}, u: {$set: {c: 1}}}]},
usesExpress: false,
expectedDocs: [docs[0], {_id: "str", a: 1, c: 1}],
});
// Cannot use express path when a hint is specified.
assert.commandWorked(coll.createIndex({a: 1}));
runExpressTest({
command: {delete: collName, deletes: [{q: {_id: 0}, limit: 0, hint: {a: 1}}]},
usesExpress: false,
expectedDocs: [docs[1]],
});
runExpressTest({
command: {findAndModify: collName, query: {_id: "str"}, update: [{$set: {c: 1}}], hint: {a: 1}},
usesExpress: false,
expectedDocs: [docs[0], {_id: "str", a: 1, c: 1}],
});
runExpressTest({
command: {update: collName, updates: [{q: {_id: "str"}, u: {$set: {c: 1}}, hint: {a: 1}}]},
usesExpress: false,
expectedDocs: [docs[0], {_id: "str", a: 1, c: 1}],
});
coll.dropIndexes();
// Can use express path use for delete commands when the predicate is a single equality on _id,
// regardless of the 'limit' value.
runExpressTest({
command: {delete: collName, deletes: [{q: {_id: 0}, limit: 0}]},
usesExpress: true,
expectedDocs: [docs[1]],
});
runExpressTest({
command: {delete: collName, deletes: [{q: {_id: 0}, limit: 1}]},
usesExpress: true,
expectedDocs: [docs[1]],
});
runExpressTest({
command: {delete: collName, deletes: [{q: {_id: "str"}, limit: 0}]},
usesExpress: true,
expectedDocs: [docs[0]],
});
// Same, but with query shape {_id: {$eq: <val>}}.
runExpressTest({
command: {delete: collName, deletes: [{q: {_id: {$eq: 0}}, limit: 0}]},
usesExpress: true,
expectedDocs: [docs[1]],
});
runExpressTest({
command: {delete: collName, deletes: [{q: {_id: {$eq: 0}}, limit: 1}]},
usesExpress: true,
expectedDocs: [docs[1]],
});
runExpressTest({
command: {delete: collName, deletes: [{q: {_id: {$eq: "str"}}, limit: 0}]},
usesExpress: true,
expectedDocs: [docs[0]],
});
// Can use express path use for findAndModify commands when the predicate is a single equality on
// _id. This is true whether the findAndModify represents an update or a delete.
runExpressTest({
command: {findAndModify: collName, query: {_id: "str"}, update: [{$set: {c: 1}}]},
usesExpress: true,
expectedDocs: [docs[0], {_id: "str", a: 1, c: 1}],
});
runExpressTest({
command: {findAndModify: collName, query: {_id: 0}, update: [{$set: {c: 1}}]},
usesExpress: true,
expectedDocs: [{_id: 0, a: 0, c: 1}, docs[1]],
});
runExpressTest({
command: {findAndModify: collName, query: {_id: 0}, remove: true},
usesExpress: true,
expectedDocs: [docs[1]],
});
// Same, but with query shape {_id: {$eq: <val>}}.
runExpressTest({
command: {findAndModify: collName, query: {_id: {$eq: "str"}}, update: [{$set: {c: 1}}]},
usesExpress: true,
expectedDocs: [docs[0], {_id: "str", a: 1, c: 1}],
});
runExpressTest({
command: {findAndModify: collName, query: {_id: {$eq: 0}}, update: [{$set: {c: 1}}]},
usesExpress: true,
expectedDocs: [{_id: 0, a: 0, c: 1}, docs[1]],
});
runExpressTest({
command: {findAndModify: collName, query: {_id: {$eq: 0}}, remove: true},
usesExpress: true,
expectedDocs: [docs[1]],
});
// Can use express path use for update commands when the predicate is a single equality on _id.
runExpressTest({
command: {update: collName, updates: [{q: {_id: "str"}, u: {$set: {c: 1}}}]},
usesExpress: true,
expectedDocs: [docs[0], {_id: "str", a: 1, c: 1}],
});
runExpressTest({
command: {update: collName, updates: [{q: {_id: 0}, u: {$set: {c: 1}}}]},
usesExpress: true,
expectedDocs: [{_id: 0, a: 0, c: 1}, docs[1]],
});
// Same, but with query shape {_id: {$eq: <val>}}.
runExpressTest({
command: {update: collName, updates: [{q: {_id: {$eq: "str"}}, u: {$set: {c: 1}}}]},
usesExpress: true,
expectedDocs: [docs[0], {_id: "str", a: 1, c: 1}],
});
runExpressTest({
command: {update: collName, updates: [{q: {_id: {$eq: 0}}, u: {$set: {c: 1}}}]},
usesExpress: true,
expectedDocs: [{_id: 0, a: 0, c: 1}, docs[1]],
});
docs.push({_id: 2, arr: [99, 100, 101]});
runExpressTest({
command: {
update: collName,
updates: [
{
q: {_id: 2},
u: {$set: {"arr.$[element]": 100}},
arrayFilters: [{"element": {$gte: 100}}],
},
],
},
usesExpress: true,
expectedDocs: [docs[0], docs[1], {_id: 2, arr: [99, 100, 100]}],
});
// Same, but with query shape {_id: {$eq: <val>}}.
runExpressTest({
command: {
update: collName,
updates: [
{
q: {_id: {$eq: 2}},
u: {$set: {"arr.$[element]": 100}},
arrayFilters: [{"element": {$gte: 100}}],
},
],
},
usesExpress: true,
expectedDocs: [docs[0], docs[1], {_id: 2, arr: [99, 100, 100]}],
});