MongoDB Schema Patterns

A schema pattern is a proven way to shape documents for a common problem. Experienced MongoDB developers reuse these patterns the way builders reuse standard building plans. Each pattern solves one kind of trouble, such as slow reads, huge documents, or repeated calculations.

The Core Rule

MongoDB design starts from one question: how will the application read and write this data? The best schema serves the most frequent actions with the fewest reads. Patterns give ready-made answers to this question.

Application needs  --->  Choose pattern  --->  Shape the documents
(frequent reads,         (see table below)      (embed, copy, group,
 big lists, totals)                              or precompute)

Pattern Overview

PatternProblem It Solves
Extended ReferenceToo many joins for small, common details
SubsetDocuments grow too large for memory
BucketMillions of tiny documents from streams of data
ComputedRepeated calculations slow every read
AttributeMany different optional fields across products

Extended Reference Pattern

Orders reference customers by id. Showing an order list with customer names would need a join on every request. The Extended Reference pattern copies the few fields that the screen always needs into the order document.

Without the pattern:                   With the pattern:

order  { customerId: 7 }               order { customer: { id: 7, name: "Asha" } }
   |                                       |
   +--join--> customers { _id: 7 ... }     (name already inside, no join)
{
  _id: 501,
  item: "Notebook",
  customer: { id: 7, name: "Asha", city: "Pune" }
}

Copy only small fields that rarely change. The full customer record stays in the customers collection.

Subset Pattern

A product page may show a product and its latest five reviews, while the product holds thousands of reviews in total. Loading all reviews on each page view wastes memory. The Subset pattern keeps the most useful slice inside the main document and moves the rest to a separate collection.

products collection                 reviews collection
+------------------------+          +-----------------------------+
| name: "Headset"        |          | 2,400 older reviews         |
| recentReviews: [ 5 ]   |  --->    | linked by productId         |
+------------------------+          +-----------------------------+
 fast to load                        loaded only on request

Bucket Pattern

A sensor that sends one reading every second creates 86,400 documents per day. The Bucket pattern groups readings into one document per hour or per day.

{
  sensorId: "S1",
  hour: ISODate("2026-05-01T10:00:00Z"),
  count: 3600,
  readings: [
    { sec: 0, temp: 24.1 },
    { sec: 1, temp: 24.2 }
  ],
  sumTemp: 86800
}

The collection holds 24 documents per day per sensor instead of 86,400. Indexes shrink, and queries for a time range read far fewer documents. MongoDB offers time series collections, which apply a similar idea automatically.

Bucket Size Guide

  • Pick a bucket that holds enough values to cut the document count noticeably.
  • Stay well below the 16 megabyte document limit.
  • Match the bucket to how people query, such as one bucket per hour for hourly charts.

Computed Pattern

A page that shows the total sales of a product must add thousands of order values on every view. The Computed pattern calculates the result once, stores it, and updates it when new data arrives.

db.products.updateOne(
  { _id: 1 },
  { $inc: { totalSales: 250, orderCount: 1 } }
)

Readers fetch totalSales with a single quick read. The write side carries a small extra cost, so the pattern fits data that applications read far more often than they change.

Attribute Pattern

Products carry different details. A shirt has a size and a fabric, and a laptop has memory and a screen size. Creating an index on every possible field becomes impossible. The Attribute pattern stores these details as a list of key and value pairs, so one index covers all of them.

{
  name: "Laptop",
  specs: [
    { k: "memory", v: "16GB" },
    { k: "screen", v: "14 inch" }
  ]
}

db.products.createIndex({ "specs.k": 1, "specs.v": 1 })

Embed or Reference

Every pattern builds on the basic choice between embedding and referencing.

Choose Embedding WhenChoose Referencing When
Applications read the data togetherApplications read the data separately
The embedded part stays smallThe related part grows without limit
The embedded part rarely changes aloneMany documents share the same related data

Anti-Patterns to Avoid

  • Unbounded arrays: A list that grows forever, such as every comment on a viral post, eventually hits the document size limit.
  • Too many collections: Splitting data into hundreds of near-identical collections burdens the server.
  • Heavy joins on every request: Constant $lookup use signals that related data belongs together.
  • Unneeded indexes: Every extra index slows writes.

Summary

Schema patterns turn common design problems into repeatable solutions. Extended Reference removes small joins, Subset keeps documents lean, Bucket groups streams of data, Computed stores results ready for reading, and Attribute handles varied fields with one index. Choose a pattern by studying how the application reads and writes data, and avoid unbounded arrays that grow without limit.