Schema Design: Bucket & Extended Reference Patterns
Advanced Schema Design Patterns
Two patterns are especially common in production MongoDB applications: the Bucket pattern for time-series data and the Extended Reference pattern for avoiding costly $lookup stages on hot read paths.
Bucket Pattern
Instead of one document per measurement (millions of tiny documents), group measurements into time buckets. This dramatically reduces document count and improves aggregation speed.
// Without bucket: one doc per reading (bad at scale)
{ sensorId: "s1", temp: 22.4, ts: ISODate("2024-01-01T00:00:00Z") }
{ sensorId: "s1", temp: 22.7, ts: ISODate("2024-01-01T00:01:00Z") }
// ... 1440 docs per sensor per day
// With bucket: one doc per sensor per hour
{
sensorId: "s1",
hour: ISODate("2024-01-01T00:00:00Z"),
readings: [
{ ts: ISODate("2024-01-01T00:00:00Z"), temp: 22.4 },
{ ts: ISODate("2024-01-01T00:01:00Z"), temp: 22.7 }
// up to 60 readings per bucket
],
count: 2,
minTemp: 22.4,
maxTemp: 22.7
}Extended Reference Pattern
Copy the most-read fields from a referenced document directly into the referencing document. This eliminates $lookup on every read at the cost of some duplication — acceptable when the copied fields rarely change.
// Order document embeds key customer fields
// instead of only storing customerId
{
_id: ObjectId("..."),
items: [{ sku: "M001", qty: 2, price: 499 }],
total: 998,
// Extended reference: copy stable customer fields
customer: {
_id: ObjectId("..."), // still reference the source
name: "Aditi Sharma",
email: "aditi@example.com"
}
}
// If customer name changes: run a background job to sync
db.orders.updateMany(
{ "customer._id": changedCustomerId },
{ $set: { "customer.name": newName } }
);Apply the Extended Reference pattern only to fields that are read far more often than they change — names, SKU codes, profile pictures. Never embed fields like balance or inventory that change frequently.