Question

I have an array and each list item in that array is an object the has a value key. What I would like to do is add all of the values to get a total. I want to do this on the back end rather than the front end. I have tried the aggregate method, but haven't had any luck as it returns an empty array. Here is my array:

"budgets" : [
    {
        "name" : "check",
        "value" : "1000",
        "expense" : "false",
        "uniqueId" : 0.9880268634296954
    },
    {
        "name" : "car",
        "value" : "500",
        "expense" : "true",
        "uniqueId" : 0.1904486275743693
    },
    {
        "name" : "car2",
        "value" : "500",
        "expense" : "false",
        "uniqueId" : 0.23043920518830419
    },
    {
        "name" : "car23",
        "value" : "500",
        "expense" : "false",
        "uniqueId" : 0.014449386158958077
    },
    {
        "name" : "car235",
        "value" : "500",
        "expense" : "false",
        "uniqueId" : 0.831609656335786
    }
],

What I want to do is get the total of the values that have "expense" : "true" and the get a separate total of "expense" : "false" how would I do this? Like I said I have tried the aggregate method, but I must be doing it wrong.

Was it helpful?

Solution

If you are new to using the aggregation framework commands you might not yet be aware of the $cond operator. You can use this in order to get your totals separated:

db.collection.aggregate([

    // Unwind the array to de-normalize the items
    { "$unwind": "$budgets" },

    // Group the totals with conditions
    { "$group": {
        "_id": "$_id",
        "expense": { "$sum": { 
            "$cond": [
                "$budgets.expense",
                "$budgets.value",
                0
            ]
        }},
        "nonExpense": { "$sum": { 
            "$cond": [
                "$budgets.expense",
                0,
                "$budgets.value"
            ]
        }}

    }}

])

So what that will do is evaluate the true/false condition of expense as the first argument, and when the condition is actually true then the second argument of the $cond will be chosen and passed on to the $sum. If the condition evaluates to false then the second argument is chosen.

But looking at your data you appear to have a problem:

    {
        "name" : "check",
        "value" : "1000",
        "expense" : "false",
        "uniqueId" : 0.9880268634296954
    },

Noting here that all of the fields, and most notably the expense and value items, are all strings. So this is a problem as while we could get around evaluating the true/false values by doing string comparisons rather than direct boolean, you simply cannot co-erce a string as a number that can be passed to $sum.

So firstly you need to fix your data, unless it is not actually in the form as you have represented. But when it meets a correct form, then you can do your aggregation.

OTHER TIPS

First of all covert you datatype of value from string to integer(summable) type and then use

db.collectioName.aggregate(
    {$unwind:"$budgets"},

    {
    $group:{_id: "$budgets.expense",
    total: { $sum: "$budgets.value"}}
    })

and then you should get result like this

{
    "result" : [
        {
            "_id" : "true",
            "total" : 500
        },
        {
            "_id" : "false",
            "total" : 2000
        }
    ],
    "ok" : 1
}

Try something like this

db.collection.aggregate([ { "$unwind": "$budgets" },
                  { $match: { expense: "true" } },
                  { $group: { _id: "$_id",
                            name: { $addToSet: "$name" },
                            uniqueId: { $addToSet: "$uniqueId" }, 
                            total: { $sum: "$$budgets.value" } } }
               ])
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top