db: a write that changes nothing is not a write
`nModified` counted every write, so an update that altered nothing was reported
as a modification. MongoDB counts a document as modified only if applying the
update changed it, and writes no oplog entry when it did not: `$set: {x: 11}`
on a document already holding `x: 11` is matched and not modified. The spec
suite says it plainly -- `bulkWrite` with four updateOne operations expects
matchedCount 2 and modifiedCount 1.
Decided in the engine rather than the command, because that is where the
document is already serialized: the comparison is against the bytes that would
actually be stored, and it lands before the log append, so a no-op costs no log
record, no fsync, no slab bytes and no garbage. `Engine.replace` returns
`Written.modified` or `.unchanged` and `cmd_update` counts the first.
That exposed a second difference. A replacement keeps `_id` at the front, so
replacing a document with itself was a byte-level change whenever `_id` was not
stored first -- and it usually was not: the Node driver fills a missing `_id` by
assigning the property, which in JavaScript appends it, so `insertOne({name,
age})` reaches the server as `{name, age, _id}` and we stored it that way.
MongoDB moves `_id` to the front whatever order it arrives in. Now so does
`serialize_with_id`, for every document rather than only the ones whose `_id` it
generates. Visible to clients as `_id` coming back first, as it does from
MongoDB.
spec scorecard 161 pass / 131 fail -> 163 pass / 129 fail
bulkWrite.json 8 pass / 2 fail -> 10 pass / 0 fail
e2e.js 45 checks -> 49
No spec file regressed. Mutation: delete the byte comparison in `upsert`'s
`.replace` arm -- red on the log growing, on the garbage counters moving, and on
`replace` claiming `.modified`.
This commit is contained in:
@@ -60,6 +60,30 @@ async function main() {
|
||||
const um = await users.updateMany({}, { $set: { seen: true } });
|
||||
check('updateMany', um.modifiedCount === 4, um);
|
||||
|
||||
// `_id` comes back first, whatever order the client sent it in. The driver
|
||||
// fills a missing `_id` by assigning the property, which in JavaScript appends
|
||||
// it, so this document reaches the server as {name, age, _id}.
|
||||
const ordered = db.collection('ordering');
|
||||
await ordered.drop().catch(() => {});
|
||||
await ordered.insertOne({ zzz: 1, aaa: 2 });
|
||||
const orderedDoc = await ordered.findOne({});
|
||||
check('_id is stored first however the client ordered it', Object.keys(orderedDoc)[0] === '_id', Object.keys(orderedDoc).join(','));
|
||||
await ordered.insertOne({ mid: 1, _id: 'explicit', tail: 2 });
|
||||
const explicitDoc = await ordered.findOne({ _id: 'explicit' });
|
||||
check(
|
||||
'_id moves to the front and the other fields keep their order',
|
||||
Object.keys(explicitDoc).join(',') === '_id,mid,tail',
|
||||
Object.keys(explicitDoc).join(','),
|
||||
);
|
||||
|
||||
// Matched but not modified: MongoDB counts a document as modified only if the
|
||||
// update altered it, and writes nothing when it did not. Setting a field to
|
||||
// the value it already holds is the plain case.
|
||||
const noop = await users.updateMany({}, { $set: { seen: true } });
|
||||
check('a no-op update matches without modifying', noop.matchedCount === 4 && noop.modifiedCount === 0, JSON.stringify(noop));
|
||||
const noopRep = await users.replaceOne({ name: 'alice' }, await users.findOne({ name: 'alice' }));
|
||||
check('replacing a document with itself modifies nothing', noopRep.matchedCount === 1 && noopRep.modifiedCount === 0, JSON.stringify(noopRep));
|
||||
|
||||
const push = await users.updateOne({ name: 'dave' }, { $push: { tags: 'x' } });
|
||||
check('$push', push.modifiedCount === 1);
|
||||
check('$push visible', (await users.findOne({ name: 'dave' })).tags.length === 1);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# semantics; ignoring them makes some cases pass that a full runner would
|
||||
# fail, so treat `pass` as an upper bound until M1 wires events up.
|
||||
|
||||
total 161 pass 131 fail 195 skip 175 files 0 errored
|
||||
total 163 pass 129 fail 195 skip 175 files 0 errored
|
||||
|
||||
# per-file: name pass fail skip
|
||||
aggregate-allowdiskuse.json 3 0 0
|
||||
@@ -57,7 +57,7 @@ bulkWrite-updateOne-let.json 0 1 1
|
||||
bulkWrite-updateOne-pipeline.json 0 1 0
|
||||
bulkWrite-updateOne-rawdata.json 0 1 1
|
||||
bulkWrite-updateOne-sort.json 1 0 1
|
||||
bulkWrite.json 8 2 0
|
||||
bulkWrite.json 10 0 0
|
||||
bypassDocumentValidation.json 8 1 0
|
||||
client-bulkWrite-delete-options.json 0 0 2
|
||||
client-bulkWrite-delete-rawdata.json 0 0 2
|
||||
@@ -213,7 +213,7 @@ aggregate.json SKIP aggregate with a document comment - pre 4.4 needs server <=
|
||||
aggregate.json SKIP aggregate with comment does not set comment on getMore - pre 4.4 needs server <= 4.3.99
|
||||
bulkWrite-arrayFilters.json FAIL BulkWrite updateOne with arrayFilters outcome crud-tests.test[0].y: expected an array, got {"$[i]":{"b":2}}
|
||||
bulkWrite-arrayFilters.json FAIL BulkWrite updateMany with arrayFilters outcome crud-tests.test[0].y: expected an array, got {"$[i]":{"b":2}}
|
||||
bulkWrite-arrayFilters.json FAIL BulkWrite with arrayFilters outcome crud-tests.test[0].y: expected an array, got {"$[i]":{"b":2}}
|
||||
bulkWrite-arrayFilters.json FAIL BulkWrite with arrayFilters bulkWrite.modifiedCount: expected 3, got 2
|
||||
bulkWrite-collation.json FAIL BulkWrite with delete operations and collation bulkWrite.deletedCount: expected 4, got 0
|
||||
bulkWrite-collation.json FAIL BulkWrite with update operations and collation bulkWrite.matchedCount: expected 6, got 2
|
||||
bulkWrite-comment.json SKIP BulkWrite with comment - pre 4.4 needs server <= 4.2.99
|
||||
@@ -266,8 +266,6 @@ bulkWrite-updateOne-pipeline.json FAIL UpdateOne in bulk write using pipelines M
|
||||
bulkWrite-updateOne-rawdata.json SKIP BulkWrite updateOne with rawData option needs server >= 8.2.0
|
||||
bulkWrite-updateOne-rawdata.json FAIL BulkWrite updateOne with rawData option on less than 8.2.0 - ignore argument MongoBulkWriteError: update spec requires u
|
||||
bulkWrite-updateOne-sort.json SKIP BulkWrite updateOne with sort option needs server >= 8.0
|
||||
bulkWrite.json FAIL BulkWrite with updateOne operations bulkWrite.modifiedCount: expected 1, got 2
|
||||
bulkWrite.json FAIL BulkWrite with updateMany operations bulkWrite.modifiedCount: expected 2, got 4
|
||||
bypassDocumentValidation.json FAIL Aggregate with $out passes bypassDocumentValidation: false MongoServerError: Unrecognized pipeline stage name: '$out'
|
||||
client-bulkWrite-delete-options.json SKIP * needs server >= 8.0
|
||||
client-bulkWrite-delete-rawdata.json SKIP client bulk write delete with rawData option needs server >= 8.2.0
|
||||
|
||||
Reference in New Issue
Block a user