MongoDB按键值对聚合/分组

MongoDB按键值对聚合/分组,mongodb,mapreduce,mongodb-query,aggregation-framework,Mongodb,Mapreduce,Mongodb Query,Aggregation Framework,我的数据如下所示: { "_id" : "9aa072e4-b706-47e6-9607-1a39e904a05a", "customerId" : "2164289-4", "channelStatuses" : { "FOO" : { "status" : "done" },

我的数据如下所示:

    { 
            "_id" : "9aa072e4-b706-47e6-9607-1a39e904a05a", 
            "customerId" : "2164289-4", 
            "channelStatuses" : {
                    "FOO" : {
                    "status" : "done"
                    }, 
                    "BAR" : {
                    "status" : "error"
                    }
            }, 
            "channel" : "BAR", 
    }
    { 
            "_id" : {
                    "customerId" : "$customerId", 
                    "channel" : "$channel", 
                    "status" : "$channelStatuses[$channel].status"
            }, 
                    "count" : {
                    "$sum" : 1
            }
    }
我的聚合/组如下所示:

    { 
            "_id" : "9aa072e4-b706-47e6-9607-1a39e904a05a", 
            "customerId" : "2164289-4", 
            "channelStatuses" : {
                    "FOO" : {
                    "status" : "done"
                    }, 
                    "BAR" : {
                    "status" : "error"
                    }
            }, 
            "channel" : "BAR", 
    }
    { 
            "_id" : {
                    "customerId" : "$customerId", 
                    "channel" : "$channel", 
                    "status" : "$channelStatuses[$channel].status"
            }, 
                    "count" : {
                    "$sum" : 1
            }
    }
因此,基本上,根据示例数据,小组应该给我一个分组:

   {"customerId": "2164289-4", "channel": "BAR", "status": "error"}

但我不能在聚合/组中使用[]-索引。我应该怎么做?

使用
.aggregate()
无法获得当前结构所需的结果。您“可以”将结构更改为使用数组而不是命名键,并且操作实际上非常简单

因此,对于以下文档:

{
“_id”:“9aa072e4-b706-47e6-9607-1a39e904a05a”,
“客户ID”:“2164289-4”,
“信道状态”:[
{
“频道”:“FOO”,
“状态”:“完成”
}, 
{
“频道”:“酒吧”,
“状态”:“错误”
}
], 
“频道”:“酒吧”,
}
然后,您可以在现代版本中使用和执行以下操作:

{“$group”:{
“_id”:{
“customerId”:“$customerId”,
“频道”:“$channel”,
“地位”:{
“$arrayElemAt”:[
{“$map”:{
“输入”:{“$filter”:{
“输入”:“$chanelstatus”,
“as”:“el”,
“cond”:{“$eq”:[“$$el.channel”,“$channel”]}
}},
“as”:“el”,
“在”:“$$el.status”
}},
0
]
}
},
“计数”:{“$sum”:1}
}}
较早版本的MongoDB将需要
$unwind
来访问匹配的数组元素

在MongoDB 2.6中,您仍然可以在展开之前“预过滤”阵列:

[
{“$project”:{
“客户ID”:1,
"频道":一,,
“地位”:{
“$setDifference”:[
{“$map”:{
“输入”:“$channelstatus”,
“as”:“el”,
“在”:{
“$cond”:[
{“$eq”:[“$$el.channel”,“$channel”]},
“$$el.status”,
假的
]
}
}},
[错误]
]
}
}},
{“$unwind”:“$status”},
{“$组”:{
“_id”:{
“customerId”:“$customerId”,
“频道”:“$channel”,
“状态”:“$status”
},
“计数”:{“$sum”:1}
}}
]
而在此之前的任何内容都可以在
$unwind
之后进行“过滤”:

[
{“$unwind”:“$channelStatuses”},
{“$project”:{
“客户ID”:1,
"频道":一,,
“状态”:“$channelStatuses.status”,
“相同”:{“$eq”:[“$channelStatuses.status”,“$channel”]}
}},
{“$match”:{“same”:true},
{“$组”:{
“\u id”:“$\u id”,
“customerId”:{“$first”:“$customerId”},
“频道”:{“$first”:“$channel”},
“状态”:{“$first”:“$status”}
}},
{“$组”:{
“_id”:{
“customerId”:“$customerId”,
“频道”:“$channel”,
“状态”:“$status”
},
“计数”:{“$sum”:1}
}}
]
在低于MongoDB 2.6的版本中,您还需要
$project
两个字段之间的相等性测试结果,然后在单独的阶段对结果进行
$match
。您还可能会注意到“两个”
$group
阶段,因为第一个阶段会通过
$first
累加器在过滤器之后删除任何可能重复的
“通道”
值。下面的
$group
与上一个清单中的内容完全相同

但是,如果您无法更改结构,并且需要在无法提供每个名称的情况下进行键的“灵活”匹配,则必须使用mapReduce:

db.collection.mapReduce(
函数(){
散发({
“customerId”:this.customerId,
“频道”:这个频道,
“状态”:this.channelstatus[此.channel].status
},1);
},
功能(键、值){
返回Array.sum(值);
},
{“out”:{“inline”:1}
)

当然,您可以使用这种符号

谢谢。我决定将channelstatus更改为您描述的数组。有没有一种更简单的方法来获取正确的通道,比如首先展开通道状态,然后进行匹配(“channelStatuses.channel”:“$channels”),然后只按状态分组?@Keksike整个操作点,如
$filter
$map
,都是为了“避免”使用
$unwind
$unwind
操作符从本质上说是一个“巨大的”性能猪。除非您打算从数组中包含的值跨文档聚合,否则应避免
$unwind
。你也可以做同样的事情,但成本要高得多。最好的方法就是上面显示的方法。你对如何在没有arrayElemAt的情况下进行分组有什么想法,因为我使用的mongodb版本不支持它吗?@Keksike你有什么想法?至少MongoDB 2.6.x?@Keksike添加了所有内容。