【问题标题】:Can't reduce a deeply nested array on MongoDB无法减少 MongoDB 上的深度嵌套数组
【发布时间】:2020-07-19 21:57:43
【问题描述】:

我有一个 Mongo 数据库,其中包含类似这样的文档:

{
   date:"2019-06-12T00:09:03.000Z",
   actions:{
      actionDate:"2019-06-12T00:15:25.000Z",
      data:{
         users:[
            [{gender:"Male",age:24},
             {gender:"Female",age:25}
            ],
            [{gender:"Male",age:34},
             {gender:"Male",age:26}
            ],
            [{gender:"Female",age:19},
             {gender:"Male",age:21}
            ]
         ]
      }
   }
}

我想在单个文档中总结出现在数组users 中的用户,比如

{
   "date":"2019-06-12T00:09:03.000Z",
   "actionDate":"2019-06-12T00:15:25.000Z",
   "summary":{
      "countFemale":2,
      "meanFemaleAge":22,
      "countMale":4,
      "meanMaleAge":26.25
   }
}

需要考虑的一些因素:可能没有一种性别的情况,而且users 数组可能仅限于其中的一两个数组。 我已经尝试使用我现在知道的关于 Mongo 查询语言的稀缺知识来解决它,但这对我来说似乎无法解决。认为这可能对检查 MongoDB: Reduce array of objects into a single object by computing the average of each field 有用,但无法跟上这个想法。

有什么想法吗?

【问题讨论】:

  • 您提到的问题是关于减少具有动态字段名称的数组。 $reduce 应该适合您的情况。

标签: arrays mongodb multidimensional-array aggregate


【解决方案1】:

试试下面的查询:

db.collection.aggregate([
    /** Merge all arrays inside 'users' & push to 'summary' field */
  {
    $project: {
      date: 1,
      actionDate: "$actions.actionDate",
      summary: {
        $reduce: {
          input: "$actions.data.users",
          initialValue: [],
          in: { $concatArrays: ["$$value", "$$this"] },
        },
      },
    },
  },
  {
    $unwind: "$summary",
  },
  /** Group on 'date' to push data related to same date */
  {
    $group: {
      _id: "$date",
      actionDate: {$first: "$actionDate",},
      countFemale: {$sum: {$cond: [{$eq: ["$summary.gender", "Female"]},1,0]}},
      countMale: {$sum: {$cond: [{$eq: ["$summary.gender", "Male"]},1,0]}},
      meanFemaleAge: {$sum: {$cond: [{$eq: ["$summary.gender", "Female"]},"$summary.age",0]}},
      meanMaleAge: {$sum: {$cond: [{$eq: ["$summary.gender", "Male"]},"$summary.age",0]}}
    }
  },
  /** Re-create 'meanFemaleAge' & 'meanMaleAge' fields to add mean */
  {
    $addFields: {
      meanFemaleAge: {$cond: [{$ne: ["$meanFemaleAge", 0]},{$divide: ["$meanFemaleAge","$countFemale"]},0]},
      meanMaleAge: {$cond: [{$ne: ["$meanMaleAge", 0]},{$divide: ["$meanMaleAge","$countMale"]},0]},      
    }
  }
]);

测试: MongoDB-Playground

注意:无论你怎么做,我都建议你不要对拥有庞大数据集的整个集合执行这种操作。

【讨论】:

  • @arlugones :通常查询性能取决于多个因素,例如您的数据库服务器容量、索引、文档数量。因此,如果您想确保您的查询运行良好,请尝试添加 $explain 阶段 (docs.mongodb.com/manual/reference/operator/meta/explain/…) 并检查它是否使用正确的索引/哪个阶段执行得最少并结合其他细节,您可以假设出了什么问题&改进现有查询..
【解决方案2】:

我们需要执行$reduce 运算符。

在第一阶段,我们创建单独的数组(Male|Female)并根据用户的性别推送用户。

在第二阶段,我们转换/计算结果。

试试这个:

db.collection.aggregate([
  {
    $addFields: {
      "users": {
        $reduce: {
          input: "$actions.data.users",
          initialValue: {
            "Male": [],
            "Female": []
          },
          in: {
            Male: {
              $concatArrays: [
                "$$value.Male",
                {
                  $filter: {
                    input: "$$this",
                    cond: {
                      $eq: [
                        "$$this.gender",
                        "Male"
                      ]
                    }
                  }
                }
              ]
            },
            Female: {
              $concatArrays: [
                "$$value.Female",
                {
                  $filter: {
                    input: "$$this",
                    cond: {
                      $eq: [
                        "$$this.gender",
                        "Female"
                      ]
                    }
                  }
                }
              ]
            }
          }
        }
      }
    }
  },
  {
    $project: {
      _id: 0,
      date: 1,
      actionDate: "$actions.actionDate",
      summary: {
        "countFemale": {
          $size: "$users.Female"
        },
        "meanFemaleAge": {
          $avg: "$users.Female.age"
        },
        "countMale": {
          $size: "$users.Male"
        },
        "meanMaleAge": {
          $avg: "$users.Male.age"
        }
      }
    }
  }
])

MongoPlayground

【讨论】:

  • 太棒了!非常感谢,先生!
  • 说实话...我更喜欢以前的方法。
猜你喜欢
  • 2019-05-05
  • 1970-01-01
  • 2020-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-26
  • 2020-09-24
相关资源
最近更新 更多