My data model has Accounts, and an accounts has some credit transactions for it. I have designed these transactions as subdocuments:
var TransactionSchema = new Schema({
amount: Number,
added: Date
}),
AccountSchema = new Schema({
owner: ObjectId,
balance: Number,
transactions: [TransactionSchema]
});
When a Transaction
is added to an Account
, the following should happen:
transactions
has the new one pushed to ittransactions
is sorted by date (for later display)balance
is set to the sum of all transactions
I have put that in a Schema.methods
-function for now, doing the above in JavaScript, before a save. However, I am not sure that is secure for multiple inserts at once.
How can this better be solved in Mongoose to use atomic or some kind of transactional update? In SQL, I would just do a transaction, but I cannot in MongoDB, so how to make sure that transactions
and balance
are always correct?
You can do all of that with a single update
call that combines all three of those operations (which is the only way to make a combination of updates atomic). You don't sum the transactions during the update, instead you update balance
with the amount of the change:
var transaction = {
amount: 500,
added: new Date()
};
Account.update({owner: owner}, {
// Adjust the balance by the amount in the transaction.
$inc: {balance: transaction.amount},
// Add the transaction to transactions while sorting by added.
$push: {transactions: {
$each: [transaction],
$sort: {added: 1}
}}
}, callback);
Note that this does utilize the $sort
modifier for the $push
which was added in 2.4 and updated in 2.6 so you need to be using a recent build.