我让这个问题搁置了一段时间,因为我在最后一个问题 https://stackoverflow.com/q/24059348/2313887做到这一点的基本过程是什么。我还评论说$redact http://docs.mongodb.org/manual/reference/operator/aggregation/redact/不是进行此类操作的动物,除了此处的答案之外,还有两个原因需要解释。可以说您知道过滤后的值,而不仅仅是过滤它们。
正如之前所给出的,您仍然需要一些用法$unwind http://docs.mongodb.org/manual/reference/operator/aggregation/unwind/但与会导致管道中要处理的文档数量激增的传统用法不同,它只是被使用after数组内容已被过滤。这里唯一真正的区别是,我们要注意“过滤数组”实际上将包含超过one元素,所以你可以适当地处理它:
db.users.aggregate([
{ "$match": {
"commentUpvotes.upvoteDate": { "$gte": 0 }
}},
{ "$project": {
"posts": 1,
"comments": 1,
"commentUpVotes": {
"$setDifference": [
{
"$map": {
"input": "$commentUpvotes",
"as": "el",
"in": {
"$cond": [
{ "$gte": [ "$$el.upvoteDate", 0 ] },
"$$el",
false
]
}
}
},
[false]
]
}
}},
{ "$project": {
"posts": 1,
"comments": 1,
"kcommentUpVotes": "$commentUpVotes",
"commentUpVotes": 1
}},
{ "$unwind": "$commentUpVotes" },
{ "$project": {
"posts": 1,
"comments": {
"$setDifference": [
{
"$map": {
"input": "$comments",
"as": "el",
"in": {
"$cond": [
{
"$eq": [
{ "$substr": [ "$$el.id", 0, 4 ] },
"$commentUpVotes.commentId"
]
},
"$$el",
false
]
}
}
},
[false]
]
},
"commentUpVotes": "$kcommentUpVotes"
}},
{ "$unwind": "$comments" },
{ "$group": {
"_id": "$_id",
"posts": { "$first": "$posts" },
"comments": { "$addToSet": "$comments" },
"kcomments": { "$addToSet": "$comments" },
"commentUpVotes": { "$first": "$commentUpVotes" }
}},
{ "$unwind": "$comments" },
{ "$project": {
"posts": {
"$setDifference": [
{
"$map": {
"input": "$posts",
"as": "el",
"in": {
"$cond": [
{
"$eq": [
"$$el.id",
"$comments.postId"
]
},
"$$el",
false
]
}
}
},
[false]
]
},
"comments": "$kcomments",
"commentUpVotes": 1
}},
{ "$unwind": "$posts" },
{ "$group": {
"_id": "$_id",
"posts": { "$addToSet": "$posts" },
"comments": { "$first": "$comments" },
"commentUpVotes": { "$first": "$commentUpVotes" }
}}
])
因此,这里有一点是要准确理解每个阶段(或重复过程)正在做什么以及为什么要这样做。$unwind http://docs.mongodb.org/manual/reference/operator/aggregation/unwind/这里的操作很重要。
如果你选择第一个$project http://docs.mongodb.org/manual/reference/operator/aggregation/project/考虑到这里,返回的结果始终是一个数组。这就是“过滤”的方式$map http://docs.mongodb.org/manual/reference/operator/aggregation/map/有效,并且非常有意义,因为您期望有多个(在本例中为所有)匹配的可能性。
重要的部分发生在您尝试将这些值与文档中的另一个数组进行匹配之前,就像您查看一个数组的结构时一样。$map http://docs.mongodb.org/manual/reference/operator/aggregation/map/重点是将元素与奇异值进行比较。这就是为什么你需要$unwind http://docs.mongodb.org/manual/reference/operator/aggregation/unwind/为了获得那些“奇异”值进行比较。
因此,除了保留“过滤”数组的副本以使事情变得更干净之外,让我们跳到与“评论”数组匹配后的部分。由于“commentUpvotes”数组已“展开”,现在每个文档都有一个副本,其中包含该数组自己的过滤版本。请注意,每个结果数组只能包含一个元素。
由于这些确实是数组,为了在文档之间组合它们,您需要展开这些“单元素”数组,然后将它们重新组合在一起。请记住,虽然“commentUpvotes”有“三个”匹配,但只有“两个”评论可以匹配,但是“三个”匹配中的“两个”共享相同的评论id
。这是使用分组回的地方$addToSet http://docs.mongodb.org/manual/reference/operator/update/addToSet/变得很重要,因为您不想重复该匹配的帖子。
一旦所有匹配的元素都在数组中,就可以再次进行$unwind http://docs.mongodb.org/manual/reference/operator/aggregation/unwind/并重复。
因此,总体前提与前面的示例和问题相同。事实上,这里的方法可以被认为是先前列表的“2.0 版本”,因为它将满足所有情况下的单数和“多”匹配。
这里要提到的一个“警告”是基本原则,即这些项目确实相关,并且任何数组中都不存在“孤立”细节。这样做的明显原因是,任何测试从一个数组到另一个不匹配的数组的匹配都会导致空数组。可能还有其他匹配项,但如果其中一个测试结果为空,那么您将必须处理生成的空数组。
最后一个注释的概念很简单,只需测试一下$size http://docs.mongodb.org/manual/reference/operator/aggregation/size/结果的,否则输入单个值false
并在稍后阶段将其过滤掉。但出于练习的目的,我认为您的“关系”确实完好无损,并将任何额外的处理留给您自己实施。
最终结果当然是您获得所需的结果,而无需诉诸相同级别的“井喷”,只需将未过滤的数组相互展开并尝试与这些记录进行相等匹配即可。