如何使用子文档数组更新 MongoDB 文档

Posted

技术标签:

【中文标题】如何使用子文档数组更新 MongoDB 文档【英文标题】:How to update MongoDB documents with arrays of sub-documents 【发布时间】:2016-11-04 10:47:08 【问题描述】:

我正在使用 MongoDB 数据库,其集合模型 classesstudentssubjects 和 [academic] performance时间>。以下是基于 Mongoose 的架构和模型:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.Types.ObjectId;

// SUBJECT collection
var subjectSchema = new Schema(
    name        :  type: String, required: true ,
    category    :  type: String, required: true 
);
var Subject = mongoose.model('Subject', subjectSchema);

// STUDENT collection
var studentSchema = new Schema(
    name        :  type: String, required: true ,
    grade       :  type: Number, required: true 
);
var Student = mongoose.model('Student', studentSchema);

// PERFORMANCE collection
var performanceSchema = new Schema(
    subjectId   :  type: ObjectId, ref: Subject.modelName, required: true ,
    score       :  type: Number, required: true ,
    maximum     :  type: Number, required: true ,
    grade       :  type: String, required: true 
);
var Performance = mongoose.model('Performance', performanceSchema);

// *Note*: This is just to use as a sub-document schema, and not as a collection
var classStudentSchema = new Schema(
    studentId   :  type: ObjectId, ref: Student.modelName, required: true ,
    performance : [performanceSchema]
,  _id: false );

// CLASS collection
var classSchema = new Schema(
    name        :  type: String, required: true ,
    scores      : [classStudentSchema]
);
var Class = mongoose.model('Class', classSchema);

class 集合的文档是最复杂的;一个示例文档是:


  "_id" : ObjectId("57758f15f68da08c254ebee1"),
  "name" : "Grade 5 - Section A",
  "scores" : [
    "studentId" : ObjectId("5776bd36ffc8227405d364d2"),
    "performance": [
      "subjectId" : ObjectId("577694ecbf6f3a781759c54a"),
      "score" : 86,
      "maximum" : 100,
      "grade" : "B+"
    , 
      "subjectId" : ObjectId("5776ffe1804540e29c602a62"),
      "score" : 74,
      "maximum" : 100,
      "grade" : "A-"
    ]
  , 
    "studentId" : ObjectId("5776bd36ffc8227405d364d5"),
    "performance": [
      "subjectId" : ObjectId("577694ecbf6f3a781759c54a"),
      "score" : 94,
      "maximum" : 100,
      "grade" : "A"
    , 
      "subjectId" : ObjectId("5776ffe1804540e29c602a62"),
      "score" : 81,
      "maximum" : 100,
      "grade" : "A"
    ]
  ]

我能够使用以下代码检索现有课程文档并将学生添加到其分数中:

Class.findOne( name: 'Grade 5 - Section A' , function(err, class) 
  if (err) throw err;
  Student.findOne( name: 'John Doe' , function(err, student) 
    if (err) throw err;
    class.scores.push(
      studentId: student.id
    );
  ;
);

但是如何添加/更新/删除该特定学生的表现?我需要能够通过以下方式与class 集合进行交互:

检索所有学生或特定学生的分数(检索scores 数组中的特定元素) 针对特定学科添加/更新/删除特定学生的分数(在更新或删除的情况下,检索 scores[n].performance 数组中的特定元素;对于添加,附加到同一数组。

【问题讨论】:

【参考方案1】:

有几种方法可以做到这一点,我会逐点回答

检索所有学生或特定学生的分数(检索分数数组中的特定元素)

Class.findOne( name: 'Grade 5 - Section A')
     .populate('scores.studentId')
     .exec(function(err, class) 
       if (err) throw err;
       //now class.scores.studentId becomes ObjectStudent
       //hence you have all scores for all students
);

添加/更新/删除特定学生的分数,针对特定科目(更新或删除时,检索 score[n].performance 数组中的特定元素;添加时,追加到同一个数组.

Class.findOneAndUpdate(name: 'Grade 5 - Section A'
                        ,'scores.studentId': ObjectId('5776bd36ffc8227405d364d2')
                        , 'scores.performance.subjectId' : ObjectId('577694ecbf6f3a781759c54a')
                        , $set: scores.performance. score: 50
                        , function(err, data) 
           if (err) throw err
    );

希望对你有帮助

【讨论】:

首先,您的答案的第一部分效果很好。我不完全理解populate('scores.studentId') 是如何检索每个学生的表演的;看起来很强大。我有一些问题:(a) 我如何获取单个学生特定科目的分数? (b) 如何使用空的 performance 数组将新学生添加到 scores 数组? (c) 如何为特定学生添加单个科目的成绩? (d) 和 (e) 与 (b) 和 (c) 类似,但删除而不是添加。你给了我一个关于如何更新的想法。非常感谢您的帮助! 无忧乐于提供帮助。在一天结束时,您可以提取的最低文件是课程。如果要提取特定字段,可以将它们作为 find 中的第二个参数传递。 findOneAndUpdate(query, 'scores.performance.grade' : 1, function(err))。要添加分数,只需在 mongoose 模型中使用 'push' 运算符,即 model.scores.push(studentId: ObjectId('xxxxxxxxx'), performance:[]) 或使用 find 和 update 并使用 $push 运算符,或者第二种方法是更好,因为它是原子的。 我可以使用填充来解析整个层次结构中的所有外键引用,除了scores.studentId。这样做可以让我获得与studentId 引用相对应的学生,同样,我想获得与subjectId 引用每个学生的performance 数组相对应的整个主题文档?如果我可以在 MongoDB 的上下文中使用该术语,我的目标是获取完全非规范化的 class 文档! 是的,你可以这样做,只要记住每个填充都是对 mongoose 的单独查询。 您能否建议如何执行多级填充?我希望它可以在单个语句中完成,因此studentId 解析为对应的studentsubjectId 解析为查询返回的对象中对应的subject .

以上是关于如何使用子文档数组更新 MongoDB 文档的主要内容,如果未能解决你的问题,请参考以下文章

如何使用C#驱动程序更新MongoDB数组中的子文档

获取数组中每个索引的子文档元素计数并更新子文档键 - 数组中的子文档(IN MONGODB)

如何使用php更新mongodb中具有键和值的子文档

如何使用php更新mongodb中具有键和值的子文档

如何使用 Monk 在 MongoDB 中插入带有子文档数组元素的文档

MongoDB:如何使用 Mongoose 添加或更新子文档?