如何使用子文档数组更新 MongoDB 文档
Posted
技术标签:
【中文标题】如何使用子文档数组更新 MongoDB 文档【英文标题】:How to update MongoDB documents with arrays of sub-documents 【发布时间】:2016-11-04 10:47:08 【问题描述】:我正在使用 MongoDB 数据库,其集合模型 classes、students、subjects 和 [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
解析为对应的student,subjectId
解析为查询返回的对象中对应的subject .以上是关于如何使用子文档数组更新 MongoDB 文档的主要内容,如果未能解决你的问题,请参考以下文章
获取数组中每个索引的子文档元素计数并更新子文档键 - 数组中的子文档(IN MONGODB)