DEV Community

Dmitry Romanoff
Dmitry Romanoff

Posted on

Exploring Nested Document Structures in Amazon DocumentDB and MongoDB

When working with Amazon DocumentDB or MongoDB, one of the powerful features is the ability to create deeply nested documents, which can help represent complex, hierarchical data structures. In this article, we’ll dive into how you can handle documents with varying levels of nesting, and explore practical ways to manage and query deeply embedded documents.

Creating a Collection with Two Levels of Embedded Documents

In both Amazon DocumentDB and MongoDB, collections don’t need to be explicitly created. Instead, the collection is created when you insert your first document. Below is an example of inserting a document that has two levels of embedded documents:

db.myCollection.insertOne({
    _id: 1,
    name: "John Doe",
    contact: {
        phone: "123-456-7890",
        address: {
            street: "123 Main St",
            city: "Exampleville",
            zip: "12345"
        }
    }
});
Enter fullscreen mode Exit fullscreen mode

Document Structure:

{
  _id: 1,
  name: "John Doe",
  contact: {              <-- Level 1
    phone: "...",
    address: {            <-- Level 2
      street: "...",
      city: "...",
      zip: "..."
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Verifying the Insertion:

To verify the inserted document, you can run the following command to view it:

db.myCollection.find().pretty()
Enter fullscreen mode Exit fullscreen mode

Inserting a Document with Five Levels of Embedded Documents

Now, let’s create a more complex document with five levels of nested embedded fields:

db.myCollection.insertOne({
    _id: 555,
    level1: {
        level2: {
            level3: {
                level4: {
                    level5: {
                        value: "This is level 5"
                    }
                }
            }
        }
    }
});
Enter fullscreen mode Exit fullscreen mode

Document Structure:

{
  _id: 555,
  level1: {
    level2: {
      level3: {
        level4: {
          level5: {
            value: "This is level 5"
          }
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Verifying the Insertion:

Again, you can verify the insertion using the following query:

db.myCollection.find({ _id: 555 }).pretty()
Enter fullscreen mode Exit fullscreen mode

Listing the Top 10 Documents Ordered by Nesting Depth

To sort documents by their depth of embedding (nesting), we need to write a script that iterates over all documents, computes their depth, and orders them by the deepest one.

Here’s how you can do it in JavaScript (for mongosh or MongoDB Shell):

function getMaxDepth(obj, currentDepth = 0) {
    if (typeof obj !== 'object' || obj === null) return currentDepth;

    let max = currentDepth;
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            const depth = getMaxDepth(obj[key], currentDepth + 1);
            if (depth > max) max = depth;
        }
    }
    return max;
}

let results = [];

db.myCollection.find().forEach(doc => {
    const depth = getMaxDepth(doc);
    results.push({ _id: doc._id, depth: depth });
});

results.sort((a, b) => b.depth - a.depth);

print("Top 10 documents by nesting depth:");
results.slice(0, 10).forEach(doc => printjson(doc));
Enter fullscreen mode Exit fullscreen mode

Sample Output:

Top 10 documents by nesting depth:
{ "_id": ObjectId("..."), "depth": 12 }
{ "_id": ObjectId("..."), "depth": 11 }
...
{ "_id": ObjectId("..."), "depth": 7 }
Enter fullscreen mode Exit fullscreen mode

Adding More Information to the Top 10 Documents

If you want more details about each document, like name and createdAt (if they exist), you can modify the script slightly:

function getMaxDepth(obj, currentDepth = 0) {
    if (typeof obj !== 'object' || obj === null) return currentDepth;

    let max = currentDepth;
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            const depth = getMaxDepth(obj[key], currentDepth + 1);
            if (depth > max) max = depth;
        }
    }
    return max;
}

let results = [];

db.myCollection.find().forEach(doc => {
    const depth = getMaxDepth(doc);
    results.push({
        _id: doc._id,
        name: doc.name || null,
        createdAt: doc.createdAt || null,
        depth: depth
    });
});

results.sort((a, b) => b.depth - a.depth);

print("Top 10 documents by nesting depth:");
results.slice(0, 10).forEach(doc => printjson(doc));
Enter fullscreen mode Exit fullscreen mode

Example Output:

{
  "_id": ObjectId("..."),
  "name": "John Doe",
  "createdAt": ISODate("2024-10-01T10:00:00Z"),
  "depth": 12
}
Enter fullscreen mode Exit fullscreen mode

Investigating the Maximum Nesting Level in a Collection

If you want to explore the maximum level of embedding within a collection, you can iterate through the documents to find the deepest document(s). Here’s how you can do it step by step:

function getMaxDepth(obj, currentDepth = 0) {
    if (typeof obj !== 'object' || obj === null) return currentDepth;

    let max = currentDepth;
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            const depth = getMaxDepth(obj[key], currentDepth + 1);
            if (depth > max) max = depth;
        }
    }
    return max;
}

let maxDepth = 0;
let maxDocs = [];

db.myCollection.find().forEach(doc => {
    const depth = getMaxDepth(doc);

    if (depth > maxDepth) {
        maxDepth = depth;
        maxDocs = [{ _id: doc._id, depth: depth, name: doc.name || null, createdAt: doc.createdAt || null }];
    } else if (depth === maxDepth) {
        maxDocs.push({ _id: doc._id, depth: depth, name: doc.name || null, createdAt: doc.createdAt || null });
    }
});

print(`📏 Maximum embedding depth found: ${maxDepth}`);
print(`🧾 Documents with max depth:`);

maxDocs.forEach(doc => printjson(doc));
Enter fullscreen mode Exit fullscreen mode

Example Output:

📏 Maximum embedding depth found: 202
🧾 Documents with max depth:
{
  _id: ObjectId('684299b8a59156739fc59f39'),
  depth: 202,
  name: null,
  createdAt: null
}
Enter fullscreen mode Exit fullscreen mode

Attempting to Insert a Document with 202 Nested Levels

You might wonder if MongoDB or Amazon DocumentDB will allow you to insert extremely deep documents. Let’s attempt to insert a document with 202 levels of nesting.

let deepDoc = { value: "Level 202" };

for (let i = 201; i >= 1; i--) {
    deepDoc = { [`level${i}`]: deepDoc };
}

try {
    db.myCollection.insertOne(deepDoc);
    print("Unexpected success: Document inserted");
} catch (e) {
    print("❌ Expected failure occurred:");
    print(e.message);
}
Enter fullscreen mode Exit fullscreen mode

Example Output:

Unexpected success: Document inserted
Enter fullscreen mode Exit fullscreen mode

While we might expect an error due to the depth limitation, it seems MongoDB allows insertion, even with extremely deep nesting.


Conclusion

MongoDB and Amazon DocumentDB provide flexible ways to handle deeply nested documents, which can be useful for complex hierarchical data. From simple two-level embeddings to deeply nested structures with hundreds of levels, the ability to dynamically create and query these documents can be a powerful tool. Understanding how to manage the depth of your data and optimize queries can lead to more efficient data handling in your applications.

Top comments (0)