From e344a073a1cf3c3684539f0827b4f5cf461ab4b1 Mon Sep 17 00:00:00 2001 From: "A.Shakhmatov" Date: Mon, 10 Aug 2026 21:48:55 +0300 Subject: [PATCH] tests/spec: record the pipeline-style updates An update document may be an *array* of aggregation stages instead of a document of operators. The pinned crud corpus has five cases, one per command, and not one of them is a refusal -- so which stages are allowed, what happens to `_id`, and what an upsert does are all unmeasured there. 23 cases into the existing operator corpus rather than an eighth directory: this is another shape of update document, and it shares the recorder, the masking and the README. Recorded red, 0/23. What it settled: - **`_id` always survives**, through `$replaceRoot: {newRoot: "$t"}` and through `$project: {_id: 0}` alike. A stage that sets it to a *different* value is ImmutableField (66); restating the same one is fine. - `$match`, `$group`, `$sort` and `$unwind` are real stages refused specifically here: InvalidOptions (72), "$X is not allowed to be used within an update". A name that is no stage at all is 40324, and two stages packed into one array element is 40323. - `arrayFilters` beside a pipeline is FailedToParse (9). - an upsert runs the pipeline over the document the filter implies. Three shapes were authored and removed: `{b: 1, $set: {...}}`, an empty pipeline, and a pipeline holding a non-document. The driver rejects all three before they reach a server, so there is nothing to record. --- tests/spec/operators/README.md | 20 ++- tests/spec/operators/pipeline.json | 1 + tests/spec/operators/sources/pipeline.json | 163 +++++++++++++++++++++ 3 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 tests/spec/operators/pipeline.json create mode 100644 tests/spec/operators/sources/pipeline.json diff --git a/tests/spec/operators/README.md b/tests/spec/operators/README.md index 5848e54..9e34f8c 100644 --- a/tests/spec/operators/README.md +++ b/tests/spec/operators/README.md @@ -1,8 +1,9 @@ # The update-operator corpus -PLAN §3 lists eight update operators for M3 — `$setOnInsert`, `$addToSet`, -`$mul`, `$min`, `$max`, `$pop`, `$pullAll`, `$currentDate` — and the pinned -crud corpus says almost nothing about any of them. A probe running the +What an update *document* may be, measured. PLAN §3 lists eight operators for +M3 — `$setOnInsert`, `$addToSet`, `$mul`, `$min`, `$max`, `$pop`, `$pullAll`, +`$currentDate` — and pipeline-style updates beside them; the pinned crud +corpus says almost nothing about any of it. A probe running the identical update against mongod 8.3.7 and this server found: - all eight answering `bad update`, code 2, one message for every question; @@ -62,6 +63,7 @@ Recorded against mongod 8.3.7. Green: ``` array-ops.json 26 pass 0 fail 0 skip +pipeline.json 23 pass 0 fail 0 skip current-date.json 11 pass 0 fail 0 skip modifiers.json 14 pass 0 fail 0 skip numeric.json 21 pass 0 fail 0 skip @@ -103,7 +105,13 @@ None of this is guessable, and several rows contradict the obvious reading: | `$currentDate` with anything but a bool or `{$type: date\|timestamp}` | BadValue (2) | | `$setOnInsert` writing `_id` on an insert | **allowed**, unlike `$set` | | an unknown modifier | FailedToParse (9), not BadValue | +| a pipeline update's `_id` | **always survives**, through `$replaceRoot` and through `$project: {_id: 0}` | +| a stage changing `_id` to something else | ImmutableField (66) | +| `$match`, `$group`, `$sort`, `$unwind` in an update | InvalidOptions (72) — real stages, refused *here* | +| a name that is no stage at all | 40324; two stages in one array element, 40323 | +| `arrayFilters` beside a pipeline | FailedToParse (9) | -One case was authored and then removed: `{b: 1, $set: {c: 1}}` is rejected by -the driver before it reaches a server, so there is no server answer to record -and the case would have asserted nothing. +Three cases were authored and then removed, all for the same reason: the +driver rejects `{b: 1, $set: {c: 1}}`, an empty pipeline and a pipeline +holding a non-document before any of them reaches a server, so there is no +server answer to record and the cases would have asserted nothing. diff --git a/tests/spec/operators/pipeline.json b/tests/spec/operators/pipeline.json new file mode 100644 index 0000000..f7eba49 --- /dev/null +++ b/tests/spec/operators/pipeline.json @@ -0,0 +1 @@ +{"description":"pipeline","schemaVersion":"1.4","createEntities":[{"client":{"id":"client0"}},{"database":{"id":"database0","client":"client0","databaseName":"operator-corpus"}},{"collection":{"id":"collection0","database":"database0","collectionName":"coll"}}],"initialData":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}],"tests":[{"description":"$replaceRoot keeps the _id it was given","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$replaceRoot":{"newRoot":"$t"}}]},"expectResult":{"matchedCount":{"$numberInt":"1"},"modifiedCount":{"$numberInt":"1"},"upsertedCount":{"$numberInt":"0"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"u":{"v":{"$numberInt":"1"}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"$replaceWith is the same stage under its short name","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$replaceWith":"$t"}]},"expectResult":{"matchedCount":{"$numberInt":"1"},"modifiedCount":{"$numberInt":"1"},"upsertedCount":{"$numberInt":"0"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"u":{"v":{"$numberInt":"1"}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"$project keeps the _id it did not mention","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$project":{"x":{"$numberInt":"1"}}}]},"expectResult":{"matchedCount":{"$numberInt":"1"},"modifiedCount":{"$numberInt":"1"},"upsertedCount":{"$numberInt":"0"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"$project excluding _id keeps it anyway","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$project":{"_id":{"$numberInt":"0"},"x":{"$numberInt":"1"}}}]},"expectResult":{"matchedCount":{"$numberInt":"1"},"modifiedCount":{"$numberInt":"1"},"upsertedCount":{"$numberInt":"0"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"$addFields adds to the document","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$addFields":{"foo":{"$numberInt":"1"}}}]},"expectResult":{"matchedCount":{"$numberInt":"1"},"modifiedCount":{"$numberInt":"1"},"upsertedCount":{"$numberInt":"0"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}},"foo":{"$numberInt":"1"}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"$set is $addFields under its other name","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$set":{"foo":"$x"}}]},"expectResult":{"matchedCount":{"$numberInt":"1"},"modifiedCount":{"$numberInt":"1"},"upsertedCount":{"$numberInt":"0"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}},"foo":{"$numberInt":"1"}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"$unset of one field","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$unset":"y"}]},"expectResult":{"matchedCount":{"$numberInt":"1"},"modifiedCount":{"$numberInt":"1"},"upsertedCount":{"$numberInt":"0"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"$unset of several fields","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$unset":["y","t"]}]},"expectResult":{"matchedCount":{"$numberInt":"1"},"modifiedCount":{"$numberInt":"1"},"upsertedCount":{"$numberInt":"0"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"the stages run in order, each on the last one's output","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$replaceRoot":{"newRoot":"$t"}},{"$addFields":{"foo":{"$numberInt":"1"}}}]},"expectResult":{"matchedCount":{"$numberInt":"1"},"modifiedCount":{"$numberInt":"1"},"upsertedCount":{"$numberInt":"0"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"u":{"v":{"$numberInt":"1"}},"foo":{"$numberInt":"1"}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"an expression reads the document the stage was handed","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$addFields":{"z":{"$add":["$x","$y"]}}}]},"expectResult":{"matchedCount":{"$numberInt":"1"},"modifiedCount":{"$numberInt":"1"},"upsertedCount":{"$numberInt":"0"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}},"z":{"$numberInt":"2"}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"a stage may restate the _id it already has","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$addFields":{"_id":{"$numberInt":"1"}}}]},"expectResult":{"matchedCount":{"$numberInt":"1"},"modifiedCount":{"$numberInt":"0"},"upsertedCount":{"$numberInt":"0"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"a stage may not change the _id","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$addFields":{"_id":{"$numberInt":"9"}}}]},"expectError":{"isError":true,"errorCode":{"$numberInt":"66"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"updateMany runs the pipeline on every match","operations":[{"object":"collection0","name":"updateMany","arguments":{"filter":{},"update":[{"$project":{"x":{"$numberInt":"1"}}},{"$addFields":{"foo":{"$numberInt":"1"}}}]},"expectResult":{"matchedCount":{"$numberInt":"2"},"modifiedCount":{"$numberInt":"2"},"upsertedCount":{"$numberInt":"0"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"foo":{"$numberInt":"1"}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"foo":{"$numberInt":"1"}}]}]},{"description":"findOneAndUpdate with a pipeline","operations":[{"object":"collection0","name":"findOneAndUpdate","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$project":{"x":{"$numberInt":"1"}}},{"$addFields":{"foo":{"$numberInt":"1"}}}],"returnDocument":"after"},"expectResult":{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"foo":{"$numberInt":"1"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"foo":{"$numberInt":"1"}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"an upsert runs the pipeline on the document the filter implies","operations":[{"object":"collection0","name":"deleteMany","arguments":{"filter":{}}},{"object":"collection0","name":"updateOne","arguments":{"filter":{"k":{"$numberInt":"1"}},"update":[{"$addFields":{"a":{"$numberInt":"1"}}}],"upsert":true},"expectResult":{"matchedCount":{"$numberInt":"0"},"modifiedCount":{"$numberInt":"0"},"upsertedCount":{"$numberInt":"1"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$$type":"objectId"},"k":{"$numberInt":"1"},"a":{"$numberInt":"1"}}]}]},{"description":"a pipeline that writes nothing new still reports a match","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$addFields":{"x":{"$numberInt":"1"}}}]},"expectResult":{"matchedCount":{"$numberInt":"1"},"modifiedCount":{"$numberInt":"0"},"upsertedCount":{"$numberInt":"0"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"$match is not allowed inside an update","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$match":{"x":{"$numberInt":"1"}}}]},"expectError":{"isError":true,"errorCode":{"$numberInt":"72"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"$group is not allowed inside an update","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$group":{"_id":null}}]},"expectError":{"isError":true,"errorCode":{"$numberInt":"72"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"$sort is not allowed inside an update","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$sort":{"x":{"$numberInt":"1"}}}]},"expectError":{"isError":true,"errorCode":{"$numberInt":"72"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"$unwind is not allowed inside an update","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$unwind":"$t"}]},"expectError":{"isError":true,"errorCode":{"$numberInt":"72"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"a stage name that is not a stage at all","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$bogus":{}}]},"expectError":{"isError":true,"errorCode":{"$numberInt":"40324"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"two stages packed into one array element","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$addFields":{"a":{"$numberInt":"1"}},"$unset":"y"}]},"expectError":{"isError":true,"errorCode":{"$numberInt":"40323"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]},{"description":"arrayFilters alongside a pipeline","operations":[{"object":"collection0","name":"updateOne","arguments":{"filter":{"_id":{"$numberInt":"1"}},"update":[{"$addFields":{"a":{"$numberInt":"1"}}}],"arrayFilters":[{"i.b":{"$numberInt":"1"}}]},"expectError":{"isError":true,"errorCode":{"$numberInt":"9"}}}],"outcome":[{"collectionName":"coll","databaseName":"operator-corpus","documents":[{"_id":{"$numberInt":"1"},"x":{"$numberInt":"1"},"y":{"$numberInt":"1"},"t":{"u":{"v":{"$numberInt":"1"}}}},{"_id":{"$numberInt":"2"},"x":{"$numberInt":"2"},"y":{"$numberInt":"1"}}]}]}]} diff --git a/tests/spec/operators/sources/pipeline.json b/tests/spec/operators/sources/pipeline.json new file mode 100644 index 0000000..b2afdc5 --- /dev/null +++ b/tests/spec/operators/sources/pipeline.json @@ -0,0 +1,163 @@ +{ + "_comment": [ + "Inputs only. Expectations are measured -- see record.js.", + "Pipeline-style updates: an update document that is an *array* of", + "aggregation stages rather than a document of operators. The pinned crud", + "corpus has five cases, one per command, and none of them is a refusal --", + "so which stages are allowed, what happens to `_id`, and what an upsert", + "does are all unmeasured there.", + "Two shapes were authored and removed: an empty pipeline and an array", + "holding a non-document. The driver rejects both before they reach a", + "server ('Update document requires atomic operators'), so there is no", + "server answer to record." + ], + "documents": [ + { "_id": 1, "x": 1, "y": 1, "t": { "u": { "v": 1 } } }, + { "_id": 2, "x": 2, "y": 1 } + ], + "cases": [ + { + "description": "$replaceRoot keeps the _id it was given", + "operation": "updateOne", + "arguments": { + "filter": { "_id": 1 }, + "update": [{ "$replaceRoot": { "newRoot": "$t" } }] + } + }, + { + "description": "$replaceWith is the same stage under its short name", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$replaceWith": "$t" }] } + }, + { + "description": "$project keeps the _id it did not mention", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$project": { "x": 1 } }] } + }, + { + "description": "$project excluding _id keeps it anyway", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$project": { "_id": 0, "x": 1 } }] } + }, + { + "description": "$addFields adds to the document", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$addFields": { "foo": 1 } }] } + }, + { + "description": "$set is $addFields under its other name", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$set": { "foo": "$x" } }] } + }, + { + "description": "$unset of one field", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$unset": "y" }] } + }, + { + "description": "$unset of several fields", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$unset": ["y", "t"] }] } + }, + { + "description": "the stages run in order, each on the last one's output", + "operation": "updateOne", + "arguments": { + "filter": { "_id": 1 }, + "update": [{ "$replaceRoot": { "newRoot": "$t" } }, { "$addFields": { "foo": 1 } }] + } + }, + { + "description": "an expression reads the document the stage was handed", + "operation": "updateOne", + "arguments": { + "filter": { "_id": 1 }, + "update": [{ "$addFields": { "z": { "$add": ["$x", "$y"] } } }] + } + }, + { + "description": "a stage may restate the _id it already has", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$addFields": { "_id": 1 } }] } + }, + { + "description": "a stage may not change the _id", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$addFields": { "_id": 9 } }] } + }, + { + "description": "updateMany runs the pipeline on every match", + "operation": "updateMany", + "arguments": { + "filter": {}, + "update": [{ "$project": { "x": 1 } }, { "$addFields": { "foo": 1 } }] + } + }, + { + "description": "findOneAndUpdate with a pipeline", + "operation": "findOneAndUpdate", + "arguments": { + "filter": { "_id": 1 }, + "update": [{ "$project": { "x": 1 } }, { "$addFields": { "foo": 1 } }], + "returnDocument": "after" + } + }, + { + "description": "an upsert runs the pipeline on the document the filter implies", + "documents": [], + "operation": "updateOne", + "arguments": { + "filter": { "k": 1 }, + "update": [{ "$addFields": { "a": 1 } }], + "upsert": true + } + }, + { + "description": "a pipeline that writes nothing new still reports a match", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$addFields": { "x": 1 } }] } + }, + { + "description": "$match is not allowed inside an update", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$match": { "x": 1 } }] } + }, + { + "description": "$group is not allowed inside an update", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$group": { "_id": null } }] } + }, + { + "description": "$sort is not allowed inside an update", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$sort": { "x": 1 } }] } + }, + { + "description": "$unwind is not allowed inside an update", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$unwind": "$t" }] } + }, + { + "description": "a stage name that is not a stage at all", + "operation": "updateOne", + "arguments": { "filter": { "_id": 1 }, "update": [{ "$bogus": {} }] } + }, + { + "description": "two stages packed into one array element", + "operation": "updateOne", + "arguments": { + "filter": { "_id": 1 }, + "update": [{ "$addFields": { "a": 1 }, "$unset": "y" }] + } + }, + { + "description": "arrayFilters alongside a pipeline", + "operation": "updateOne", + "arguments": { + "filter": { "_id": 1 }, + "update": [{ "$addFields": { "a": 1 } }], + "arrayFilters": [{ "i.b": 1 }] + } + } + ] +}