Mongodb 对符合给定条件的子文档进行计数
我的Mongo收藏包含以下格式的文档:Mongodb 对符合给定条件的子文档进行计数,mongodb,mongodb-query,aggregation-framework,Mongodb,Mongodb Query,Aggregation Framework,我的Mongo收藏包含以下格式的文档: { ... "notifications": [ { "enabled": true, "groups": [ "NG1", "NG3" ] }, { "enabled": false, "groups": [] } ] } 其中,enabled为布尔值,groups为字符
{
...
"notifications": [
{
"enabled": true,
"groups": [ "NG1", "NG3" ]
},
{
"enabled": false,
"groups": []
}
]
}
其中,enabled
为布尔值,groups
为字符串列表。
我需要执行一个查询,以确定通知
中有多少条目启用了并在组中包含给定字符串(例如NG3
)
以前,如果没有enabled
属性(后来作为需求引入),我的查询就很简单
db.collection.find({ "notifications.groups": "NG3" })
我尝试了一些与$和操作符的组合,但运气不佳,所以欢迎您提出任何建议。提前谢谢 建议运行聚合框架管道,在管道步骤中使用和数组运算符的组合
运算符将返回一个数组,其中数组子集中的元素与指定条件匹配。将只返回过滤数组中的元素数
因此,总而言之,您需要运行此管道,以便您可以确定通知中有多少条目启用了enabled=true
,并在组中包含给定字符串(例如“NG3”):
以上内容适用于MongoDB版本3.2.X
及更高版本。但是,对于涵盖MongoDB版本2.6.X到3.0.X(含3.0.X)的解决方案,其他数组操作符,如,将是很好的替代操作符
过滤阵列
考虑使用操作符,使用与上面$cond中相同的逻辑作为映射表达式来过滤数组。然后,运算符返回一个集合,其中的元素出现在第一个集合中,但不出现在第二个集合中;i、 e.相对于第一组,对第二组进行相对补充。在这种情况下,它将通过启用
和组
属性返回最终通知数组,该数组包含与父文档无关的元素
var pipeline = [
{ "$match": { "notifications.enabled": true, "notifications.groups": "NG3" } },
{
"$project": {
"numberOfEntries": {
"$size": {
"$setDifference": [
{
"$map": {
"input": "$notifications",
"as": "items",
"in": {
"$cond": [
{ "$and": [
{ "$eq": [ "$$items.enabled", true ] },
{ "$setIsSubset": [ [ "NG3" ], "$$items.groups" ] }
] },
"$$items",
false
]
}
}
},
[false]
]
}
}
}
}
];
db.collection.aggregate(pipeline);
对于没有上述运算符的较老的MangoDB版本,考虑使用<强> < /强>、<强> < /强>和<强> < /强>运算符以实现相同的目标:
var pipeline = [
{ "$match": { "notifications.enabled": true, "notifications.groups": "NG3" } },
{ "$unwind": "$notifications" },
{ "$match": { "notifications.enabled": true, "notifications.groups": "NG3" } },
{
"$group": {
"_id": "$_id",
"numberOfEntries": { "$sum": 1 }
}
}
];
db.collection.aggregate(pipeline);
清点文件
使用$elemMatch
:
db.collection.find({
"notifications": {
"$elemMatch": {
"enabled": true,
"groups": "NG3"
}
}
})
运算符将包含数组字段的文档与至少一个匹配所有指定查询条件的元素相匹配
以下查询的问题:
db.collection.find({ "notifications.groups": "NG3", "notifications.enabled": true })
字段引用不限于单个通知。因此,只要其中一个通知已启用,此查询就会匹配
与true
一样,其中一个在其组中包含NG3
,但您需要将这两个属性应用于同一通知。要限制匹配过程,应使用$elemMatch
运算符
对通知进行单独计数
如果您想根据该标准计算通知的数量,那么应该使用聚合管道,正如@chridam在其回答中深入解释的那样。
我建议按以下四个阶段计算通知数:
把所有的东西都扔掉,只保留通知
展开通知
数组,生成一个通知
每个数组元素的文档
使用{enabled:true,groups:“NG3”}
统计剩余的通知
然后定义对应于每个阶段的这四个变量:
var keepNotification = {$project: {
"notifications.enabled": 1,
"notifications.groups": 1,
_id: 0
}
}
var expandNotifications = {$unwind: "$notifications"}
var filterByEnabledAndGroups = {$match: {
"notifications.enabled": true,
"notifications.groups": "NG3"
}
}
var count = {$group: {_id: "notifications", count: {$sum: 1}}}
并在聚合管道中使用它们:
db.collection.aggregate([
keepNotification,
expandNotifications,
filterByEnabledAndGroups,
count
])
最终的结果是:
{ "_id" : "notifications", "count" : 5 }
{ "_id" : ObjectId("57487d006a2fa1f11efc3208"), "count" : 1 }
实际上,实现这一点的最佳方法是在MongoDB 3.2或更高版本中,因为您可以使用或使用本文所示的运算符,或者利用stage中可用的累加器运算符,该运算符可用于返回数组中所有元素的总和
让我们看看如何在$project
阶段中使用$sum
实现这一点
在$project
阶段,需要使用运算符返回一个数组,其中数组中的项为“数值”1
或0
。为此,我们需要一个使用运算符的逻辑条件,该运算符在条件为true
时返回1
,在条件为false时返回0
db.collection.aggregate([
{ "$match": {
"notifications.enabled": true,
"notifications.groups": "NG3"
}},
{ "$project": {
"count": {
"$sum": {
"$map": {
"input": "$notifications",
"as": "n",
"in": {
"$cond": [
{ "$and": [
"$$n.enabled",
{ "$setIsSubset": [
[ "NG3" ],
"$$n.groups"
]}
]},
1,
0
]
}
}
}
}
}}
])
在MongoDB 3.2之前,您需要采取另一种效率较低的方法,因为它要求我们在$project
阶段之后使用数组,因为$sum
运算符从3.0向后在$project
阶段不可用
从那里,您只需输入文档,然后使用$sum
操作符返回计数
db.collection.aggregate([
{ "$match": {
"notifications.enabled": true,
"notifications.groups": "NG3"
}},
{ "$project": {
"count": {
"$map": {
"input": "$notifications",
"as": "n",
"in": {
"$cond": [
{ "$and": [
"$$n.enabled",
{ "$setIsSubset": [
[ "NG3" ],
"$$n.groups"
]}
]},
1,
0
]
}
}
}
}},
{ "$unwind": "$count" },
{ "$group": { "_id": "$_id", "count": { "$sum": "$count" } } }
])
这两个查询产生如下结果:
{ "_id" : "notifications", "count" : 5 }
{ "_id" : ObjectId("57487d006a2fa1f11efc3208"), "count" : 1 }
是的,你是对的,但是他以前的方法db.collection.find({“notifications.groups”:“NG3”})
让我相信他想清点所有的文档。你是对的,抱歉搞混了!我的方法是首先关注查询,因此我对整个集合执行一个简单的查询。你的答案和chridam的答案显示了同一问题的两面,再次感谢!作为一个建议,这比我预期的要多得多!!我从你的回答中学到了很多,所以非常感谢克里丹和阿里·德哈尼@ilPittiz不用担心,总是乐于帮助:)谢谢你的另一个非常详细的回答!我永远不会停止学习Mongo 3.2有多强大:)