我的Mongo集合包含以下格式的文档:
{
...
"notifications": [
{
"enabled": true,
"groups": [ "NG1", "NG3" ]
},
{
"enabled": false,
"groups": []
}
]
}
其中enabled
是布尔值,groups
是字符串列表。
我需要执行查询以确定notifications
中有多少条目enabled = true
并且groups
中包含给定字符串(例如NG3
)。
以前,如果没有后来作为要求引入的enabled
属性,我的查询只是
db.collection.find({ "notifications.groups": "NG3" })
我尝试了一些$and
运营商的组合,但没有运气,所以欢迎任何建议。提前谢谢!
答案 0 :(得分:7)
建议运行聚合框架管道,该管道在 {{中使用 $filter
和 $size
数组运算符的组合3}} 管道步骤。
$project
运算符将返回一个数组,该数组包含与指定条件匹配的数组子集中的元素。 $filter
只会返回该已过滤数组中的元素数。
所以,完全放上这个,你就可以运行这个管道,这样你就可以确定通知中有多少条目enabled = true
并且在组中包含给定的字符串(例如" NG3"):
var pipeline = [
{ "$match": { "notifications.enabled": true, "notifications.groups": "NG3" } },
{
"$project": {
"numberOfEntries": {
"$size": {
"$filter": {
"input": "$notifications",
"as": "items",
"cond": {
"$and": [
{ "$eq": [ "$$items.enabled", true ] },
{ "$setIsSubset": [ [ "NG3" ], "$$items.groups" ] }
]
}
}
}
}
}
}
];
db.collection.aggregate(pipeline);
以上适用于MongoDB版本3.2.X
及更新版本。但是,对于涵盖MongoDB版本2.6.X up to and including 3.0.X
的解决方案,其他数组运算符(如 $size
, $map
)将是良好的替代运算符
过滤数组。
考虑使用 $setDifference
运算符,使用与$ cond中相同的逻辑过滤数组作为映射表达式。 $map
运算符然后返回一个集合,其中的元素出现在第一个集合中但不出现在第二个集合中;即执行第二组相对于第一组的相对补充。在这种情况下,它将通过enabled
和groups
属性返回包含与父文档无关的元素的最终通知数组。
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);
对于没有上述运营商的旧版MongoDB,请考虑使用 $setDifference
, $match
和 {{3 }} 运算符实现相同的目标:
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);
答案 1 :(得分:4)
使用$elemMatch
:
db.collection.find({
"notifications": {
"$elemMatch": {
"enabled": true,
"groups": "NG3"
}
}
})
$elemMatch
运算符匹配包含数组字段的文档,其中至少有一个元素符合所有指定的查询条件。
以下查询的问题:
db.collection.find({ "notifications.groups": "NG3", "notifications.enabled": true })
字段引用是否仅限于单个通知。因此,只要其中一个通知为enabled
,此查询就会匹配
作为true
,其中一个NG3
中包含groups
,但您希望将这两个属性应用于同一通知。为了限制匹配过程,您应该使用$elemMatch
运算符。
如果要计算具有该条件的通知数,则应使用聚合管道,正如@chridam在其答案中深入解释的那样。 我建议使用以下四个阶段来计算通知:
notifications
数组,生成一个通知
每个数组元素的文档使用{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 }
答案 2 :(得分:2)
实际上,最好的方法是使用MongoDB 3.2或更高版本,因为您可以使用此$filter
中所示的answer运算符,或利用$sum
累加器运算符在$project
阶段,可用于返回数组中所有元素的总和。
让我们看看如何使用$sum
阶段中的$project
来完成此操作。
在$project
阶段,您需要使用$map
运算符返回一个数组,其中数组中的项目为“数字”1
或0
。要做到这一点,我们需要使用$cond
运算符的逻辑条件,当条件为1
时返回true
,而0
时返回false
。
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
阶段后$unwind
我们的数组,因为$sum
运算符不可用于从{3.0}向后的$project
阶段。
从那里,您只需$group
您的文档,并使用$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" : ObjectId("57487d006a2fa1f11efc3208"), "count" : 1 }