聚合计数数组成员匹配条件

And*_*ani 5 mongodb aggregation-framework

如标题所述,我在使用MongoDB计数数组中的元素时遇到了麻烦。我有一个只有一个文档的数据库,如下所示:

 {_id: ObjectId("abcdefghilmnopq"),
    "Array": [
      {field1: "val1",
       field2: "val2",
       field3: "val3",
       ...
       },
       {field1: "Value1",
        field2: "Value2",
        field3: "Value3",
       ...
       },
        ...
     ]
 }
Run Code Online (Sandbox Code Playgroud)

我想计算具有一定条件的数组元素的数量(例如field1: "a",并计算具有的所有元素field1 = a)。我正在尝试使用以下代码:

db.collection.aggregate([
{ $unwind : {path: "$Array", 
             includeArrayIndex: "arrayIndex"}},
{ $match : { "Array.field1" : "a"}},
{ $project : { _id : 0, 
               Array : 1, 
               arrayIndex: 1, 
               total: {$size: "$Array"}}}
])
Run Code Online (Sandbox Code Playgroud)

但我收到此错误:

命令失败,错误17124:“ $ size的参数必须是数组,但类型为:对象”在服务器上

我为该问题寻找了几个答案,但没有找到解决该问题的方法。我的意思是,“数组”是一个数组!

提前致谢

Nei*_*unn 10

错误是因为它在你之后不再是一个数组$unwind,因此不再是$size.

您似乎试图“合并”几个现有答案,但不了解它们在做什么。你真正想要的是$filter$size

db.collection.aggregate([
  { "$project": {
    "total": {
      "$size": {
        "$filter": {
          "input": "$Array",
          "cond": { "$eq": [ "$$this.field1", "a" ] }
        }
      }
    }
  }}
])
Run Code Online (Sandbox Code Playgroud)

或“重新发明轮子”使用$reduce

db.collection.aggregate([
  { "$project": {
    "total": {
      "$reduce": {
        "input": "$Array",
        "initialValue": 0,
        "in": {
          "$sum": [
            "$$value", 
            { "$cond": [{ "$eq": [ "$$this.field1", "a" ] }, 1, 0] }
        }
      }
    }
  }}
])
Run Code Online (Sandbox Code Playgroud)

或者对于您尝试使用的内容$unwind,您实际上$group是为了“计算”有多少匹配项:

db.collection.aggregate([
  { "$unwind": "$Array" },
  { "$match": { "Array.field1": "a" } },
  { "$group": {
    "_id": "$_id",
    "total": { "$sum": 1 }
  }}
])
Run Code Online (Sandbox Code Playgroud)

前两种形式是现代 MongoDB 环境的“最佳”形式。带有$unwindand的最终形式$group是一个“遗留”构造,自 MongoDB 2.6 以来,这种类型的操作实际上并不是必需的,尽管有一些略有不同的运算符。

在前两个中,我们基本上比较field1每个数组元素的值,而它仍然是一个数组。双方$filter$reduce旨在与工作场所中的现有阵列现代化的运营商。使用聚合$eq运算符对每个运算符进行相同的比较,该运算符根据给定的参数是否“相等”返回一个布尔值。在这种情况下对每个数组成员的期望值"a"

在 的情况下$filter,除了"cond"从数组中删除不满足 中提供的条件的任何元素之外,数组实际上保持完整。由于我们仍然有一个“数组”作为输出,因此我们可以使用$size运算符来测量处理过滤条件后剩余的数组元素的数量。

$reduce通过阵列元件和建筑材料的表达式在每个元件和存储的“累加器”值,这与我们初始化另一方面作品"initialValue"。在这种情况下,同样的$eq测试适用于$cond操作员。这是一个“三元”或if/then/else条件运算符,它允许返回布尔值的测试表达式返回then值 whentrueelse值 when false

在该表达式中,我们分别返回1or0并提供将返回值和当前“累加器”"$$value"$sum运算符相加的总体结果,以将它们相加。

$unwind数组上使用的最终形式。这实际上是解构数组成员,为每个数组成员创建一个“新文档”,并在原始文档中与它相关的父字段。这有效地“复制”了每个数组成员的主文档。

一旦您$unwind将文档的结构更改为“更扁平”的形式。这就是为什么您可以执行后续$match管道阶段以删除不匹配的文档的原因。

这给我们带来了$group用于“将所有与公共密钥相关的信息重新组合在一起”的应用。在这种情况下,它是_id原始文档的字段,它当然被复制到$unwind. 当我们回到这个“公共密钥”作为单个文档时,我们可以“计算”使用$sum累加器从数组中提取的剩余“文档” 。

如果我们想要剩余的“数组”返回,那么您可以$push仅使用剩余成员重建数组:

  { "$group": {
    "_id": "$_id",
    "Array": { "$push": "$Array" },
    "total": { "$sum": 1 }
  }}
Run Code Online (Sandbox Code Playgroud)

但是当然,而不是$size在另一个管道阶段使用,我们仍然可以简单地“计数”,就像我们已经对$sum