Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get percentages with MongoDB aggregate $group

I'd like to get percentages from a group pipeline in a MongoDB aggregate.

My data:

{
    _id : 1,
    name : 'hello',
    type : 'big'
},
{
    _id : 2,
    name : 'bonjour',
    type : 'big'
},
{
    _id : 3,
    name : 'hi',
    type : 'short'
},
{
    _id : 4,
    name : 'salut',
    type : 'short'
},
{
    _id : 5,
    name : 'ola',
    type : 'short'
}

My request group by type, and count:

[{
    $group : {
        _id : {
            type : '$type'
        },
        "count" : {
            "$sum" : 1
        }
    }
}]

Result:

[
    {
        _id {
            type : 'big',
        },
        count : 2
    },
    {
        _id {
            type : 'short',
        },
        count : 3
    }
]

But I'd like to have count AND percentage, like that:

[
    {
        _id {
            type : 'big',
        },
        count: 2,
        percentage: 40%
    },
    {
        _id {
            type : 'short',
        },
        count: 3,
        percentage: 60%
    }
]

But I've no idea how to do that. I've tried $divide and other things, but without success. Could you please help me?

like image 805
user3582562 Avatar asked Jul 02 '15 15:07

user3582562


People also ask

What does $group do in MongoDB?

The $group stage separates documents into groups according to a "group key". The output is one document for each unique group key. A group key is often a field, or group of fields. The group key can also be the result of an expression.

Can we use count with aggregate function in MongoDB?

MongoDB aggregate $count element in array In this topic, you will learn to count the elements in the array from the MongoDB collection. For this, MongoDB provides the $size aggregation to count and returns the total number of items in an array.

Is aggregation fast in MongoDB?

Unless you're stuck with legacy code, it makes sense to migrate to the aggregation framework. Comparing MySQL 8.0 and MongoDB 4.0. 3, it was seen that MongoDB is typically faster on more complex queries. It's faster from disk when there are no indexes, whereas MySQL is faster from RAM.


2 Answers

Well I think percentage should be string if the value contains %

First get you will need to count the number of document.

var nums = db.collection.count();

db.collection.aggregate(
    [
        { "$group": { "_id": {"type":  "$type"}, "count": { "$sum": 1 }}},    
        { "$project": { 
            "count": 1, 
            "percentage": { 
                "$concat": [ { "$substr": [ { "$multiply": [ { "$divide": [ "$count", {"$literal": nums }] }, 100 ] }, 0,2 ] }, "", "%" ]}
            }
        }
    ]
)

Result

{ "_id" : { "type" : "short" }, "count" : 3, "percentage" : "60%" }
{ "_id" : { "type" : "big" }, "count" : 2, "percentage" : "40%" }
like image 146
styvane Avatar answered Sep 21 '22 08:09

styvane


First find total number of documents in collections using count method and used that count variable to calculate percentage in aggregation like this :

var totalDocument = db.collectionName.count() //count total doc.

used totalDocument in aggregation as below :

db.collectionName.aggregate({"$group":{"_id":{"type":"$type"},"count":{"$sum":1}}},
                            {"$project":{"count":1,"percentage":{"$multiply":[{"$divide":[100,totalDocument]},"$count"]}}})

EDIT

If you need to this in single aggregation query then unwind used in aggregation but using unwind it creates Cartesian problem check below aggregation query :

db.collectionName.aggregate({"$group":{"_id":null,"count":{"$sum":1},"data":{"$push":"$$ROOT"}}},
                            {"$unwind":"$data"},
                             {"$group":{"_id":{"type":"$data.type"},"count":{"$sum":1},
                                       "total":{"$first":"$count"}}},
                             {"$project":{"count":1,"percentage":{"$multiply":[{"$divide":[100,"$total"]},"$count"]}}}
                            ).pretty()

I recconmed first find out toatal count and used that count in aggregation as per first query.

like image 39
Yogesh Avatar answered Sep 20 '22 08:09

Yogesh