问题
$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