I am using the bulk operation in mongoose to update multiple documents in a concurrent manner.
The update data looks as below:
let docUpdates = [
{
id : docId1,
status : "created",
$push : {
updates : {
status: "created",
referenceId : referenceId,
createdTimestamp : "2022-06-14T00:00:00.000Z"
}
}
},
{
id : docId2,
status : "completed",
$push : {
updates : {
status: "compelted",
referenceId : referenceId,
createdTimestamp : "2022-06-14T00:00:00.000Z"
}
}
},
];
// Update documents using bulk operation
let bulkUpdate = Model.collection.initializeOrderedBulkOp();
for (let i = 0; i < docUpdates.length; i ) {
bulkUpdate.find({_id : docUpdates[i].docId}).updateOne({ $set : docUpdates[i]})
}
When I execute the above snippet, I get the below error.
(node:468) UnhandledPromiseRejectionWarning: MongoBulkWriteError: The dollar ($) prefixed field '$push' in '$push' is not allowed in the context of an update's replacement document. Consider using an aggregation pipeline with $replaceWith.
at OrderedBulkOperation.handleWriteError (/usr/src/app/node_modules/mongoose/node_modules/mongodb/lib/bulk/common.js:884:22)
Could you please help in finding a work around for this error.
Thank you,
KK
CodePudding user response:
The problem isn't that $push
isn't supported, it's that the way you pass the arguments in the update document, it's interpreted as a top-level field. See the documentation about field name considerations, specifically the section on document modifying updates.
This section of your code updateOne({ $set : docUpdates[i]})
is equivalent to the following:
updateOne({
$set: {
id: docId1,
status: "created",
$push: {
updates: {
status: "created",
referenceId: referenceId,
createdTimestamp: "2022-06-14T00:00:00.000Z"
}
}
}
})
Because the $push
is contained in the $set
, you're telling MongoDB to set the value of a field named $push
to a document with a field called updates
, which has status
, referenceId
, and createdTimestamp
fields.
What you want to do instead, is specify an update document with two different update operators. In other words, $push
needs to be on the same level as the $set
.
Something like:
updateOne({
$set: {
id: docId1,
status: "created"
},
$push: {
updates: {
status: "created",
referenceId: referenceId,
createdTimestamp: "2022-06-14T00:00:00.000Z"
}
}
})
How you represent that in the docUpdates
array is up to you. Maybe you can do something like:
let docUpdates = [
{
id : docId1,
update : {
$set : {
status : "created"
},
$push : {
updates : {
status: "created",
referenceId : referenceId,
createdTimestamp : "2022-06-14T00:00:00.000Z"
}
}
}
}
// ... Other updates
]
And use it in the loop like:
bulkUpdate.find({_id : docUpdates[i].docId}).updateOne(docUpdates[i].update)