Manage MongoDB Embedded Docs

MongoDBBeginner
Practice Now

Introduction

In this lab, you will learn how to effectively manage embedded documents in MongoDB. Embedded documents, or nested documents, are a core feature of MongoDB's data model that allows you to store complex, hierarchical data within a single document. You will learn a range of techniques, including creating documents with nested data, updating specific fields within them, removing elements, and querying these complex structures. You will also learn how to enforce a consistent data structure using schema validation. These skills are essential for building robust and efficient applications with MongoDB.

Create Documents with Nested Data

In this step, you will learn how to create documents with nested structures in MongoDB. This is a fundamental skill for modeling related data within a single document.

First, open the MongoDB Shell (mongosh) to begin interacting with the database. All subsequent commands in this lab will be run inside this shell unless otherwise specified.

mongosh

Next, switch to a new database named bookstore. If this database does not exist, MongoDB will create it for you when you first store data in it.

use bookstore

Now, you will create a collection called books and insert a single document. This document will contain nested objects and an array of nested objects, representing a book's details.

db.books.insertOne({
    title: "Advanced MongoDB",
    author: {
        name: "Jane Smith",
        contact: {
            email: "jane.smith@example.com",
            phone: "+1-555-123-4567"
        }
    },
    published: {
        year: 2023,
        publisher: "Tech Publications"
    },
    chapters: [
        { number: 1, title: "Introduction to Nested Documents" },
        { number: 2, title: "Advanced Document Structures" }
    ]
})

In the document you just created:

  • author and published are nested documents (objects).
  • The author document contains another nested document, contact.
  • chapters is an array that contains multiple nested documents, each representing a chapter.

To view the document and confirm its structure, use the find() method.

db.books.find()

You should see the following output, confirming the document was inserted correctly. The _id is a unique identifier automatically generated by MongoDB.

[
  {
    _id: ObjectId("..."),
    title: 'Advanced MongoDB',
    author: {
      name: 'Jane Smith',
      contact: {
        email: 'jane.smith@example.com',
        phone: '+1-555-123-4567'
      }
    },
    published: {
      year: 2023,
      publisher: 'Tech Publications'
    },
    chapters: [
      { number: 1, title: 'Introduction to Nested Documents' },
      { number: 2, title: 'Advanced Document Structures' }
    ]
  }
]

Update Nested Fields and Array Elements

After creating documents, you will often need to modify parts of them. In this step, you will learn how to update specific fields within nested documents and arrays. You should still be in the mongosh shell from the previous step.

First, let's update the author's contact information. To do this, you use the $set operator with dot notation to specify the exact path to the field you want to change. This command will change the phone number and add a new website field.

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $set: {
        "author.contact.phone": "+1-888-999-0000",
        "author.contact.website": "www.janesmith.com"
    } }
)

The dot notation "author.contact.phone" tells MongoDB to navigate into the author object, then into the contact object, and update the phone field.

Next, you will add a new chapter to the chapters array using the $push operator.

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $push: {
        chapters: {
            number: 3,
            title: "MongoDB Advanced Techniques"
        }
    } }
)

You can also update a specific element within an array. Let's modify the title of the first chapter. We use the positional $ operator, which acts as a placeholder for the first array element that matches the query condition ("chapters.number": 1).

db.books.updateOne(
    { title: "Advanced MongoDB", "chapters.number": 1 },
    { $set: {
        "chapters.$.title": "Introduction to Nested Documents (Revised)"
    } }
)

To verify all the changes, retrieve the document again. The .pretty() method formats the output to be more readable.

db.books.find({ title: "Advanced MongoDB" }).pretty()

The output will show the updated phone number, the new website, the added third chapter, and the revised title for the first chapter.

Remove Nested Fields and Array Elements

In this step, you will learn how to remove parts of your documents. This includes removing specific fields, entire nested documents, and elements from an array.

First, let's remove the website field from the author's contact information using the $unset operator. The value provided to $unset (in this case, an empty string "") does not matter; the operator simply removes the specified field.

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $unset: { "author.contact.website": "" } }
)

Next, you will remove an element from an array. Let's remove the third chapter (the one with number: 3) from the chapters array using the $pull operator. The $pull operator removes all array elements that match the specified condition.

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $pull: {
        chapters: { number: 3 }
    } }
)

Finally, you can remove an entire nested object. Let's remove the contact object from the author document, again using the $unset operator.

db.books.updateOne(
    { title: "Advanced MongoDB" },
    { $unset: { "author.contact": "" } }
)

