MongoDB在对象数组中获取嵌套在对象数组中的单个对象

Posted

技术标签:

【中文标题】MongoDB在对象数组中获取嵌套在对象数组中的单个对象【英文标题】:MongoDB Get Single Object Nested in Array of Objects in Array of Objects 【发布时间】:2018-06-12 02:16:26 【问题描述】:

如何根据一些属性值访问嵌套在对象数组中的单个对象,该对象数组嵌套在另一个对象数组中,类似于伪代码中的内容:

选择 DAY=1 WHERE _id=5a3469f22dc3784bdd9a6190 AND MONTH=12

Mongoose 模型架构如下所示。根据需要,子文档的列表高于其对应的父文档,dailySchedulesSchema 最高:

var dailySchedulesSchema = new mongoose.Schema(
    day: Number,
    dayStart: Number,
    firstBreakStart: Number,
    firstBreakEnd: Number,
    lunchStart: Number,
    lunchEnd: Number,
    secondBreakStart: Number,
    secondBreakEnd: Number,
    dayEnd: Number,
    workDuration: Number
);

var monthlyScheduleSchema = new mongoose.Schema(
    month: type: Number, required: true ,
    dailySchedules: [dailySchedulesSchema]
);

var employeeSchema = new mongoose.Schema(
    name: type: String, required: true,
    surname: type: String, required: true,
    email: type: String, required: true,
    phone: type: String, required: true,
    occupation: type: String, required: true,
    status: type: Boolean, required: true,
    monthlySchedule: [monthlyScheduleSchema]
);

这是我正在尝试处理的数据库中的员工条目。:

"_id" : ObjectId("5a3469f22dc3784bdd9a6190"),
        "name" : "Eric",
        "surname" : "K. Farrell",
        "email" : "EricKFarrell@dayrep.com",
        "phone" : "864-506-7281",
        "occupation" : "Employee",
        "status" : true,
        "monthlySchedule" : [
                
                        "month" : 12,
                        "dailySchedules" : [
                                
                                        "day" : 1,
                                        "dayStart" : 480,
                                        "firstBreakStart" : 600,
                                        "firstBreakEnd" : 615,
                                        "lunchStart" : 720,
                                        "lunchEnd" : 750,
                                        "secondBreakStart" : 870,
                                        "secondBreakEnd" : 885,
                                        "dayEnd" : 1020,
                                        "workDuration" : 480
                                ,
                                
                                        "day" : 2,
                                        "dayStart" : 540,
                                        "firstBreakStart" : 630,
                                        "firstBreakEnd" : 645,
                                        "lunchStart" : 750,
                                        "lunchEnd" : 780,
                                        "secondBreakStart" : 870,
                                        "secondBreakEnd" : 885,
                                        "dayEnd" : 1050,
                                        "workDuration" : 480
                                
                        ]
                
        ]

获取单日的路线本身是:"/employees/:employeeid/:month/:day"

虽然我设法访问了父文档(例如列出所有员工),但我无法列出特定的子文档条目(例如该员工的具体日程安排) - mongoose 要么已返回当月的所有现有日程安排或者什么都没有:

(...)

  var sendJsonResponse = function(res, status, content)
        res.status(status);
        res.json(content);
    

(...)

module.exports.empDayReadOne = function(req, res)
    var monthParam = req.params.month;
    var employeeid = req.params.employeeid;
    var dayParam = req.params.day;

    Emp
        .aggregate([
        $match: $and: ['monthlySchedule': $elemMatch: $exists: true  , _id: employeeid ]  ,
        $unwind:'$monthlySchedule', 
        $unwind:'$monthlySchedule.dailySchedules', 
        $match: $and:[ 'monthlySchedule.dailySchedules.day': dayParam,'monthlySchedule.month': monthParam ]  

        ])
        .exec(function(err, dailySchedule)
            if(dailySchedule)
                sendJsonResponse(res, 200, dailySchedule);
             else if(err)
                sendJsonResponse(res, 400, err);
                return;
             else 
                sendJsonResponse(res, 404, "message": "This day has no schedules added.");
                return;
            

        );

;

【问题讨论】:

你用的是哪个版本 我使用的是 3.2.5 版本 【参考方案1】:

您可以尝试以下聚合查询。

下面的查询首先是$filtersmonthlySchedule,它返回具有匹配月份数组元素的数组,然后是第二个过滤器以检索dailySchedules数组中匹配的日期元素。

$arrayElemAt 将具有单个元素的数组转换为文档。

db.collection_name.aggregate([
  "$match":"_id":ObjectId("5a3469f22dc3784bdd9a6190"),
  "$project":
    "dailyschedule":
      "$arrayElemAt":[
        "$filter":
          "input":
            "$filter":
              "input":"$monthlySchedule",
              "cond":"$eq":["$$this.month",12]
            
          ,
          "cond":"$eq":["$$this.day",1]
          
        ,
      0]
    
  
])

更新了 Mongoose 代码:

Emp.aggregate([
  "$match":"_id":mongoose.Types.ObjectId(employeeid),
  "$project":
    "dailyschedule":
      "$arrayElemAt":[
        "$filter":
          "input":
            "$filter":
              "input":"$monthlySchedule",
              "cond":"$eq":["$$this.month",monthParam]
            
          ,
          "cond":"$eq":["$$this.day",dayParam]
          
        ,
      0]
    
  
])

【讨论】:

但是使用 $arrayElemAt 您需要知道值在数组中的索引,而上面的示例似乎没有考虑到这一点,只是关心它搜索的特定键值。老实说,这看起来像是一个更实际的例子。 $arrayElemAt 索引为 0 用于将匹配的日程安排(一次匹配)转换为文档。如果没有 arrayElemAt,您最终将得到包含匹配日程安排的数组。如果您愿意,可以删除 arrayElemAt。 $unwind 正是我对 arrayElemAt 所做的,因为它总是会输出一个每日日程匹配。所以这里不需要 $unwind。 findOne 方法似乎做得很好,除了返回整个 dailySchedules 数组。有没有办法抑制其余的索引? Emp.findOne( _id: employeeid, "monthlySchedule.month":monthParam, "monthlySchedule.dailySchedules.day": dayParam ) 这就是我们使用聚合的原因,因为使用常规查询语言无法做到这一点。聚合代码对您不起作用吗?它非常简单,您首先根据月份参数过滤monthlySchedule 数组,然后根据day 参数过滤daySchedule 数组。如果您坚持在查找查询中执行此操作,那么您可以做的最好的事情是根据服务器的月度参数输出月度计划数组,并在日参数上对日计划进行客户端过滤。希望这是有道理的。 尝试var monthParam = Number.parseInt(req.params.month) 并将其传递给聚合方法。如我的回答所示,不需要 $unwind。

以上是关于MongoDB在对象数组中获取嵌套在对象数组中的单个对象的主要内容,如果未能解决你的问题,请参考以下文章

mongodb $查找带有投影的数组中的嵌套对象

MongoDB - 基于嵌套数组的字段值更新数组对象中的字段

将嵌套的 mongoDB 文档转换为平面 pandas DataFrame(对象数组中的对象数组)

带有 C#Driver 的 MongoDB:如何过滤嵌套对象数组中的字段

MongoDB,从数组中的对象中删除嵌套项

搜索并将数组推送到MongoDB中的嵌套对象数组[重复]