Making an efficient and healthy db call for a real-time browser game
The power of the aggregation framework is it's ability to iterate over the dataset in various useful ways without incurring extra round trips between the database and the app.

Your code uses one stage for aggregation (grouping by id1), and then jumps out of aggregation to iterate over the entire FUNDS collection for every user. That is very expensive, giving you exponential time complexity or worse.

The code below makes the database do all the heavy lifting in 3 aggregation framework stages, and returns an array with all the necessary data.


pivot over the user_id, calculating invest_amount, saving unique records in an array called records
unwind records. Now you have your original documents, but with a useful extra field.
calculate profit


Then we end up with an array of objects which can be iterated over, applying the the $inc update.

FUNDS.aggregate([
    {
        "$group": {
            "_id": '$id1',
            "invest_total": {"$sum": "$invest_amount"},
            "records": {"$push": "$$ROOT"}
        }
    },
    {
        "$unwind": "$records"
    },
    {
        "$project": {
            "invest_id": "$records.invest_id",
            "user_id": "$records.id1",
            "invest_total": "$invest_total",
            "invest_amount": "$records.invest_amount",
            "profit": {
                "$divide": [
                    { "$multiply": ["$records.invest_amount", AN_AMOUNT_FROM_SITE ] },
                    "$invest_total"
                ]
            }
        }
    }
]).exec(function(err,docs){
    docs.forEach(function(d){
        FUNDS.findOneAndUpdate(
            { "invest_id": d.invest_id},
            { "$inc": { 'invest_profit': d.profit } },
            function(e3){
                callback();
            }
        );
    });
});