太长,读不下go 了
现代版本应在最初的$group
之后使用$reduce
和$setUnion
,如图所示:
db.collection.aggregate([
{ "$group": {
"_id": { "Host": "$Host", "ArtId": "$ArtId" },
"count": { "$sum": 1 },
"tags": { "$addToSet": "$tags" }
}},
{ "$addFields": {
"tags": {
"$reduce": {
"input": "$tags",
"initialValue": [],
"in": { "$setUnion": [ "$$value", "$$this" ] }
}
}
}}
])
您找到$addToSet
运算符是对的,但在处理数组中的内容时,通常需要先处理$unwind
运算符.这会"反规范化"数组条目,并基本上生成父文档的"副本",每个数组条目在字段中都是单数值.这就是你在不使用它的情况下避免看到的行为所需要的.
虽然"计数"带来了一个有趣的问题,但在最初的$group
次操作后,通过使用"双重放松"很容易解决:
db.collection.aggregate([
// Group on the compound key and get the occurrences first
{ "$group": {
"_id": { "Host": "$Host", "ArtId": "$ArtId" },
"tcount": { "$sum": 1 },
"ttags": { "$push": "$tags" }
}},
// Unwind twice because "ttags" is now an array of arrays
{ "$unwind": "$ttags" },
{ "$unwind": "$ttags" },
// Now use $addToSet to get the distinct values
{ "$group": {
"_id": "$_id",
"tcount": { "$first": "$tcount" },
"tags": { "$addToSet": "$ttags" }
}},
// Optionally $project to get the fields out of the _id key
{ "$project": {
"_id": 0,
"Host": "$_id.Host",
"ArtId": "$_id.ArtId",
"count": "$tcount",
"tags": "$ttags"
}}
])
最后一位是$project
,因为我在聚合管道的其他阶段 for each 字段使用了"临时"名称.这是因为在$project
中有一个优化,即在将任何"新"字段添加到文档之前,按照字段已经出现的顺序"复制"现有阶段中的字段.
否则,输出将如下所示:
{ "count":2 , "tags":[ "tag1", "tag2", "tag3" ], "Host": "abc.com", "ArtId": "123" }
字段的顺序与您可能认为的不同.这真的很琐碎,但对一些人来说很重要,所以值得解释为什么,以及如何处理.
因此,$unwind
的作用是保持项目分开,而不是在数组中,而首先执行$group
可以获得"分组"键出现的"计数".
后来使用的$first
运算符"保留"了"count"值,因为它只是对"tags"数组中的每个值进行了"复制".反正都是一样的,所以没关系.随便挑一个.