Mongoose Many-to-Many related models with NodeJS/Express, MongoDB

mongoose-many-to-many-related-models-with-nodejs-express-mongodb-feature-image

In the tutorial, we will show you how to develop Mongoose Many-to-Many related documents with NodeJs/Express, MongoDB.

Related post:
Crud RestAPIs with NodeJS/Express, MongoDB using Mongoose
NodeJs/Express MongoDB One-to-Many related documents

Goal

Prerequisites

Crud RestAPIs with NodeJS/Express, MongoDB using Mongoose
In the above tutorial, we show how to build CRUD RestAPIs with NodeJS/Express and MongoDB using Mongoose:


/nodejs-restapi-mongodb
	/app
		/config
			mongodb.config.js
		/controllers
			customer.controller.js
		/models
			customer.model.js
		/routes
			customer.routes.js
	/node_modules
	package.json
	server.js

Objective

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

Many-to-Many related models

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);
	});
};

Practice

Create a NodeJS/Express project

Following below guide: Crud RestAPIs with NodeJS/Express, MongoDB using Mongoose

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

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);

Route

Init Data Route


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);
}

Controller

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

nodejs-express-mongodb-mongoosejs-many-to-many-related-documents-init-data

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

nodejs-express-mongodb-mongoosejs-many-to-many-related-documents-get-all-subjects

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

nodejs-express-mongodb-mongoosejs-many-to-many-related-documents-get-all-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

nodejs-express-mongodb-mongoosejs-many-to-many-related-documents-find-student-learnt-a-given-subject-id

Sourcecode

Nodejs-Mongoose-Many-To-Many

51 thoughts on “Mongoose Many-to-Many related models with NodeJS/Express, MongoDB”

  1. Great article but I would like to know, how one can do this with the data coming from a form rather than the hard coded data used in this example?

  2. good one.
    how will we findout the students who are attending both maths and computer classes considering the fact that there are theree subjects taken up by students(maths, computer and Physics)

  3. 868094 29244Aw, this was an exceptionally good post. In concept I would like to location in writing such as this moreover – spending time and actual effort to create a outstanding article but so what can I say I procrastinate alot by way of no indicates locate a way to go completed. 202062

  4. Hiya. Very nice site!! Man .. Excellent .. Wonderful .. I’ll bookmark this web site and take the feeds additionally…I am happy to find so much useful info right here within the article. Thanks for sharing…

  5. Nice post. I learn some thing more challenging on different blogs everyday. It will always be stimulating to read content off their writers and exercise a little something from their site. I’d would rather apply certain with the content in my blog whether you don’t mind. Natually I’ll provide link in your web weblog. Thank you sharing.

  6. Hey there, I think your blog might be having browser compatibility issues. When I look at your blog site in Opera, it looks fine but when opening in Internet Explorer, it has some overlapping. I just wanted to give you a quick heads up! Other then that, fantastic blog!

  7. Youre so cool! I dont suppose Ive learn something like this before. So nice to find any individual with some authentic thoughts on this subject. realy thank you for beginning this up. this website is something that’s needed on the web, someone with slightly originality. useful job for bringing something new to the web!

  8. Excellent goods from you, man. I have understand your stuff previous to and you are just extremely wonderful. I really like what you have acquired here, really like what you’re saying and the way in which you say it. You make it entertaining and you still care for to keep it wise. I cant wait to read much more from you. This is really a great site.

  9. This design is wicked! You obviously know how to keep a reader entertained. Between your wit and your videos, I was almost moved to start my own blog (well, almost…HaHa!) Great job. I really enjoyed what you had to say, and more than that, how you presented it. Too cool!

  10. You actually make it appear so easy along with your presentation but I to find this topic to be really something that I think I would by no means understand. It sort of feels too complicated and extremely broad for me. I’m looking forward to your next submit, I will attempt to get the cling of it!

  11. certainly like your web-site but you need to take a look at the spelling on quite a few of your posts. A number of them are rife with spelling issues and I find it very troublesome to tell the truth nevertheless I will certainly come back again.

  12. My programmer is trying to convince me to move to .net from PHP. I have always disliked the idea because of the costs. But he’s tryiong none the less. I’ve been using WordPress on a number of websites for about a year and am nervous about switching to another platform. I have heard fantastic things about blogengine.net. Is there a way I can import all my wordpress posts into it? Any help would be really appreciated!

  13. This is the precise weblog for anybody who needs to find out about this topic. You notice so much its virtually laborious to argue with you (not that I really would need…HaHa). You undoubtedly put a new spin on a subject thats been written about for years. Nice stuff, simply nice!

  14. Hmm it seems like your website ate my first comment (it was super long) so I guess I’ll just sum it up what I submitted and say, I’m thoroughly enjoying your blog. I too am an aspiring blog blogger but I’m still new to everything. Do you have any helpful hints for beginner blog writers? I’d really appreciate it.

  15. There are actually a variety of details like that to take into consideration. That may be a great level to deliver up. I offer the ideas above as normal inspiration but clearly there are questions like the one you bring up the place crucial factor will likely be working in honest good faith. I don?t know if best practices have emerged round things like that, however I’m certain that your job is clearly identified as a good game. Both girls and boys really feel the impact of just a moment’s pleasure, for the remainder of their lives.

  16. Nice post. I was checking continuously this blog and I’m impressed! Very helpful information specially the last part 🙂 I care for such info a lot. I was looking for this certain info for a very long time. Thank you and best of luck.

  17. You have remarked very interesting details! ps decent web site. “I didn’t attend the funeral, but I sent a nice letter saying that I approved of it.” by Mark Twain.

  18. Thank you so much for providing individuals with an exceptionally superb possiblity to check tips from here. It really is very enjoyable and as well , stuffed with fun for me personally and my office acquaintances to visit your site minimum 3 times in 7 days to read through the new guidance you have. And indeed, I am also at all times satisfied with the breathtaking guidelines you serve. Some 3 areas on this page are basically the most efficient we have had.

  19. 879624 956821Exceptional read, I lately passed this onto a colleague who has been performing slightly research on that. And the man really bought me lunch because I came across it for him smile So allow me to rephrase that: Appreciate your lunch! 507240

  20. Hmm it appears like your website ate my first comment (it was extremely long) so I guess I’ll just sum it up what I wrote and say, I’m thoroughly enjoying your blog. I too am an aspiring blog blogger but I’m still new to everything. Do you have any recommendations for first-time blog writers? I’d really appreciate it.

  21. I am now not certain the place you’re getting your information, but great topic. I needs to spend a while finding out more or figuring out more. Thank you for magnificent information I used to be searching for this information for my mission.

  22. This is the right site for everyone who wants to find out about this topic. You realize a whole lot its almost tough to argue with you (not that I personally will need to…HaHa). You definitely put a new spin on a topic that has been discussed for decades. Excellent stuff, just great!

  23. Today, I went to the beach front with my children. I found a sea shell and gave it to my 4 year old daughter and said “You can hear the ocean if you put this to your ear.” She placed the shell to her ear and screamed. There was a hermit crab inside and it pinched her ear. She never wants to go back! LoL I know this is completely off topic but I had to tell someone!

  24. Very interesting details you have noted, thanks for putting up. “She had an unequalled gift… of squeezing big mistakes into small opportunities.” by Henry James.

Leave a Reply

Your email address will not be published.