Says we currently have two collections: Teachers
and Students
, we limit each teacher can only owns up to 30 students, surely I have already set a reference-key like studentsId
, teachersId
on each document.
When I'm about to create a new Student, how can I make sure I only create it when the specify teacher has less than 30 students?
// get students count under specify teacher
const StudentNums = await Teachers.findById(1234).studentsId.length;
if (StudentNums < 30) /* create student */
(above code is written JS)
undoubtedly above code would fail when concurrency, cause mongodb share read locks, any ideas? tks in advance.
You can apply standard Optimistic lock on application level.
Assuming a document in teacher collection has following fields:
db.teachers.insert({
_id: 1234,
students: [12,34],
version: 8
});
Where _id
and students
are object's payload, and version
is an internal field for concurrency control. The update in the question may look like following:
var teacher = db.teachers.findById(1234);
if(teacher.students.length < 30) {
var res = db.teachers.update({_id: 1234, version: teacher.version}, {$push: {students: 56}, $inc: {version: 1}});
}
if(res.nModified < 1) {
// apply retry logic
}
If the concurrent update happened, version of the document in the database won't match the query part of the update, which will result with 0 documents updated.
Instead of handling anything related to concurrency, why not just check if your teacher can accomodate any more students ? You can check and update in a single operation to ensure atomicity.
It essentially boils down to adding student length check in find query like below :
teachers.update({id: 1, '$where': 'this.students.length < 8'}, {'$push': {students: 8}}
Added a test case below:
const mongodb = require('mongodb')
const mongoClient = mongodb.MongoClient
mongoClient.connect('mongodb://localhost:27017/stackoverflow', function (err, db) {
if (err) {
console.log(`DB connect error ${JSON.stringify(err)}`)
} else {
const teachers = db.collection('teachers')
let doc = {id: 1, name: 'Ms Anderson', students: [1, 2, 3, 4, 5, 6, 7], subject: 'Matrix'}
teachers.insert(doc, {w: 1}, function (err, result) {
console.log(`insert error ${JSON.stringify(err)}`)
console.log(`insert result : ${JSON.stringify(result)}`)
//Check the $where parameter, it will return only if student array has size less than 8
teachers.update({id: 1, '$where': 'this.students.length < 8'}, {'$push': {students: 8}}, {w: 1}, function (err, result) {
console.log(`update error ${JSON.stringify(err)}`)
console.log(`update result : ${JSON.stringify(result)}`)
})
})
}
})
result
is 1 if document is updated else 0.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.