【问题标题】:Aggregate Count Array Members Matching Condition聚合计数数组成员匹配条件
【发布时间】:2022-04-13 22:00:57
【问题描述】:

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

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

我想计算具有特定条件的数组元素的数量(例如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"}}}
])

但我收到此错误:

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

我为这个问题寻找了几个答案,但我没有找到任何解决我的问题的方法。我的意思是,'Array' 是一个数组!

【问题讨论】:

    标签: mongodb aggregation-framework


    【解决方案1】:

    错误是因为它不再是 $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 值。

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

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

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

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

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

      { "$group": {
        "_id": "$_id",
        "Array": { "$push": "$Array" },
        "total": { "$sum": 1 }
      }}
    

    当然,除了在另一个管道阶段使用$size,我们仍然可以像使用$sum 一样简单地“计数”

    【讨论】:

    • 你不能更好地解释它!现在我明白了,总的来说,我是 MongoDB 和 NoSQL 的新手,我一次只学习所有方法和运算符:我不知道 $unwind (neither $group) 机制。真的真的真的非常感谢!
    • 这个答案太棒了!谢谢尼尔!
    猜你喜欢
    • 2018-09-17
    • 1970-01-01
    • 1970-01-01
    • 2016-03-30
    • 1970-01-01
    • 2016-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多