I'm trying to delete the subobject 'apples' from my documents and update the 'fruitInventory' property, decrease by the amount of apples.
I'm confused on how to proceed, should I use dot notation or do a full text search for apples? I don't know if this matters but you can assume apples will always be in field 1.
// Document 1
{
"1": {
"apples": 3,
"fruitInventory": 21,
"oranges": 12,
"kiwis": 3,
"lemons": 3
},
"2": {
"bananas": 4,
"fruitInventory": 12,
"oranges": 8,
},
"_id": "1"
}
// Document 2
{
"1": {
"apples": 5,
"fruitInventory": 10,
"oranges": 2,
"pears": 3
},
"2": {
"bananas": 4,
"fruitInventory": 6,
"cherries": 2,
},
"_id": "2"
}
Result should be like this:
// Document 1
{
"1": {
"fruitInventory": 18,
"oranges": 12,
"kiwis": 3,
"lemons": "3"
},
"2": {
"bananas": 4,
"fruitInventory": 12,
"oranges": 8,
},
"_id": "1"
}
// Document 2
{
"1": {
"fruitInventory": 5,
"oranges": "2",
"pears": "3"
},
"2": {
"bananas": 4,
"fruitInventory": 6,
"cherries": 2,
},
"_id": "2"
}
Thanks in advance for your help.
In the MongoDB query language there is no way to update a document using information from the document. In other words, there is no way to increment one property with a value from another property. To be specific: there is no way to increment the fruitInventory
by X where X is the value of the apples
property as an atomic operation.
The way I would implement this, while avoiding races, is with two findAndModify
operations (seen here in Mongo shell syntax):
var fruit = db.fruit.findAndModify({
query: {locked: {$ne: true}, "1.apples": {$exists: true}},
update: {$set: {locked: true}}
});
var fruitInventory = fruit["1"]["fruitInventory"];
var apples = fruit["1"]["apples"];
db.fruit.findAndModify({
query: {"_id": fruit["_id"]},
update: {
$set: {"1.fruitInventory": fruitInventory + apples},
$unset: {locked: false, "1.apples": true}
}
});
What happens is this: first I find a document which is not locked (more on this later), and has an 1.apples
property. The document is returned and updated to get a locked
property as a single atomic operation. Since the document now has a locked
property it will not be found by the same query, so this query can be run multiple times in parallel without any risk of modifying the same document twice.
I then extract the 1.fruitInventory
and 1.apples
, to simplify the next expression.
The second findAndModify
updates the 1.fruitInventory
property with the new sum, and unsets the 1.apples
property, as well as the locked
property (to return the document to its previous, unlocked, state).
To update all documents you will have to run this code over and over again until the first findAndModify
returns null
(meaning there are no documents matching the query).