I seem to have a problem with batch-updates that I run in the background. Periodically, a job runs that goes over all elements in one collection and updates them.
I wrote a few helper functions, which should ensure that only available resources are taken and the collection is updated properly:
static getStore(warehouse, resource) {
return warehouse.storage.find(x => x.resource == resource)
}
static getStoreAmount(warehouse, resource) {
let store = this.getStore(warehouse, resource)
if (store) {
return store.amount
} else {
return 0
}
}
static addToStorage(warehouse, resource, amount) {
if (amount <= 0) return true;
console.log("adding "+amount+" "+resource)
let store = this.getStore(warehouse, resource)
// TODO: check if enough storage space is left
// FIXME: somehow, something created a duplicate entry for grain here (from 2 fields) - async database calls?
// with 3 fields, it gets added 3 times - definitely reliably bugged :-(
// FIXME: somehow, despite adding twice, nothign was left - again async update call just before $pull'ing it?
if (store) {
Warehouses.update({"_id":warehouse._id, "storage.resource": resource}, {
$set: {
"storage.$.amount": store.amount + amount
}
})
} else {
Warehouses.update({"_id":warehouse._id}, {
$push: {
storage: {
resource: resource,
amount: amount,
buy: { amount: 0, price: 0 },
sell: { amount: 0, price: 0 }
}
}
})
}
return true
}
static checkTakeFromStorage(warehouse, resource, amount, min=1) {
if (amount <= 0) return 0;
let have = this.getStoreAmount(warehouse,resource)
if (have < min) {
return 0
}
console.log("taking "+amount+" "+resource)
if (amount >= have) {
// FIXME: don't pull if we have buy or sell values !
amount = have
// $pull is tricky because of meteor limitations, so here is a workaround I found on the Internet:
let raw = Warehouses.rawCollection()
let findOneAndUpdate = Meteor.wrapAsync(raw.findOneAndUpdate, raw)
findOneAndUpdate({_id: warehouse._id}, {
$pull: { storage: { resource: resource }}
})
} else {
Warehouses.update({"_id":warehouse._id, "storage.resource": resource}, {
$set: {
"storage.$.amount": have - amount
}
})
}
return amount
}
the data structure is like this:
warehouses {
...various things...
storage: [
{ resource: "a", amount: 123 },
{ resource: "b", amount: 456 },
...
]
}
however, when I run the batch job, contrary to my intended code, multiple entries are made in the storage table for the same resource. I’m probably doing my updates in a stupid way and there is probably a proper meteor/mongo way to do them that guarantees this cannot happen, but I couldn’t find it when I searched for it, due to the fact that I update an array in an embedded document.