Skip to main content

How to design an effective MongoDB document schema

Design a MongoDB schema from access patterns, choosing embedding for bounded co-accessed data and referencing for unbounded or shared data, then add indexes and JSON Schema validation.

Difficulty
Intermediate
Duration
45 minutes
Steps
6

What and why

MongoDB has no fixed schema, so the model is driven by how the application reads and writes data, not by normalization rules. Good design embeds data that is read together and references data that grows unbounded or is shared. This tutorial walks through the key decisions and how to enforce them.

Prerequisites

  • A MongoDB instance reachable via mongosh or a driver.
  • A clear picture of your most frequent queries.
  • Basic knowledge of BSON documents.

Steps

1. Map your access patterns

List the top read and write operations and how often each runs. Design for the queries you make most, since MongoDB rewards data locality.

2. Decide embed vs reference

Embed when data is accessed together, has a bounded size, and changes with the parent. Reference when sub-documents grow without limit, are large, or are shared across parents. Watch the 16 MB document limit.

3. Model one-to-many relationships

For a small, bounded list (an order's line items), embed:

{
  _id: ObjectId(),
  customer: "Ada",
  items: [ { sku: "A1", qty: 2 }, { sku: "B7", qty: 1 } ]
}

For an unbounded list (a user's events), store events in their own collection with a userId field.

4. Handle many-to-many

Keep two collections and store an array of references on the side queried most. For products and tags, an array of tag ids on the product is usually enough:

{ _id: ObjectId(), name: "Widget", tagIds: [ObjectId(), ObjectId()] }

5. Add indexes

Index the fields used in filters and sorts:

db.events.createIndex({ userId: 1, createdAt: -1 });

Use compound indexes ordered to match equality-then-range queries.

6. Enforce schema validation

Add a JSON Schema validator so documents stay consistent:

db.createCollection("users", {
  validator: { $jsonSchema: {
    bsonType: "object",
    required: ["email"],
    properties: { email: { bsonType: "string" } }
  } }
});

Verification

Run your top queries with .explain("executionStats") and confirm they use indexes (IXSCAN, not COLLSCAN). Insert an invalid document and confirm validation rejects it.

Next Steps

Review the schema as access patterns change, use the bucket pattern for high-volume time-series data, and add a shard key aligned to your query distribution before scaling out.

Prerequisites

  • A running MongoDB instance
  • Basic JSON and query knowledge
  • Understanding of your read/write patterns

Steps

  • 1
    Map your access patterns
  • 2
    Decide embed vs reference
  • 3
    Model one-to-many relationships
  • 4
    Handle many-to-many
  • 5
    Add indexes
  • 6
    Enforce schema validation

Category

Database