Unfortunately, this cannot be done in a single update statement using an upsert
or similar.
This can be achieved using bulkWrite() which is supported from MongoDB 3.2 onwards.
You can also use the new update() command found in MongoDB 4.2 - note this is only available in 4.2.
The update command now allows multiple update statements. While you may have to run two update commands, you only need to send the command once and have MongoDB batch the statements for you.
Given your requirement, you can try the following which will first try and update the relevant element in the array using the $ positional operator.
We then use the $addToSet array operator which will attempt to add a new array element - only if no matching array element is found perfect for our scenario where no update could be done in step 1.
Both solutions work for your scenario.
Using bulkWrite()
db.getCollection("tests").bulkWrite([
{
updateOne: {
filter: {link: "abc.com", "Values.valueID": "v2"},
update: {$set: {"Values.$.value": "xyz"}}
}
},
{
updateOne: {
filter: {link: "abc.com"},
update: {
$addToSet: {
"Values": {
"valueID": "v2",
"date": "05-07-2015",
"value": "xyz"
}
}
}
}
}
]);
Using new update() command:
db.runCommand(
{
update: "tests",
updates: [
{
q: {link: "abc.com", "Values.valueID": "v2"},
u: {$set: {"Values.$.value": "xyz"}}
},
{
q: {link: "abc.com"},
u: {
$addToSet: {
"Values": {
"valueID": "v2",
"date": "05-07-2015",
"value": "xyz"
}
}
}
}
],
ordered: false,
writeConcern: {w: "majority", wtimeout: 5000}
}
)
Sample Data:
db.getCollection("tests").insertMany([{
"link": "abc.com",
"Values": [
{
"valueID": "v1",
"date": "05-07-2015",
"value": "10"
}
]
},
{
"link": "def.com",
"Values": [
{
"valueID": "v1",
"date": "05-07-2015",
"value": "1"
}
]
}]
);
Insert array when none exists:
db.runCommand(
{
update: "tests",
updates: [
{
q: {link: "abc.com", "Values.valueID": "v2"},
u: {$set: {"Values.$.value": "xyz"}}
},
{
q: {link: "abc.com"},
u: {
$addToSet: {
"Values": {
"valueID": "v2",
"date": "05-07-2015",
"value": "xyz"
}
}
}
}
],
ordered: false,
writeConcern: {w: "majority", wtimeout: 5000}
}
)
Result:
{
"_id" : ObjectId("5dd8164969f4361ce9821b88"),
"link" : "abc.com",
"Values" : [
{
"valueID" : "v1",
"date" : "05-07-2015",
"value" : "20"
},
{
"valueID" : "v2",
"date" : "05-07-2015",
"value" : "xyz"
}
]
}
Update an existing value:
db.runCommand(
{
update: "tests",
updates: [
{
q: {link: "abc.com", "Values.valueID": "v2"},
u: {$set: {"Values.$.value": "new value"}}
},
{
q: {link: "abc.com"},
u: {
$addToSet: {
"Values": {
"valueID": "v2",
"date": "05-07-2015",
"value": "new value"
}
}
}
}
],
ordered: false,
writeConcern: {w: "majority", wtimeout: 5000}
}
)
Result:
{
"_id" : ObjectId("5dd8164969f4361ce9821b88"),
"link" : "abc.com",
"Values" : [
{
"valueID" : "v1",
"date" : "05-07-2015",
"value" : "20"
},
{
"valueID" : "v2",
"date" : "05-07-2015",
"value" : "new value"
}
]
}