Tutorial: “Mongoose Relationships Many-To-Many Example – Mongoose Many-to-Many related models with NodeJS/Express, MongoDB”
In the tutorial, we will show you how to develop Mongoose Many-to-Many related documents with NodeJs/Express, MongoDB.
Objective – Mongoose Relationships Many-To-Many
In the tutorial, we show how to develop Mongoose Many-to-Many related documents with NodeJS/Express, MongoDB. Project structure:
/Nodejs-Mongoose-Many-To-Many
/app
/config
mongodb.config.js
/controllers
init.controller.js
student.controller.js
subject.controller.js
/models
student.model.js
subject.model.js
/routes
init.routes.js
students.routes.js
subjects.routes.js
/node_modules
package.json
server.js
Mongoose Many-to-Many related models
In the tutorial “Mongoose Relationships Many-To-Many Example”, for working with related documents, we use the ObjectId schema field.
– SubjectSchema:
const mongoose = require('mongoose'), Schema = mongoose.Schema;
const SubjectSchema = mongoose.Schema({
code: String,
name: String
});
module.exports = mongoose.model('Subject', SubjectSchema);
– StudentSchema :
const Subject = require('../models/subject.model.js');
const mongoose = require('mongoose'), Schema = mongoose.Schema;
const StudentSchema = mongoose.Schema({
firstname: String,
lastname: String,
age: { type: Number, required: true },
subjects : [{ type: Schema.Types.ObjectId, ref: 'Subject' }]
});
module.exports = mongoose.model('Student', StudentSchema);
We can save all references to the related documents by assigning the _id value:
// Add Subject to MongoDB
var math = new Subject({
code: 'M-01',
name: 'Mathematics'
});
var computer = new Subject({
code: "C-02",
name: 'Computer'
});
math.save(function (err){
if(err) return console.error(err.stack)
console.log("Math is added")
});
computer.save(function(err){
if(err) return console.error(err.stack);
console.log("Computer is added")
})
// Add Student to MongoDB
var peter = new Student({
firstname: 'Peter',
lastname: 'Thomas',
age: 25
})
peter.subjects.push(math._id, computer._id)
peter.save(function(err){
if(err) return console.log(err.stack);
console.log("Peter is added")
});
...
We use populate()
to get the Subject information in Student:
Student.findOne({ firstname: req.params.firstname })
.populate('subjects')
.exec(function (err, student) {
if (err){
...
}
res.send(student);
});
We didn’t add our students to subjects, how to get all students that learnt the same particular subject?
– One way, we create a references array field of students in SubjectSchema as below:
const Student = require('../models/student.model.js');
const mongoose = require('mongoose'), Schema = mongoose.Schema;
const SubjectSchema = mongoose.Schema({
code: String,
name: String,
students : [{ type: Schema.Types.ObjectId, ref: 'Student' }]
});
module.exports = mongoose.model('Subject', SubjectSchema);
But What is problem with above way? -> We have two places where the information relating students and subjects needs to be maintained.
What is the better solution?
– We get the _id of our subject, then use find()
to search for this in the subjects field across all students.
exports.findBySubjectId = (req, res) => {
Student.find({ subjects : req.params.subjectId })
.exec(function (err, students) {
if (err){
...
}
res.send(students);
});
};
Create a NodeJS/Express project – Mongoose Relationships Many-To-Many
See dependencies in ‘package.json’ file:
"dependencies": {
"body-parser": "^1.18.2",
"express": "^4.16.3",
"mongoose": "^5.0.13",
"npm": "^5.8.0"
}
Create Model Schemas – Mongoose Relationships Many-To-Many
– Subject Schema:
const mongoose = require('mongoose'), Schema = mongoose.Schema;
const SubjectSchema = mongoose.Schema({
code: String,
name: String
});
module.exports = mongoose.model('Subject', SubjectSchema);
– Student Schema:
const Subject = require('../models/subject.model.js');
const mongoose = require('mongoose'), Schema = mongoose.Schema;
const StudentSchema = mongoose.Schema({
firstname: String,
lastname: String,
age: { type: Number, required: true },
subjects : [{ type: Schema.Types.ObjectId, ref: 'Subject' }]
});
module.exports = mongoose.model('Student', StudentSchema);
Express RestAPI Route
In the tutorial “Mongoose Relationships Many-To-Many”, we initial Data Route as below:
module.exports = function(app) {
var initialController = require('../controllers/init.controller.js')
app.get('/api/data/init', initialController.init);
}
Subject Routes
module.exports = function(app) {
var subjects = require('../controllers/subject.controller.js')
app.get('/api/subjects', subjects.findAll);
}
– Student Routes:
module.exports = function(app) {
var students = require('../controllers/student.controller.js');
// Get All Students
app.get('/api/students', students.findAll);
// Find a single Student by Name
app.get('/api/students/:firstname', students.findByName);
// Find all Students that learnt a given subject
app.get('/api/students/subject/:subjectId', students.findBySubjectId);
}
Nodejs Express Controller – Mongoose Relationships Many-To-Many
– Init Data Controller :
const Subject = require('../models/subject.model.js');
const Student = require('../models/student.model.js');
exports.init = (req, res) => {
// Add Subject to MongoDB
var math = new Subject({
code: 'M-01',
name: 'Mathematics'
});
var computer = new Subject({
code: "C-02",
name: 'Computer'
});
math.save(function (err){
if(err) return console.error(err.stack)
console.log("Math is added")
});
computer.save(function(err){
if(err) return console.error(err.stack);
console.log("Computer is added")
})
// Add Students to MongoDB
var jack = new Student({
firstname: 'Jack',
lastname: 'Davis',
age: 21
});
jack.subjects.push(math._id, computer._id);
var peter = new Student({
firstname: 'Peter',
lastname: 'Thomas',
age: 25
})
peter.subjects.push(math._id, computer._id)
peter.save(function(err){
if(err) return console.log(err.stack);
console.log("Peter is added")
});
jack.save(function(err){
if(err) return console.log(err.stack);
console.log("Jack is added");
});
// Return Message
res.send("Done Initial Data!");
}
– Subject Controller :
const Subject = require('../models/subject.model.js');
exports.findAll = (req, res) => {
Subject.find()
.then(subjects => {
res.send(subjects);
}).catch(err => {
res.status(500).send({
message: err.message
});
});
};
Student Controller
const Student = require('../models/student.model.js');
const Subject = require('../models/subject.model.js');
// Get All Students
exports.findAll = (req, res) => {
Student.find()
.then(students => {
res.send(students);
}).catch(err => {
res.status(500).send({
message: err.message
})
});
};
// Find a Student by firstname
exports.findByName = (req, res) => {
Student.findOne({ firstname: req.params.firstname })
.populate('subjects')
.exec(function (err, student) {
if (err){
if(err.kind === 'ObjectId') {
return res.status(404).send({
message: "Student not found with given firstname " + req.params.firstname
});
}
return res.status(500).send({
message: "Error retrieving Student with given firstname" + req.params.firstname
});
}
res.send(student);
});
};
// Find all student learnt a given subject
exports.findBySubjectId = (req, res) => {
Student.find({ subjects : req.params.subjectId })
.exec(function (err, students) {
if (err){
if(err.kind === 'ObjectId') {
return res.status(404).send({
message: "Student not found with given Subject Id " + req.params.subjectId
});
}
return res.status(500).send({
message: "Error retrieving Student with given subject Id " + req.params.subjectId
});
}
res.send(students);
});
};
Run & Check results
– Run MongDB server by commandline:
\MongoDB\Server\3.6\bin>mongod.exe
2018-04-13T05:13:11.363+0700 I CONTROL [initandlisten] MongoDB starting : pid=1584 port=27017 dbpath=C:\data\db\ 64-bit host=LOI-COMPUTER
2018-04-13T05:13:11.365+0700 I CONTROL [initandlisten] targetMinOS: Windows 7/Windows Server 2008 R2
2018-04-13T05:13:11.366+0700 I CONTROL [initandlisten] db version v3.6.3
2018-04-13T05:13:11.366+0700 I CONTROL [initandlisten] git version: 9586e557d54ef70f9ca4b43c26892cd55257e1a5
2018-04-13T05:13:11.366+0700 I CONTROL [initandlisten] OpenSSL version: OpenSSL 1.0.1u-fips 22 Sep 2016
...
Run NodeJS/Express application:
>node server.js
App listening at http://:::8081
Successfully connected to MongoDB.
– Initial data
-> http://localhost:8081/api/init

– Get All Subjects:
-> http://localhost:8081/api/subjects

– Get All Students:
-> http://localhost:8081/api/students

– Find Student by Name:
-> http://localhost:8081/api/students/Peter

– Find Students learnt a given subject Id:
-> http://localhost:8081/api/students/subject/5ad006faa6996d2634ff65f5

Read More
– Reference: Mongoose Association