Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB aggregation $divide computed fields

Tags:

mongodb

I am trying to compute a percentage in a MongoDB query based on computed fields - not sure if this is possible or not. What I'd like to be able to do is calculate the failure percentage: (failed count / total) * 100

Here are a few sample documents:

    {
            "_id" : ObjectId("52dda5afe4b0a491abb5407f"),
            "type" : "build",
            "time" : ISODate("2014-01-20T22:39:43.880Z"),
            "data" : {
                    "buildNumber" : 30,
                    "buildResult" : "SUCCESS"
            }
    },
    {
            "_id" : ObjectId("52dd9fede4b0a491abb5407a"),
            "type" : "build",
            "time" : ISODate("2014-01-20T22:15:07.901Z"),
            "data" : {
                    "buildNumber" : 4,
                    "buildResult" : "FAILURE"
            }
    },
    {
            "_id" : ObjectId("52dda153e4b0a491abb5407b"),
            "type" : "build",
            "time" : ISODate("2014-01-20T22:21:07.790Z"),
            "data" : {
                    "buildNumber" : 118,
                    "buildResult" : "SUCCESS"
            }
    }

Here is the query I am trying to work with. The issue is in the FailPercent/$divide line:

db.col.aggregate([    
    { $match: { "data.buildResult" : { $ne : null } } },
    { $group: {          
        _id: {              
            month: { $month: "$time" },             
            day: { $dayOfMonth: "$time" },             
            year: { $year: "$time" },                       
        },         
        Aborted: { $sum: { $cond :  [{ $eq : ["$data.buildResult", "ABORTED"]}, 1, 0]} },
        Failure: { $sum: { $cond :  [{ $eq : ["$data.buildResult", "FAILURE"]}, 1, 0]} },
        Unstable: { $sum: { $cond : [{ $eq : ["$data.buildResult", "UNSTABLE"]}, 1, 0]} },
        Success: { $sum: { $cond :  [{ $eq : ["$data.buildResult", "SUCCESS"]}, 1, 0]} },
        Total: { $sum: 1 },
        FailPercent: { $divide: [ "Failure", "Total" ] }
    } },     
    { $sort: { "_id.year": 1, "_id.month": 1, "_id.day": 1 } } 
])
like image 935
JamesE Avatar asked Apr 02 '14 17:04

JamesE


People also ask

Is aggregate faster than find in MongoDB?

Because of this, if you have a simple aggregation pipeline or one which does not cut down the data volume much it can often be quicker to use a find() and perform the aggregation client side.

What does MongoDB aggregation return?

In MongoDB, aggregation operations process the data records/documents and return computed results. It collects values from various documents and groups them together and then performs different types of operations on that grouped data like sum, average, minimum, maximum, etc to return a computed result.

What are the differences between using aggregate () and find () MongoDB?

With aggregate + $match, you get a big monolithic BSON containing all matching documents. With find, you get a cursor to all matching documents. Then you can get each document one by one.


1 Answers

You almost got it. Only change that would be required is that you'll have to compute the FailPercent in an additional project phase, because the total is only available after the completion of the group phase. Try this:

db.foo.aggregate([    
    { $match: { "data.buildResult" : { $ne : null } } },
    { $group: {          
        _id: {              
            month: { $month: "$time" },             
            day: { $dayOfMonth: "$time" },             
            year: { $year: "$time" },                       
        },         
        Aborted: { $sum: { $cond :  [{ $eq : ["$data.buildResult", "ABORTED"]}, 1, 0]} },
        Failure: { $sum: { $cond :  [{ $eq : ["$data.buildResult", "FAILURE"]}, 1, 0]} },
        Unstable: { $sum: { $cond : [{ $eq : ["$data.buildResult", "UNSTABLE"]}, 1, 0]} },
        Success: { $sum: { $cond :  [{ $eq : ["$data.buildResult", "SUCCESS"]}, 1, 0]} },
        Total: { $sum: 1 }
    } }, 
    {$project:{Aborted:1, Failure:1, Unstable:1, Success:1, Total:1, FailPercent: { $divide: [ "$Failure", "$Total" ]}}},
    { $sort: { "_id.year": 1, "_id.month": 1, "_id.day": 1 } } 
])
like image 74
Anand Jayabalan Avatar answered Sep 19 '22 01:09

Anand Jayabalan