错误是因为它不再是 $unwind 之后的数组,因此不再是 $size 的有效参数。
您似乎试图“合并”几个现有的答案,而不了解他们在做什么。你真正想要的是$filter和$size
db.collection.aggregate([
{ "$project": {
"total": {
"$size": {
"$filter": {
"input": "$Array",
"cond": { "$eq": [ "$$this.field1", "a" ] }
}
}
}
}}
])
或者使用$reduce“重新发明轮子”:
db.collection.aggregate([
{ "$project": {
"total": {
"$reduce": {
"input": "$Array",
"initialValue": 0,
"in": {
"$sum": [
"$$value",
{ "$cond": [{ "$eq": [ "$$this.field1", "a" ] }, 1, 0] }
}
}
}
}}
])
或者对于您尝试对$unwind 执行的操作,您实际上再次$group 以“计算”有多少匹配项:
db.collection.aggregate([
{ "$unwind": "$Array" },
{ "$match": { "Array.field1": "a" } },
{ "$group": {
"_id": "$_id",
"total": { "$sum": 1 }
}}
])
前两种形式是现代 MongoDB 环境的“最佳”形式。带有$unwind 和$group 的最终形式是一个“遗留”结构,自 MongoDB 2.6 以来,这种类型的操作实际上并不需要,尽管有一些稍微不同的运算符。
在前两个中,我们基本上比较每个数组元素的field1 值,而它仍然是一个数组。 $filter 和 $reduce 都是现代运算符,旨在与现有阵列一起工作。使用聚合$eq 运算符对每个参数进行相同的比较,该运算符根据给定的参数是否“相等”返回一个布尔值。在这种情况下对每个数组成员的期望值为"a"。
在$filter 的情况下,除了从数组中删除不满足"cond" 中提供的条件的任何元素外,数组实际上保持不变。由于我们仍然有一个“数组”作为输出,因此我们可以使用 $size 运算符来测量处理过滤条件后剩下的数组元素的数量。
另一方面,$reduce 处理数组元素,并为每个元素提供一个表达式和一个存储的“累加器”值,我们用"initialValue" 对其进行初始化。在这种情况下,$cond 运算符中应用了相同的 $eq 测试。这是一个“三元”或if/then/else 条件运算符,它允许一个经过测试的表达式返回一个布尔值,在true 时返回then 值,在false 时返回else 值。
在该表达式中,我们分别返回 1 或 0,并提供将返回值和当前“累加器”"$$value" 与 $sum 运算符相加的整体结果,以将它们相加。
在数组上使用$unwind 的最终形式。这实际上做的是解构数组成员,为每个数组成员创建一个“新文档”,它是原始文档中的相关父字段。这有效地“复制”了每个数组成员的主文档。
一旦您$unwind,文档的结构就会更改为“更扁平”的形式。这就是为什么您可以执行后续的$match 管道阶段以删除不匹配的文档。
这将我们带到$group,它用于“重新组合”与公共密钥相关的所有信息。在这种情况下,它是原始文档的_id 字段,当然它被复制到$unwind 生成的每个文档中。当我们将这个“公用密钥”作为单个文档返回时,我们可以使用$sum 累加器“计数”从数组中提取的剩余“文档”。
如果我们想要返回剩余的“数组”,那么您可以$push 并仅使用剩余的成员重建数组:
{ "$group": {
"_id": "$_id",
"Array": { "$push": "$Array" },
"total": { "$sum": 1 }
}}
当然,除了在另一个管道阶段使用$size,我们仍然可以像使用$sum 一样简单地“计数”