Given is this situation:
-UserA publishs a Campaign with a given amount of photo uploads.
-other Users can upload Photos
-UserA can accept the uploaded Photos
-the owner of the photos get for each accepted photo 1 Coin
-for each accepted photo the amount of possible photos in the campaign decrease.
I solved the "accept photos" function like this (pseudocode):
accept: function (req, res, next) {
var photos = req.param('photos'); // [1, 2, 5]
var campaign_id = req.param('campaign_id'); //1
Photo.update(photos, {status:'accepted'}).exec(function afterwards(err, updatedPhotos)
{
//get the users Ids from the updatedPhotos
// also know how many photos of each user were accepted
User.update(users){
//increase the amount of coins of this user
}
Campaign.update(amountOfAcceptedPhotos){
//decrease the amount of selectable photos.
}
})
}
Everything worked well first, til I noticed when you send quickly the same request with the same photoID's, the Users will get for the same photo multiple coins! How can I prevent this?
Here is a Link for the whole Code, if you want to see the exact function: https://jsfiddle.net/zsmuoh0x/
I wrote a library that implements the two phase commit system described in the docs. It might help in this scenario. Fawn - Transactions for MongoDB. Example: Transferring $20 from one bank account to another
var Fawn = require("Fawn");
// intitialize Fawn
Fawn.init("mongodb://127.0.0.1:27017/testDB");
/**
optionally, you could initialize Fawn with mongoose
var mongoose = require("mongoose");
mongoose.connect("mongodb://127.0.0.1:27017/testDB");
Fawn.init(mongoose);
**/
// after initialization, create a task
var task = Fawn.Task();
// assuming "Accounts" is the Accounts collection
task.update("Accounts", {_id: "Sender"}, {$inc: {balance: -20}})
.update("Accounts", {_id: "Reciever"}, {$inc: {balance: 20}})
.run()
.then(function(results){
// task is complete
// result from first operation
var firstUpdateResult = results[0];
// result from second operation
var secondUpdateResult = results[1];
})
.catch(function(err){
// Everything has been rolled back.
// log the error which caused the failure
console.log(err);
});
In your particular case, I guess you are storing the user selected photos somewhere at campaigns collection for example:
{Id:999, Name: mycampaign, photos:[1,7] }
So, the update query should select the document by id AND by photo id. If you want to add picture 3:
db.campaigns.update({id:999, photos:3},. {$addToSet:{photos:3}})
If result of that is 1 documents updated, then increment the user coins.
PS: I am answering via a mobile phone