DEV Community

Dan N
Dan N

Posted on

Adding, Updating, and Removing Subdocuments with Mongoose

I was building an application when I ran into a problem; how do I use subdocuments?

I found a way that worked for what I needed; I'll use an example where the application has users and each user can have one or more posts.

Here's what the model for the posts could look like:

const mongoose = require("mongoose");

const postSchema = new mongoose.Schema({
  title: { type: String, required: true },
  text: { type: String },  
});

const Post = mongoose.model("Post", postSchema);

module.exports = Post;
Enter fullscreen mode Exit fullscreen mode

And here's the model for the user:

const mongoose = require("mongoose");
const postSchema = require("./post.js").schema; //or wherever post.js is

const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  details: { type: String },
  userid: { type: String, required: true,
    index: {
      unique: true,
    }
  },
  posts: [postSchema]
});

const User = mongoose.model("User", userSchema);

module.exports = User;
Enter fullscreen mode Exit fullscreen mode

The important thing to note here is that the each user will have an array of posts and that each of those is an array of post schemas, not the post model itself.

Adding subdocuments

Now let's get to adding the subdocuments!

For adding posts, I have this where I have this where I have my routes:

const Project = require("../models/project.js");

router.put("/addpost/:id", (req, res) => {

  //find the user first, then add the post to it
  User.findById(req.params.id, function(err, result) {
    if (!err) {
      if (!result){
        res.sendStatus(404).send('User was not found').end();
      }
      else{
        result.posts.push(req.body);
        result.markModified('posts'); 
        result.save(function(saveerr, saveresult) {
          if (!saveerr) {
            res.status(200).send(saveresult);
          } else {
            res.status(400).send(saveerr.message);
          }
        });
      }
    } else {
      res.status(400).send(err.message);
    }
  });
});

Enter fullscreen mode Exit fullscreen mode

Here, I look for the existing user first (There's more than one way to do this; I did it by using findById() and passing in the id from the route parameter). The part that tripped me up here was, what do I do after finding the user? It turns out that I can edit the result in the callback of the find directly - by adding the new post to the user's post array, and then using the save() function to save those changes to the user in MongoDB! If I go to my database on MongoDB and navigate to the user I just edited, I'll be able to find the post I created for that particular user under the "posts" array. If I expand that post, I should be able to see that MongoDB created a unique id for it. This will be important later.

Updating subdocuments

router.put("/updatepost/:id", (req, res) => {
  //find user by its id, update its post with what's in req.body
  User.findById(req.params.id, function(err, result) {
    if (!err) {
      if (!result){
        res.status(404).send('User was not found');
      }
      else{
        result.posts.id(req.body._id).title = req.body.title;
        result.posts.id(req.body._id).text = req.body.text;
        result.markModified('posts'); 
        result.save(function(saveerr, saveresult) {
          if (!saveerr) {
            res.status(200).send(saveresult);
          } else {
            res.status(400).send(saveerr.message);
          }
        });
      }
    } else {
      res.status(400).send(err.message);
    }
  });
});
Enter fullscreen mode Exit fullscreen mode

First, I find the user like I did before. This time, since I'm looking for an existing post, I can use id() to find that post in the posts array identified by its unique id. I set the fields that I want to change and then use save() to save the user.

Removing subdocuments

router.put("/removepost/:id", (req, res) => {
  //find the user by the id parameter first, then locate and remove the post specified by the id in req.body 
  User.findById(req.params.id, function(err, result) {
    if (!err) {
      if (!result){
        res.status(404).send('User was not found');
      }
      else{
        result.posts.id(req.body._id).remove(function(removeerr, removresult) {
          if (removeerr) {
            res.status(400).send(removeerr.message);
          }
        });
        result.markModified('posts'); 
        result.save(function(saveerr, saveresult) {
          if (!saveerr) {
            res.status(200).send(saveresult);
          } else {
            res.status(400).send(saveerr.message);
          }
        });
      }
    } else {
      res.status(400).send(err.message);
    }
  });
});
Enter fullscreen mode Exit fullscreen mode

For this last part, I again first located the user. Then I found the specific post I wanted to delete by its id using id() and then removed it by calling remove(). Again, I used save() to save the changes.

For reference: https://mongoosejs.com/docs/subdocs.html

Top comments (0)