Similar to your previous question, you use .bulkWrite() but since the array element selection has "multiple conditions" this is where you use $elemMatch:
db.collection.bulkWrite([
{ "updateOne": {
"filter": {
"_id": "1",
"option": {
"$elemMatch": { "weight": "40", "size": "40" }
}
},
"update": {
"$set": { "option.$.price": "300" }
}
}},
{ "updateOne": {
"filter": {
"_id": "1",
"option": {
"$not": {
"$elemMatch": { "weight": "40", "size": "40" }
}
}
},
"update": {
"$push": { "option": { "weight": "40", "size": "40", "price": "300" } }
}
}},
{ "updateOne": {
"filter": { "_id": 1 },
"update": {
"$setOnInsert": {
"option": [
{ "weight": "40", "size": "40", "price": "300" }
]
}
},
"upsert": true
}}
])
So the operations are:
Test that the array element matching conditions in $elemMatch is present and then $set the matched value.
Test the array element is $not present in negation. You could alternately use $ne on each property, but negating the condition where both match is a bit cleaner.
"$elemMatch": { "weight": { "$ne": "40" }, "size": { "$ne": "40" } }
At any rate you $push the new array element when one not matching the supplied criteria is found.
Attempt an "upsert" only where the primary document _id is not found, and use $setOnInsert so that if the document is found this operation does nothing.
Same as before, only one of these will actually write anything despite the whole batch being sent to the server.