To see the result of these removal operations, query the document again.

db.books.find({ title: "Advanced MongoDB" }).pretty()

The output will show that the website field and the entire contact object are gone, and the chapters array now contains only two elements.

Query Documents with Nested Data

Querying is a fundamental database operation. In this step, you will learn how to query documents based on values within nested objects and arrays. To ensure we have a clean set of data for these examples, first drop the existing books collection.

db.books.drop()

Now, insert a few new documents with different nested structures to practice querying.

db.books.insertMany([
    {
        title: "MongoDB Essentials",
        author: {
            name: "John Doe",
            experience: { years: 5, specialization: "Database Design" }
        },
        tags: ["beginner", "database", "nosql"],
        chapters: [
            { number: 1, title: "Introduction", pages: 25 },
            { number: 2, title: "Advanced Concepts", pages: 45 }
        ]
    },
    {
        title: "Advanced Database Techniques",
        author: {
            name: "Jane Smith",
            experience: { years: 8, specialization: "Distributed Systems" }
        },
        tags: ["advanced", "distributed", "nosql"],
        chapters: [
            { number: 1, title: "System Design", pages: 35 },
            { number: 2, title: "Performance Optimization", pages: 55 }
        ]
    }
])

To find a book by a field in a nested document, use dot notation. This query finds the book written by "John Doe".

db.books.find({ "author.name": "John Doe" })

You can go deeper into nested structures. This query finds books where the author's specialization is "Database Design".

db.books.find({ "author.experience.specialization": "Database Design" })

To find documents where an array contains a specific value, you can query the array field directly. This finds all books tagged with "nosql".

db.books.find({ tags: "nosql" })

You can also query for documents where an array element meets a certain condition. This query finds books that have at least one chapter longer than 40 pages, using the $gt (greater than) operator.

db.books.find({ "chapters.pages": { $gt: 40 } })

Finally, you can combine multiple conditions. This query finds books tagged as "advanced" written by an author with 5 or more years of experience ($gte means greater than or equal to).

db.books.find({
    "author.experience.years": { $gte: 5 },
    tags: "advanced"
})

Enforce Document Structure with Schema Validation

To maintain data consistency, MongoDB allows you to enforce a specific structure for documents in a collection using JSON Schema validation. In this step, you will create a new collection with a validator to ensure all documents adhere to a predefined structure.

First, create a new collection named courses. When creating it, you will pass a validator option containing the schema rules.

db.createCollection("courses", {
   validator: {
      $jsonSchema: {
         bsonType: "object",
         required: ["title", "instructor", "duration", "topics"],
         properties: {
            title: {
               bsonType: "string",
               description: "must be a string and is required"
            },
            instructor: {
               bsonType: "object",
               required: ["name", "email"],
               properties: {
                  name: { bsonType: "string" },
                  email: {
                     bsonType: "string",
                     pattern: "^.+@.+$"
                  }
               }
            },
            duration: {
               bsonType: "int",
               minimum: 1,
               maximum: 100
            },
            topics: {
               bsonType: "array",
               minItems: 1,
               items: { bsonType: "string" }
            }
         }
      }
   }
})

This schema requires every document to have a title, instructor, duration, and topics. It also defines the data type for each field, including nested rules for the instructor object and the topics array.

Now, try inserting a document that conforms to these rules. This operation should succeed.

db.courses.insertOne({
   title: "MongoDB Advanced Techniques",
   instructor: {
      name: "Jane Smith",
      email: "jane.smith@example.com"
   },
   duration: 40,
   topics: ["Nested Documents", "Schema Validation"]
})

Next, attempt to insert a document that violates the schema rules. This document has an incorrect data type for instructor.name, an invalid email format, a duration outside the allowed range, and an empty topics array.

db.courses.insertOne({
   title: "Invalid Course",
   instructor: {
      name: 123,
      email: "invalid-email"
   },
   duration: 200,
   topics: []
})

This insertion will fail, and MongoDB will return a Document failed validation error. This prevents inconsistent data from being saved and helps maintain the integrity of your database.

Summary

In this lab, you have learned the fundamental techniques for managing embedded documents in MongoDB. You started by creating documents with complex nested structures, including objects and arrays. You then practiced updating specific fields using dot notation and operators like $set and $push. You also learned how to remove data with $unset and $pull. Furthermore, you explored how to perform targeted queries on nested data and, finally, how to enforce data integrity by defining and applying a JSON Schema validator to a collection. These skills are crucial for building applications that effectively leverage MongoDB's flexible document model.