# A Beginner's Guide to Subdocuments in Mongoose

As I was testing the TMDB API, I encountered many one-to-many relationships. This made me curious how one-to-many relationships are achieved in a MongoDB/Mongoose database.

The image below shows an example of one-to-many relationships such as the created by , genres and languages elements.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1708789803565/531780c8-37a2-4502-ad28-e45b0c918425.png align="center")

You can model a one-to-many relationship in Mongoose in two ways;

* using subdocuments,
    
* using document references
    

**Subdocuments**, also known as embedded documents, are schemas that are nested in a parent schema while **document references** is a technique where a model references another model using their id.

In this tutorial, I will explain how to use subdocuments in Mongoose to model a one-to-many relationship.

## Prerequisites

* Basic knowledge of Mongoose operations
    
* MongoDB database
    

I will be using Node and Express but you're free to use a language of your choice.

To demonstrate how a subdocument works, we will create a model for a social media post. This is the starter template of the post model.

```javascript
// in Post.js file
const mongoose = require("mongoose")

const PostSchema = new mongoose.Schema({
    picture: Buffer,
    caption: String,
    likes: {
        type: Number,
        default: 0
    },
    createdAt: {
        type: Date,
        default: Date.now
    },
    user: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    }
})

module.exports = mongoose.model('Post', PostSchema)
```

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">The user property is an example of a document reference.</div>
</div>

## How to Specify a Subdocument

Let's add a comment subdocument to the Post model.

1. Creating a Comment schema
    

```javascript
const CommentSchema = new mongoose.Schema({
    message: {
        type: String,
        required: true
    },
    user: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    },
    createdAt: {
        type: Date,
        default: Date.now
    },
    likes: {
        type: Number,
        default: 0
    }
})
```

2. Adding the comment schema to the Post model
    

```javascript
const PostSchema = new mongoose.Schema({
    // the default values above
    comments: [CommentSchema]
})
```

The above code defines an array that contains documents that will be validated by the comment schema.

## How to Add a Subdocument

We will add documents to the comments array by updating the post using the `$push` operator.

The `$push` operator is similar to the `$set` operator but it adds values to an array. The format is:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1708793616950/feb47108-95a6-4abd-addc-0acccf43a893.png align="left")

Let's create a comment.

```javascript
let postId = "485hrfnw8u489rj298j4r"
await Post.updateOne({
    _id: postId
}, {
    $push: {
        comments: {
            message: "This is a nice view",
            user: "587t38hfbg8hteng8etgthhuern"
        }
    }
})
```

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Please ensure you use a valid ObjectId for the <code>user</code> property to avoid getting a validation error.</div>
</div>

You can also check whether the comment was successfully added by querying the post. We will cover this in the next section.

## How to Read a Subdocument

Every subdocument is assigned a unique `ObjectId` which makes it easier for us to query it.

To get a comment, we will use the `id()` method.

```javascript
// get the post that has the comment
let post = Post.findById(postId)
// get the comment using its id
let comment = post.comments.id(commentId)
```

You can also find a subdocument using dot notation. For example, to get a user's comments, you will use this query.

```javascript
await Post.find({
    "comments.user": "67hg8ueugtj8ue78576896"
})
```

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Always use strings for properties that use the dot notation.</div>
</div>

## How to Update a Subdocument

You can use the dot notation to update a document or to add new fields.

Let's update a comment's message.

```javascript
await User.updateOne({
       _id: postId,
       "comments._id": id
    }, {
        $set: {
           "comments.message": "Really nice view!"
        }
})
```

We can also increase the likes property using the `$inc`.

```javascript
await User.updateOne({
       _id: postId,
       "comments._id": id
   }, {
       $inc: {
           "comments.likes": 1
        }
})
```

## How to Delete a Subdocument

We will be using the `id()` method to find the comment then delete the comment using `deleteOne()`.

```javascript
// get the post that has the comment
let post = Post.findById(postId)
// get the comment using its id and delete it
post.comments.id(commentId).deleteOne()
```

## Summary

* A subdocument allows you to have an in-built one-to-many relationship in a document without creating a new model.
    
* Mongoose adds `_id` properties for subdocuments.
    
* You can use dot notation to read and update subdocuments
    
* The inbuilt `id()` method allows you to read a subdocument
    
* The `$inc` operator allows you to increment a property
    
* Every document in Mongoose has a `deleteOne()` method.
