how to avoid $push-ing nulls in mongo aggregation framework

橙三吉。 提交于 2019-12-04 11:21:06

问题


$push is aggregating nulls if the field is not present. I would like to avoid this.

Is there a way to make a sub expression for $push operator in such way that null values will be skipped and not pushed into the resulting array ?


回答1:


Bit late to the party, but..

I wanted to do the same thing, and found that I could accomplish it with an expression like this:

  // Pushes events only if they have the value 'A'
  "events": {
    "$push": {
      "$cond": [
        {
          "$eq": [
            "$event",
            "A"
          ]
        },
        "A",
        "$noval"
      ]
    }
  }

The thinking here is that when you do

{ "$push": "$event" } 

then it seems to only push non-null values.

So I made up a column that doesn't exist, $noval, to be returned as the false condition of my $cond.

It seems to work. I'm not sure if it is non-standard and therefore susceptible to breaking one day but..




回答2:


It's really not completely clear what your specific case is without an example. There is the $ifNull operator which can "replace" a null value or missing field with "something else", but to truly "skip" is not possible.

That said, you can always "filter" the results depending on your actual use case.

If your resulting data is actually a "Set" and you have a MongoDB version that is 2.6 or greater then you can use $setDifference with some help from $addToSet to reduce the number of null values that are kept initially:

db.collection.aggregate([
    { "$group": {
        "_id": "$key",
        "list": { "$addToSet": "$field" }
    }},
    { "$project": {
        "list": { "$setDifference": [ "$list", [null] ] }
    }}
])

So there would only be one null and then the $setDifference operation will "filter" that out in the comparison.

In earlier versions or when the values are not in fact "unique" and not a "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 don't want to be "destructive" of arrays that would end up "empty" because they contained "nothing but" null, then you keep a count use $ifNull and match on 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 a final $project replacing any array that simply consisted of null values only with an empty array object.



来源:https://stackoverflow.com/questions/29064874/how-to-avoid-push-ing-nulls-in-mongo-aggregation-framework

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!