Mongoose Relationships Many-To-Many Example

Mongoose Many to Many Example

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

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

MongoDB Related Documents Init Data - Mongoose Relationships Many-To-Many Example
MongoDB Related Documents Init Data

– Get All Subjects:

-> http://localhost:8081/api/subjects

Nodejs Express Mongodb Mongoosejs Many to Many Related Documents Get All Subjects - Mongoose Relationships Many-To-Many Example
Nodejs Express Mongodb Mongoosejs Many to Many Related Documents Get All Subjects

– Get All Students:

-> http://localhost:8081/api/students

Mongoosejs Many-to-many related documents get all students - Mongoose Relationships Many-To-Many Example
Mongoosejs Many-to-many related documents get all students

– Find Student by Name:

-> http://localhost:8081/api/students/Peter

Mongoosejs many-to-many related documents find student by firstname - Mongoose Relationships Many-To-Many Example
Mongoosejs many-to-many related documents find student by firstname

– Find Students learnt a given subject Id:

-> http://localhost:8081/api/students/subject/5ad006faa6996d2634ff65f5

Mongoosejs many-to-many related documents find student learnt a given subject id 1
Mongoosejs many-to-many related documents find student learnt a given subject id 1

Read More

Related posts:


– Reference: Mongoose Association

Sourcecode

Node-js-Mongoose-Many-To-Many

Leave a Reply

Your email address will not be published. Required fields are marked *