In fact, it is not entirely clear what your specific case is without an example. There is a $ifNull that can “replace” a null value or an absent “something else” field, but it is impossible to truly “skip” it.
However, you can always “filter” the results depending on your actual use.
If your resulting data is actually “Set” and you have a version of MongoDB that is 2.6 or higher, you can use $setDifference with $addToSet to reduce the number of null values that are stored initially:
db.collection.aggregate([ { "$group": { "_id": "$key", "list": { "$addToSet": "$field" } }}, { "$project": { "list": { "$setDifference": [ "$list", [null] ] } }} ])
That way, there will be only one null , and then the $setDifference operation will "filter" this by comparison.
In earlier versions, or when the values are not "unique" and not "set", then you "filter" by processing with $unwind and $match :
db.collection.aggregate([ { "$group": { "_id": "$key", "list": { "$push": "$field" } }}, { "$unwind": "$list" }, { "$match": { "list": { "$ne": null } }}, { "$group": { "_id": "$_id", "list": { "$push": "$list" } }} ])
If you do not want to be "destructive" of arrays that end in "empty" because they contain "nothing but" null , then you keep using count $ifNull and match the conditions:
db.collection.aggregate([ { "$group": { "_id": "$key", "list": { "$push": "$field" }, "count": { "$sum": { "$cond": [ { "$eq": { "$ifNull": [ "$field", null ] }, null }, 0, 1 ] } } }}, { "$unwind": "$list" }, { "$match": { "$or": [ { "list": { "$ne": null } }, { "count": 0 } ] }}, { "$group": { "_id": "$_id", "list": { "$push": "$list" } }}, { "$project": { "list": { "$cond": [ { "$eq": [ "$count", 0 ] }, { "$const": [] }, "$list" ] } }} ])
With the final $project replacing any array that simply consisted of null values with only an empty array object.