2

I have a document with entries like this

{ 
    "_id": ObjectId("5644c495d0807a1750043237"),
    "siteid": "123456"
    "amount": 1.32
}

Some documents have other amounts eg."cashbackAmount"

I want a sum and a count for each amount fields. Not every document contains all the amount fields.

I hjave tried the following

{
    $group: {   
        "_id": "$siteid",
        item2: { "$sum": "$amount" },
        item3: { "$sum": "$totalAmount" },
        item4: { "$sum": "$cashbackAmount" },
        item5: { "$sum": "$unitPrice" },
    }
}  

It gives me the sum, but I cannot work out how to get the number times each amount field is present.

{ "$sum": 1 } does not work because that gives me all the documents that have any one of the totals fields.

1
  • What do you mean by get the number times each amount field is present? Can you give some expected output example? Commented Jun 24, 2016 at 10:45

2 Answers 2

1

I guess you probably want something like that

db.getCollection('amounts').aggregate([
    {
        $project: {
            siteid: 1,
            amount: 1,
            totalAmount: 1,
            unitPrice: 1,
            cashbackAmount: 1,
            amountPresent: {
                $cond: {
                    if: "$amount",
                    then: 1,
                    else: 0
                }
            },
            totalAmountPresent: {
                $cond: {
                    if: "$totalAmount",
                    then: 1,
                    else: 0
                }
            },
            cashbackAmountPresent: {
                $cond: {
                    if: "$cashbackAmount",
                    then: 1,
                    else: 0
                }
            },
            unitPricePresent: {
                $cond: {
                    if: "$unitPrice",
                    then: 1,
                    else: 0
                }
            }
        }
    },
    {
        $group: {   
            "_id": "$siteid",
            amountSum:   { "$sum": "$amount" },
            amountCount: { "$sum": "$amountPresent" },
            totalAmountSum:   { "$sum": "$totalAmount" },
            totalAmountCount: { "$sum": "$totalAmountPresent" },
            cashbackAmountSum:   { "$sum": "$cashbackAmount" },
            cashbackAmountCount: { "$sum": "$cashbackAmountPresent" },
            unitPriceSum:   { "$sum": "$unitPrice" },
            unitPriceCount: { "$sum": "$unitPricePresent" }
        }
    }
])
Sign up to request clarification or add additional context in comments.

2 Comments

Then please up vote the answers which helped you (not only mine) :)
Will do when I get a reputation
1

If you know the amount fields in advance then you could do this in a single aggregation operation where you create the pipeline dynamically.

Check out the following demonstration:

var amountFields = ["amount", "totalAmount", "cashbackAmount", "unitPrice"],
    groupOperator = { "$group": { "_id": "$siteid" } };

amountFields.forEach(function (field){ 
    groupOperator["$group"][field+"Total"] = { "$sum": "$"+field }; 
    groupOperator["$group"][field+"Count"] = { 
       "$sum": {            
           "$cond": [ { "$gt": [ "$"+field, null ] }, 1, 0 ]
        }
    };
});

db.test.aggregate([groupOperator]);

Populate Test Documents

db.test.insert([
    { 
        "siteid": "123456",
        "amount": 1.32
    },
    { 
        "siteid": "123456",
        "cashbackAmount": 8.32
    },
    { 
        "siteid": "123456",
        "cashbackAmount": 9.74
    },
    { 
        "siteid": "123456",
        "unitPrice": 0.19
    },
    { 
        "siteid": "123456",
        "amount": 27.8,
        "totalAmount": 15.22,
        "unitPrice": 5.10,
        "cashbackAmount": 43.62    
    },
    { 
        "siteid": "123456",
        "unitPrice": 5.07
    },
    { 
        "siteid": "123456",
        "amount": 12.98,
        "totalAmount": 32.82
    },
    { 
        "siteid": "123456",
        "amount": 6.65,
        "unitPrice": 5.10
    }
])

Sample Aggregation Output

{
    "_id" : "123456",
    "amountTotal" : 48.75,
    "amountCount" : 4,
    "totalAmountTotal" : 48.04,
    "totalAmountCount" : 2,
    "cashbackAmountTotal" : 61.68,
    "cashbackAmountCount" : 3,
    "unitPriceTotal" : 15.46,
    "unitPriceCount" : 4
}

1 Comment

Thanks "$cond": [ { "$gt": [ "$"+field, null ] }, 1, 0 ] was the way to go

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.