Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exclude 0 values from mongodb $avg but keeping other fields

I run some aggregation queries on MongoDB 3.2. I would like to group documents by a field with an average on another numeric field. I need the average to ignore the 0 values. The problem is I can't entirely filter the document, cause there is another field I need for a count.

Let's illustrate :

This is the structure of my documents:

{"stringToGroupByOn":"foo", "valueToAvg":42, "valueToSum":21}
{"stringToGroupByOn":"foo", "valueToAvg":0, "valueToSum":13}

I can't just filter like this:

db.foobar.aggregate([
    { 
        $match : { valueToAvg : { $gt : 0 } } 
    },
    { 
        $group : {
            _id : '$stringToGroupByOn',
            avg : { $avg :  '$valueToAvg' }, 
            count : { $sum : '$valueToSum' }
        }
    }
])

Because I lose the value 13 for the count.

Do you think there is a way to do it in only one query ?

like image 576
eli0tt Avatar asked May 31 '16 15:05

eli0tt


People also ask

How do I exclude fields in MongoDB?

To exclude the _id field from the output documents of the $project stage, specify the exclusion of the _id field by setting it to 0 in the projection document.

How do I ignore NULL values in MongoDB?

Solution 1: In case preservation of all null values[] or null fields in the array itself is not necessary. Filter out the not null elements using $filter , the ( all null elements) array would be empty, filter that out from documents using $match then $sort on values .

Can we use $and in aggregate MongoDB?

You can use $and with aggregation but you don't have to write it, and is implicit using different filters, in fact you can pipe those filters in case one of them needs a different solution.

What are the differences between using aggregate () and find () in 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 can use $cond in projection to set null instead of 0, as null is not considered when using average.

db.avg.aggregate([
   {$project:{
       _id:1,
       valueToSum:1,
       stringToGroupByOn:1,
       valueToAvg:{$cond: 
             { if: { $eq: [ "$valueToAvg", 0 ] }, 
                   then: null, 
                   else: "$valueToAvg" }}           
       }},
    { 
        $group : {
            _id : '$stringToGroupByOn',
            avg : { $avg :  '$valueToAvg' }, 
            count : { $sum : '$valueToSum' }
        }
    }

output:

{
    "_id" : "foo",
    "avg" : 42.0,
    "count" : 34.0
}
like image 109
profesor79 Avatar answered Nov 15 '22 03:11

profesor79