聚合和减少基于ObjectId的嵌套数组

时间:2019-10-09 19:44:56

标签: mongodb mongoose aggregation-framework

我有一个像这样的employeeResponses文档,并且我试图针对[ { ... eventDate: 2019-10-08T03:30:15.000+00:00, employeeResponses: [ { _id:"5d978d372f263f41cc624727", response: "Available to work.", notes: "" }, ...etc ]; } ]; 数组进行查询,以收集单个雇员的所有响应(可能存在或可能不存在):

const eventResponses = await Event.aggregate([
  {
    // find all events for a selected month
    $match: {
      eventDate: {
        $gte: startOfMonth,
        $lte: endOfMonth,
      },
    },
  },
  {
    // unwind the employeeResponses array
    $unwind: {
      path: "$employeeResponses",
      preserveNullAndEmptyArrays: true,
    },
  },
  {
    $group: {
      _id: null,
      responses: {
        $push: {
          // if a response id matches the employee's id, then 
          // include their response; otherwise, it's a "No response."
          $cond: [
            { $eq: ["$employeeResponses._id", existingMember._id] },
            "$employeeResponses.response",
            "No response.",
          ],
        },
      },
    },
  },
  { $project: { _id: 0, responses: 1 } },
]);

我当前的猫鼬聚合是:

employeeResponses

您会毫无疑问地注意到,上面的查询在一个以上的员工记录了一个响应之后将不起作用,因为它将每个单独的响应都视为T / F条件,而不是所有响应$match数组中的strong>作为单个T / F条件。

结果,我删除了最初const responses = eventResponses.reduce((acc, { employeeResponses }) => { const foundResponse = employeeResponses.find(response => response._id.equals(existingMember._id)); return [...acc, foundResponse ? foundResponse.response : "No response."]; }, []); 之后的所有后续查询,并进行了手动减少:

$reduce

我想知道是否有可能获得与上述相同的减少结果,但是也许使用mongo的employeeResponses函数?还是重构上面的聚合查询,将Event内的所有响应都视为一个T / F条件?

此汇总的最终目标是从当月发现的每个["I want to work.", "Available to work.", "Not available to work.", "No response.", "No response." ...etc] 中提取每个先前记录的员工的答复和/或缺少答复,并将他们的答复放入一个单独的数组中:

{{1}}

1 个答案:

答案 0 :(得分:2)

您可以将$filter$map结合使用来重塑数据并按_id进行过滤。然后,如果数组为空,则可以继续使用$push$ifNull来提供默认值:

db.collection.aggregate([
    {
        $addFields: {
            employeeResponses: {
                $map: {
                    input: {
                        $filter: {
                            input: "$employeeResponses",
                            cond: {
                                $eq: [ "$$this._id", "5d978d372f263f41cc624727"]
                            }
                        }
                    },
                    in: "$$this.response"
                }
            }
        }
    },
    {
        $group: {
            _id: null,
            responses: { $push: { $ifNull: [ { $arrayElemAt: [ "$employeeResponses", 0 ] }, "No response" ] } }
        }
    }
])

Mongo Playground