【问题标题】:How to filter nested array after $group stage in aggregation pipeline in MongoDB?如何在 MongoDB 的聚合管道中的 $group 阶段之后过滤嵌套数组?
【发布时间】:2021-09-02 15:23:58
【问题描述】:

我刚才问了一个关于在内部数组中对 $lookup 结果进行分组的问题,链接到question

我有一个看起来像这样带有查找结果的数据集

_id: ObjectID('abc'),
sections: [
   {
   sectionId: "sec0",
   sectionName: "ABC",
   contents: [
      {
      contentId: 0,
      tasks: [
         {
          _id: task01
          //properties of task01
          duration: '2 days',
         },
         {
          _id: task02
          //properties of task02
          duration: '3 days',
         },
    ],
    contentDescription: "Content is etc",
    }
  ]
}
{
   sectionId: "sec1",
   sectionName: "EFG",
   contents: [
      {
      contentId: 0,
      tasks: [
         {
          _id: task03
          //properties of task03
          duration: '4 days',
         },
         {
          _id: task04
          //properties of task04
          duration: '3 days',
         },
    ],
    contentDescription: "Content is etc",
    }
  ]
}
]

我使用了以下聚合管道

{
      $match: {
        _id: id,
      },
    },
    {
      $unwind: '$sections',
    },
    {
      $unwind: {
        path: '$sections.contents',
        preserveNullAndEmptyArrays: true,
      },
    },
    {
      $unwind: {
        path: '$sections.contents.tasks',
        preserveNullAndEmptyArrays: true,
      },
    },
    {
      $lookup: {
        from: 'tasks',
        let: {
          task_id: '$sections.contents.tasks.tId',
          duration: '$sections.contents.tasks.duration',
        },
        pipeline: [
          {
            $match: {
              $expr: {
                $eq: ['$_id', '$$task_id'],
              },
            },
          },
          {
            $addFields: {
              duration: '$$duration',
            },
          },
        ],
        as: 'sections.contents.tasks',
      },
    },
    {
      $addFields: {
        'sections.contents.tasks': {
          $arrayElemAt: ['$sections.contents.tasks', 0],
        },
      },
    },
    {
      $group: {
        _id: {
          contentId: '$sections.contents.tasks',
          sectionId: '$sections.sectionId',
          sectionName: '$sections.sectionName',
          originalId: '$_id',
        },
        tasks: {
          $push: '$sections.contents.tasks',
        },
        contentDescription: {
          $first: '$sections.contents.contentDescription',
        },
      },
    },
    {
      $sort: { _id: 1 },
    },
    {
      $group: {
        _id: {
          sectionId: '$_id.sectionId',
          sectionName: '$_id.sectionName',
          originalId: '$_id.originalId',
        },
        contents: {
          $push: {
            contentId: '$_id.contentId',
            tasks: '$tasks',
            contentDescription: '$contentDescription',
          },
        },
      },
    },
    {
      $group: {
        _id: '$_id.originalId',
        sections: {
          $push: {
            sectionId: '$_id.sectionId',
            sectionName: '$_id.sectionName',
            contents: '$contents',
          },
        },
      },
    },

而且一切正常,但是有一个折痕,假设如果一个人添加了一个部分而不添加其内容,那么以下数据保存在 MongoDB 中

 [
       {
       sectionId: "sec0",
       sectionName: "ABC",
       contents: [
          {
          contentId: 0,
          tasks: [
             {
              _id: task01
              //properties of task01
              duration: '2 days',
             },
             {
              _id: task02
              //properties of task02
              duration: '3 days',
             },
        ],
        contentDescription: "Content is etc",
        }
      ]
    }
    {
       sectionId: "sec1",
       sectionName: "EFG",    
    }
    ]

对于每个获取请求,我都会得到以下结果(仅发布第二部分元素)

{
   sectionId: "sec1",
   sectionName: "EFG",
   contents: [
      {
       tasks: [],
       contentDescription: null,
       }
     ]
}

     

因此,客户端正在亮起,因为它无法同时找到 contentDescriptiontasks,并且由于内容数组的存在,所有检查都失败了。客户端和后端架构都无法进行重构。

如果没有内容数组存在,desired 输出如下所示

_id: ObjectID('abc'),
sections: [
   {
   sectionId: "sec0",
   sectionName: "ABC",
   contents: [
      {
      contentId: 0,
      tasks: [
         {
          _id: task01
          //properties of task01
          duration: '2 days',
         },
         {
          _id: task02
          //properties of task02
          duration: '3 days',
         },
    ],
    contentDescription: "Content is etc",
    }
  ]
}
{
   sectionId: "sec1",
   sectionName: "EFG",
}
]

我在聚合管道的最后一个组阶段之后添加了以下阶段,如下所示

{
   $addFields: {
      'sections.contents.tasks': {
        $filter: {
           input: '$sections.contents.tasks',
           as: 'task',
           cond: {
                   $ne: ['$$task', []],
                 },
           },
       },
    },
   },

但它不起作用,任何建议或帮助将不胜感激。谢谢

【问题讨论】:

  • 在展开任务数组时使用preserveNullAndEmptyArrays: false 是否可以避免这种情况?
  • 这是我做的第一件事,但不幸的是它不起作用,因为 $unwind 不输出任务数组,结果我得到一个空数组。我确信在最后一个 $group 阶段之后的 $project 阶段有某种方法使用 $cond 或任何其他相关聚合查询,以确保如果 contentDescription 为空,并且任务为空,则过滤掉内容数组。我尝试使用 $project 但我被 $cond 难住了
  • 这是您要找的吗? mongoplayground.net/p/fQL6Fw8pArk
  • preserveNullAndEmptyArrays: false 如果不存在任务数组则不起作用,这是我的问题的症结
  • @Joe,此外,在您的示例中,聚合管道不会显示没有任何内容数组的部分。

标签: node.js mongodb aggregation-framework


【解决方案1】:

如果您希望您的数据集确保添加内容是强制性的,那么只需添加一个条件

if(!req.body.contents){
     return {
        status: 400,
        message: Contents not specified;
}

【讨论】:

  • 问题不是来自客户端,否则第二部分将存储在 MongoDB 中,内容为空。问题是我在聚合管道中使用查找,并且组阶段添加了任务数组和 contentDescription 是否存在于输入文档部分数组中
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-03
  • 1970-01-01
  • 1970-01-01
  • 2015-05-12
  • 1970-01-01
  • 2020-08-19
相关资源
最近更新 更多