现代 MongoDB 版本当然只需应用 $filter 和 $addFields 将过滤后的可能为空的数组结果写入文档:
Collection.aggregate([
{ "$addFields": {
"images": {
"$filter": {
"input": "$filter",
"as": "i",
"cond": { "$eq": [ "$$i.deleted", null ] }
}
}
}}
])
您之前给出的答案不是一个非常现代或有效的答案,并且有其他更好的方法来处理从数组中过滤内容而不是 $unwind、$match 和 $group。
与 MongoDB 2.6 一样,您可以使用唯一的数组标识符来做到这一点:
Collection.aggregate([
{ "$project": {
"name": 1,
"images": { "$cond": [
{ "$eq": [{ "$size": { "$ifNull": [ "$images",[]] }}, 0] },
{ "$ifNull": [ "$images", [] ] },
{ "$setDifference": [
{ "$map": {
"input": "$images",
"as": "i",
"in": { "$cond": [
{ "$eq": [ "$$i.deleted", null ] },
"$$i",
false
]}
}},
[false]
]}
]}
}}
],
$map 运算符通过返回由给定表达式评估的每个检查元素来转换文档中的数组。在这里,您可以使用$cond 来测试字段值并决定是按原样返回该字段还是返回false。
$setDifference 操作将生成的转换数组与另一个奇异元素数组[false]“比较”。这样做的效果是从数组中删除所有不匹配的项,只留下那个,甚至是一个没有匹配项的空数组。
只要您的文档在文档的多个级别不包含相同的引用属性,以下带有$redact 的内容是安全的。看起来有些滑稽的情况是因为 "deleted": null 属性实际上被投影(出于评估目的)在它不存在的级别。这是必需的,因为$redact 以“递归”方式使用,下降文档树以决定删除什么或“编辑”:
Collection.aggregate([
{ "$redact": {
"$cond": [
{ "$eq": [ { "$ifNull": [ "$deleted", null ] }, null ] },
"$$DESCEND",
"$$PRUNE"
]
}}
]
这确实是为您的特定目的实现的最简单的逻辑。请记住,如果您在文档中添加另一个“已删除”字段以某种方式表示其他含义,您以后可能无法使用它。
如果您真的被 MongoDB 2.6 之前的版本卡住并且无法访问这些操作,那么您当然需要执行 $unwind、$match 和 $groupprocess。因此,在开始时需要注意空数组或缺失数组,以及匹配没有匹配条目的数组时。
一种方法:
Collection.aggregate([
// Cater for missing or empty arrays
{ "$project": {
"name": 1,
"images": { "$cond": [
{ "$eq": [{ "$ifNull": [ "$images", [] ] }, [] ] },
{ "$const": [{ "deleted": false }] },
"$images"
]}
}},
// Safe to unwind
{ "$unwind": "$images" },
// Just count the matched array entries first
{ "$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"images": { "$push": "$images" },
"count": { "$sum": { "$cond": [
{ "$eq": [ "$images.deleted", null ] },
1,
0
]}}
}},
// Unwind again
{ "$unwind": "$images" },
// Match either non deleted or unmatched array
{ "$match": {
"$or": [
{ "images.deleted": null},
{ "count": 0 }
]
}},
// Group back with the things that were matched
{ "$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"images": { "$push": "$images" },
"count": { "$first": "$count" }
}},
// Replace the un-matched arrays with empty ones
{ "$project": {
"name": 1,
"images": { "$cond": [
{ "$eq": [ "$count", 0 ] },
[],
"$images"
]}
}}
],
所以那里有更多的提升,但一般原则是只获得匹配元素的“计数”,当您过滤时,您还可以使用0“计数”保留数组项,但只需替换那些稍后整个数组。
您还可以在此处考虑,如果您首先在文档中维护“activeCount”字段,那么您将无需计算该字段并放弃一些阶段。
当然,这里的另一个论点是,您可以通过在单独的数组中实际维护“活动”和“已删除”项目来省去这个麻烦。每次更新都这样做消除了通过聚合进行过滤的任何需要。我想这一切都取决于你的真实目的。但是思想的食物。
当然,这一切都是根据您的原始数据进行测试的,并进行了一些修改以适应测试用例:
{
"_id" : ObjectId("54ec9cac83a214491d2110f4"),
"name" : "my_images",
"images" : [
{
"ext" : "jpeg",
"type" : "image/jpeg",
"_id" : ObjectId("54f2311026b0cb289ed04188"),
"deleted" : null,
"date_added" : ISODate("2015-02-28T21:20:16.961Z")
},
{
"ext" : "jpeg",
"type" : "image/jpeg",
"_id" : ObjectId("54f2314a26b0cb289ed04189"),
"deleted" : ISODate("2015-02-24T15:38:14.826Z"),
"date_added" : ISODate("2015-02-28T21:21:14.910Z")
},
{
"ext" : "jpeg",
"type" : "image/jpeg",
"_id" : ObjectId("54f2315526b0cb289ed0418a"),
"deleted" : null,
"date_added" : ISODate("2015-02-28T21:21:25.042Z")
},
{
"ext" : "jpeg",
"type" : "image/jpeg",
"_id" : ObjectId("54f2315d26b0cb289ed0418b"),
"deleted" : null,
"date_added" : ISODate("2015-02-28T21:21:33.081Z")
}
]
},
{
"_id" : ObjectId("54fa6ca87c105bc872cc1886"),
"name" : "another",
"images" : [ ]
},
{
"_id" : ObjectId("54fa6cef7c105bc872cc1887"),
"name" : "final",
"images" : [
{
"ext" : "jpeg",
"type" : "image/jpeg",
"_id" : ObjectId("54f2314a26b0cb289ed04189"),
"deleted" : ISODate("2015-02-24T15:38:14.826Z"),
"date_added" : ISODate("2015-02-28T21:21:14.910Z")
}
]
}
所有版本产生的安全结果:
{
"_id" : ObjectId("54ec9cac83a214491d2110f4"),
"name" : "my_images",
"images" : [
{
"ext" : "jpeg",
"type" : "image/jpeg",
"_id" : ObjectId("54f2311026b0cb289ed04188"),
"deleted" : null,
"date_added" : ISODate("2015-02-28T21:20:16.961Z")
},
{
"ext" : "jpeg",
"type" : "image/jpeg",
"_id" : ObjectId("54f2315526b0cb289ed0418a"),
"deleted" : null,
"date_added" : ISODate("2015-02-28T21:21:25.042Z")
},
{
"ext" : "jpeg",
"type" : "image/jpeg",
"_id" : ObjectId("54f2315d26b0cb289ed0418b"),
"deleted" : null,
"date_added" : ISODate("2015-02-28T21:21:33.081Z")
}
]
},
{
"_id" : ObjectId("54fa6ca87c105bc872cc1886"),
"name" : "another",
"images" : [ ]
},
{
"_id" : ObjectId("54fa6cef7c105bc872cc1887"),
"name" : "final",
"images" : [ ]
}