Embedding: Benefits, Limits, and Safe Array Design

When embedding is a good fit

Embedding places related values inside the parent document as nested documents or arrays. It is especially useful when:

  • Child data is usually read with the parent.
  • The child has no meaningful independent lifecycle.
  • The number of children is bounded or naturally small.
  • The parent and child should be updated atomically.
  • A snapshot is needed even if the source record changes later.

Embedded one-to-one data

{
  _id: ObjectId("65a000000000000000000020"),
  username: "asha",
  profile: {
    displayName: "Asha Rao",
    locale: "en-IN",
    theme: "dark"
  }
}

Embedded one-to-few data

{
  _id: ObjectId("65a000000000000000000021"),
  title: "MongoDB Notes",
  tags: ["database", "mongodb", "backend"],
  settings: {
    visibility: "private",
    allowComments: false
  }
}

Array growth is a design constraint

An array is not automatically wrong, but an unbounded array is dangerous. For example, storing every click event, notification, or transaction inside a user's document causes the document to grow continuously. This can increase document rewrite work, create contention, complicate pagination, and eventually hit MongoDB's 16 MiB maximum BSON document size.

Avoid using an ever-growing array as a substitute for a queryable history collection. If the parent only needs the latest few items, keep a bounded subset or use a separate collection for the full history.

Bounded recent-items pattern

db.users.updateOne(
  { _id: userId },
  {
    $push: {
      recentItems: {
        $each: [{ itemId, viewedAt: new Date() }],
        $sort: { viewedAt: -1 },
        $slice: 10
      }
    }
  }
);

This keeps only the ten most recent entries in recentItems. It is appropriate only if older entries are intentionally disposable or are stored elsewhere.

Nested document updates

Use dot notation to update a nested field without replacing the entire subdocument:

db.customers.updateOne(
  { _id: customerId },
  { $set: { "preferences.theme": "dark" } }
);

Setting preferences to a new object instead may replace the existing subdocument and remove keys that are not included in the replacement.

Specifications & Requirements
  • Embedding:Store related data inside the parent document.
  • Bounded array:An array whose maximum size is intentionally constrained.
  • Dot notation:Target a nested field without replacing the full nested object.
Note

MongoDB documents have a 16 MiB BSON size limit. More importantly, arrays with unlimited growth can become operationally expensive long before reaching that limit.

Bound an embedded array and update a nested field
// Retain only the five latest status events in this compact summary
db.jobs.updateOne(
  { _id: jobId },
  {
    $push: {
      recentStatusEvents: {
        $each: [{ status: "running", at: new Date() }],
        $sort: { at: -1 },
        $slice: 5
      }
    },
    $set: { "settings.notifications": true }
  }
);
Key Takeaways
  • Embed small, bounded, co-accessed data.
  • Use $slice with $push to maintain intentionally bounded recent-item lists.
  • Store complete histories separately when they can grow without limit.
Knowledge Check

Which design is most appropriate for a user's complete, potentially millions-of-events activity history?