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
| Pattern | Problem It Solves |
|---|---|
| Extended Reference | Too many joins for small, common details |
| Subset | Documents grow too large for memory |
| Bucket | Millions of tiny documents from streams of data |
| Computed | Repeated calculations slow every read |
| Attribute | Many 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 requestBucket 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 When | Choose Referencing When |
|---|---|
| Applications read the data together | Applications read the data separately |
| The embedded part stays small | The related part grows without limit |
| The embedded part rarely changes alone | Many 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
$lookupuse 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.
