这里的区别在于.count() 操作在返回字段存在的“文档”计数时实际上是“正确的”。因此,一般考虑可分解为:
如果你只想排除带有数组字段的文档
然后最有效的方法是排除那些“街道”是“地址”属性的文档作为“数组”,然后只需使用点符号属性查找0索引不存在排除在外:
db.coll.find({
"address.street": { "$exists": true },
"address.0": { "$exists": false }
}).count()
作为本机编码的运算符测试,$exists 在这两种情况下都能有效地完成正确的工作。
如果您打算计算字段出现次数
如果您实际询问的是“字段计数”,其中一些“文档”包含数组条目,其中该“字段”可能出现多次。
为此,您需要您提到的聚合框架或 mapReduce。 MapReduce 使用基于 JavaScript 的处理,因此将比.count() 操作慢得多。聚合框架还需要计算并且“将”比.count() 慢,但不会像 mapReduce 那么多。
在 MongoDB 3.2 中,您可以通过 $sum 处理值数组以及作为分组累加器的扩展能力获得一些帮助。这里的另一个助手是$isArray,当数据实际上是“数组”时,它允许通过$map 使用不同的处理方法:
db.coll.aggregate([
{ "$group": {
"_id": null,
"count": {
"$sum": {
"$sum": {
"$cond": {
"if": { "$isArray": "$address" },
"then": {
"$map": {
"input": "$address",
"as": "el",
"in": {
"$cond": {
"if": { "$ifNull": [ "$$el.street", false ] },
"then": 1,
"else": 0
}
}
}
},
"else": {
"$cond": {
"if": { "$ifNull": [ "$address.street", false ] },
"then": 1,
"else": 0
}
}
}
}
}
}
}}
])
早期版本依赖于更多的条件处理,以便以不同方式处理数组和非数组数据,并且通常需要$unwind 来处理数组条目。
在 MongoDB 2.6 中通过 $map 转置数组:
db.coll.aggregate([
{ "$project": {
"address": {
"$cond": {
"if": { "$ifNull": [ "$address.0", false ] },
"then": "$address",
"else": {
"$map": {
"input": ["A"],
"as": "el",
"in": "$address"
}
}
}
}
}},
{ "$unwind": "$address" },
{ "$group": {
"_id": null,
"count": {
"$sum": {
"$cond": {
"if": { "$ifNull": [ "$address.street", false ] },
"then": 1,
"else": 0
}
}
}
}}
])
或者使用 MongoDB 2.2 或 2.4 提供条件选择:
db.coll.aggregate([
{ "$group": {
"_id": "$_id",
"address": {
"$first": {
"$cond": [
{ "$ifNull": [ "$address.0", false ] },
"$address",
{ "$const": [null] }
]
}
},
"other": {
"$push": {
"$cond": [
{ "$ifNull": [ "$address.0", false ] },
null,
"$address"
]
}
},
"has": {
"$first": {
"$cond": [
{ "$ifNull": [ "$address.0", false ] },
1,
0
]
}
}
}},
{ "$unwind": "$address" },
{ "$unwind": "$other" },
{ "$group": {
"_id": null,
"count": {
"$sum": {
"$cond": [
{ "$eq": [ "$has", 1 ] },
{ "$cond": [
{ "$ifNull": [ "$address.street", false ] },
1,
0
]},
{ "$cond": [
{ "$ifNull": [ "$other.street", false ] },
1,
0
]}
]
}
}
}}
])
所以后一种形式“应该”比 mapReduce 表现好一点,但可能不会好很多。
在所有情况下,逻辑都归结为使用 $ifNull 作为聚合框架的 $exists 的“逻辑”形式。与$cond 配对,当属性实际存在时获得“真实”结果,不存在时返回false 值。这决定了是1还是0分别通过$sum返回到整体累加中。
理想情况下,您拥有可以在单个 $group 管道阶段执行此操作的现代版本,否则您需要更长的路径。