我有一个要求,我需要在两个记录上进行聚合,这两个记录都有一个具有不同值的数组字段。我需要的是当我对这些记录进行聚合时,结果应该有一个数组,其中包含来自不同数组的唯一值。这是一个例子:
第一条记录
{ Host:"abc.com" ArtId:"123", tags:[ "tag1", "tag2" ] }
第二条记录
{ Host:"abc.com" ArtId:"123", tags:[ "tag2", "tag3" ] }
在主机和artid上聚合后,我需要这样的结果:
{ Host: "abc.com", ArtId: "123", count :"2", tags:[ "tag1", "tag2", "tag3" ]}
我在群组声明中尝试了$addToset
,但它给了我这样的标签:[["tag1","tag2"],["tag2","tag3"]]
你能帮我解决一下如何在聚合中实现这个目标
答案 0 :(得分:24)
现代版本应在初始$reduce
之后使用$setUnion
$group
,如下所示:
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
也在那里,因为我在聚合管道的其他阶段为每个字段使用了“临时”名称。这是因为$project
中存在一个优化,它按照“在”任何“新”字段添加到文档之前已经出现的顺序“复制”现有阶段中的字段。
否则输出如下:
{ "count":2 , "tags":[ "tag1", "tag2", "tag3" ], "Host": "abc.com", "ArtId": "123" }
字段的顺序与您想象的顺序不同。真的很琐碎,但对某些人来说很重要,所以值得解释原因,以及如何处理。
所以$unwind
完成了将项目分开而不是数组的工作,并且首先执行$group
可以让您获得“分组”键出现次数的“计数”。 / p>
稍后使用的$first
运算符“保留”该“计数”值,因为它对“标记”数组中的每个值都“重复”。无论如何它都是相同的价值,所以无所谓。选择一个。