实际上你可以用$addFields https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/ or $project https://docs.mongodb.com/manual/reference/operator/aggregation/project/
db.collection.aggregate([
{ "$addFields": {
"groups": {
"$map": {
"input": "$groups",
"in": {
"$mergeObjects": [
"$$this",
{ "teams": {
"$reduce": {
"input": "$$this.teams",
"initialValue": [ ],
"in": {
"$cond": {
"if": {
"$ne": [ { "$indexOfArray": ["$$value.state", "$$this.state"] }, -1 ]
},
"then": {
"$concatArrays": [
{ "$filter": {
"input": "$$value",
"as": "v",
"cond": { "$ne": [ "$$v.state", "$$this.state" ] }
}},
[{
"state": "$$this.state",
"count": { "$sum": [
{ "$arrayElemAt": [
"$$value.count",
{ "$indexOfArray": ["$$value.state", "$$this.state" ] }
]},
1
]}
}]
]
},
"else": {
"$concatArrays": [
"$$value",
[{ "state": "$$this.state", "count": 1 }]
]
}
}
}
}
}}
]
}
}
}
}}
])
这非常复杂并且基本上使用$reduce https://docs.mongodb.com/manual/reference/operator/aggregation/reduce/“内联”作为替代$group https://docs.mongodb.com/manual/reference/operator/aggregation/group/管道运营商。
The $reduce https://docs.mongodb.com/manual/reference/operator/aggregation/reduce/是工作的主要部分,因为它使用键上的“分组”总计来迭代每个数组项“减少”到另一个数组。它通过寻找值来做到这一点state
在当前减少的结果内通过$indexOfArray https://docs.mongodb.com/manual/reference/operator/aggregation/indeOfArray/。当找不到某些东西时(-1
返回)它通过以下方式附加到当前结果$concatArrays https://docs.mongodb.com/manual/reference/operator/aggregation/concatArrays/与一个新的state
and count
of 1
。这是else
case.
当某事is found ( the then
case ),我们通过以下方式从结果数组中删除匹配的元素$filter https://docs.mongodb.com/manual/reference/operator/aggregation/filter/ and 连接来自匹配索引的新元素$indexOfArray https://docs.mongodb.com/manual/reference/operator/aggregation/indexOfArray/并使用提取值$arrayElemAt https://docs.mongodb.com/manual/reference/operator/aggregation/arrayElemAt/。这给出了当前count
使用添加的匹配元素的$sum https://docs.mongodb.com/manual/reference/operator/aggregation/sum/为了增加计数1
.
当然,传统上你可以这样做$unwind https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/ and $group https://docs.mongodb.com/manual/reference/operator/aggregation/group/声明:
db.collection.aggregate([
{ "$unwind": "$groups" },
{ "$unwind": "$groups.teams" },
{ "$group": {
"_id": {
"_id": "$_id",
"gId": "$groups._id",
"wlActive": "$groups.wlActive",
"state": "$groups.teams.state"
},
"count": { "$sum": 1 }
}},
{ "$sort": { "_id": -1, "count": -1 } },
{ "$group": {
"_id": {
"_id": "$_id._id",
"gId": "$_id.gId",
"wlActive": "$_id.wlActive",
},
"teams": { "$push": { "state": "$_id.state", "count": "$count" } }
}},
{ "$group": {
"_id": "$_id._id",
"groups": {
"$push": {
"_id": "$_id.gId",
"wlActive": "$_id.wlActive",
"teams": "$teams"
}
}
}}
])
Here $unwind https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/习惯于“压扁”将数组内容放入单独的文档中。你这样做到teams
水平和$group https://docs.mongodb.com/manual/reference/operator/aggregation/group/ on the 复合键其中标识独特性下降到state
level.
由于所有文档详细信息都是初始文档的一部分$group https://docs.mongodb.com/manual/reference/operator/aggregation/group/键,您删除的级别“独特性” so teams
变成一个数组使用$push https://docs.mongodb.com/manual/reference/operator/aggregation/push/。为了回到原来的文档形式,另一个$group https://docs.mongodb.com/manual/reference/operator/aggregation/group/是在原来的基础上完成的_id
文件的价值和$push https://docs.mongodb.com/manual/reference/operator/aggregation/push/重建了groups
array.
那个形式大概是"easier"理解,但它确实需要更长的时间来运行并且需要更多的资源。第一种形式是optimal因为你实际上不需要$group https://docs.mongodb.com/manual/reference/operator/aggregation/group/在现有文档中,您通常应该避免$unwind https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/除非绝对必要。即分组state
across 所有文件是必要的,但在单个文档中则不是。
无论哪种方式基本上都会返回相同的结果:
{
"_id" : "event1",
"groups" : [
{
"_id" : "group1",
"wlActive" : true,
"teams" : [
{
"state" : 2,
"count" : 2
},
{
"state" : 1,
"count" : 3
},
{
"state" : 0,
"count" : 2
}
]
},
{
"_id" : "group2",
"wlActive" : false,
"teams" : [
{
"state" : 2,
"count" : 2
},
{
"state" : 1,
"count" : 3
},
{
"state" : 0,
"count" : 2
}
]
}
]
}
{
"_id" : "event2",
"groups" : [
{
"_id" : "group3",
"wlActive" : true,
"teams" : [
{
"state" : 2,
"count" : 2
},
{
"state" : 1,
"count" : 3
},
{
"state" : 0,
"count" : 2
}
]
},
{
"_id" : "group4",
"wlActive" : false,
"teams" : [
{
"state" : 2,
"count" : 2
},
{
"state" : 1,
"count" : 3
},
{
"state" : 0,
"count" : 2
}
]
}
]
}
不管它的价值如何,因为这并不是真正的“聚合”跨文档的任何内容,您都可以简单地返回所有数据并在客户端代码中“聚合”数组项。
作为 mongo shell 示例:
db.collection.find().map(doc => Object.assign({}, doc, {
_id: doc._id,
groups: doc.groups.map(g => Object.assign({}, g, {
_id: g._id,
wlActive: g.wlActive,
teams: ((input) => {
var obj = input.reduce((o, e) =>
(o.hasOwnProperty(e.state)) ?
Object.assign({} , o, { [e.state]: o[e.state]+1 })
: Object.assign({}, o, { [e.state]: 1 }), {});
return Object.keys(obj)
.map(k => ({ state: parseInt(k), count: obj[k] }))
.sort((a,b) => b.state - a.state);
})(g.teams)
}))
}))