Unit 3: Schema Design and Data Modeling

CSE494 — Intelligent Nosql Databases 9 min read

I. Foundations of MongoDB Data Modeling

MongoDB is a document-oriented NoSQL database in which data is stored as BSON documents inside collections. Unlike relational modeling, which usually begins with normalized tables and joins, MongoDB modeling begins with application access patterns: what data is read together, updated together, and expected to grow together.

Defining characteristics:

  • Document model: A document contains field-value pairs and may include arrays, nested documents, dates, object identifiers, and other BSON types.
  • Flexible structure: Documents in one collection may have different fields or data types unless validation rules restrict them.
  • Aggregate orientation: Related information can be stored as one self-contained document, making the document an application-level aggregate.
  • Access-pattern design: A schema is shaped around frequent queries, writes, updates, and transactions rather than abstract normalization alone.
  • Controlled denormalization: Data may be duplicated to reduce joins and improve read performance, provided synchronization costs remain manageable.
  • Atomicity boundary: A write affecting one document is atomic. Multi-document transactions are available, but good modeling minimizes unnecessary dependence on them.
  • Practical limits: A BSON document has a maximum size of 16 MiB, so indefinitely growing arrays or embedded histories require alternative designs.
  • Relationship choices: Associations are represented primarily through embedding related data or referencing another document by its identifier.

II. Flexible Document Structure

A. Schema-less Nature of MongoDB

MongoDB is described as schema-less because a collection does not require every document to have an identical predefined structure, although applications still need an intentional logical schema.

  • Structural flexibility: Documents can contain different fields according to their subtype or lifecycle stage. For example, one products document may contain screenSize, while another contains fabric.
  • Incremental evolution: A new field such as loyaltyTier can be added to new customer documents without immediately rewriting every existing document.
  • Polymorphic data: Different but related entities can share a collection when common fields support shared queries.
JAVASCRIPT
{ _id: 1, type: "book", title: "Database Systems", pages: 640 }
{ _id: 2, type: "video", title: "MongoDB Basics", durationSec: 900 }
  • Application responsibility: Application code must safely handle missing fields, optional values, and multiple schema versions.
  • Not structure-free: Field names, BSON types, indexes, validation rules, and document relationships collectively form an implicit or explicit schema.
  • Risk of inconsistency: Accidental variations such as phoneNumber, phone_number, and phone complicate querying and indexing.
  • Design implication: Flexibility is most valuable for evolving or heterogeneous data; it should not replace governance where stable business rules exist.

III. Relationship Representation

A. Embedding vs. Referencing

Embedding stores related data inside a parent document, whereas referencing stores separate documents connected through identifiers.

  1. Embedding

    • Locality: Data retrieved together is placed together, allowing one query to return the complete aggregate.
    • Atomic updates: Changes to embedded fields can participate in the parent document’s single-document atomic write.
    • Best conditions: Embed when the child belongs exclusively to the parent, remains bounded in size, and is normally accessed with it.
    • Example: An order can embed the purchased item’s name, quantity, and price because these values form an historical snapshot.
  2. Referencing

    • Independent identity: Related entities remain in separate collections and are connected using fields such as customerId.
    • Best conditions: Reference when data is shared, updated independently, very large, or capable of unbounded growth.
    • Retrieval cost: Applications may issue additional queries or use $lookup, potentially increasing latency and complexity.
    • Consistency benefit: A shared entity has one authoritative document, reducing duplicate updates.
  • Decision test: Model according to cardinality, ownership, update frequency, data size, and dominant query patterns.
  • Hybrid approach: A document may reference the source entity while embedding frequently displayed snapshot fields.

IV. Singular Associations

A. One-to-One Relationships

A one-to-one relationship associates one document with at most one corresponding document, such as one user and one profile.

  • Embedded representation: Store the dependent object inside its owner when both are usually read together and share a lifecycle.
JAVASCRIPT
{
  _id: ObjectId("64f000000000000000000001"),
  email: "mina@example.com",
  profile: { displayName: "Mina", timezone: "UTC+5:30" }
}
  • Referenced representation: Store users and profiles separately when the profile is large, sensitive, rarely accessed, or governed by different permissions.
  • Relationship direction: The reference should normally be stored on the side from which navigation occurs most often, for example profile.userId.
  • Uniqueness constraint: A unique index on the referencing field enforces that no two profiles belong to the same user.
JAVASCRIPT
db.profiles.createIndex({ userId: 1 }, { unique: true })
  • Lifecycle consideration: Embedded profile data is removed automatically with its user document; referenced data requires explicit deletion or archival handling.
  • Trade-off: Separation supports independent access and security, while embedding reduces reads and keeps related updates atomic.

V. Parent-Child Associations

A. One-to-Many Relationships

A one-to-many relationship connects one parent to multiple children, and its representation depends mainly on the number and growth of those children.

  1. Bounded one-to-few

    • Embedding: A customer’s small set of delivery addresses can be stored in an addresses array.
    • Advantage: Reading the customer returns all addresses without a join.
    • Condition: The array must remain predictably small and normally be accessed with the parent.
  2. Large or unbounded one-to-many

    • Referencing: Orders should usually be separate documents containing customerId, because a customer may create orders indefinitely.
    • Indexing: An index such as { customerId: 1, createdAt: -1 } supports recent-order queries for one customer.
    • Pagination: Separate child documents permit sorting and limiting without loading the entire relationship.
  • Avoid unbounded arrays: Continually appending logs, events, or messages enlarges the parent document and can approach the 16 MiB limit.
  • Reference placement: Storing the parent identifier in each child generally scales better than maintaining an ever-growing parent array of child identifiers.
  • Deletion policy: MongoDB does not automatically cascade deletes; applications must define whether children are deleted, retained, or marked orphaned.

VI. Networked Associations

A. Many-to-Many Relationships

A many-to-many relationship exists when each entity may be associated with multiple entities of another type, such as students and courses.

  • Small bounded sets: Each document may store an array of related identifiers when membership is limited and updates are manageable.
  • Intermediate collection: A junction-style collection is preferable when the relationship is large or has attributes of its own.
JAVASCRIPT
{
  _id: ObjectId("64f000000000000000000010"),
  studentId: ObjectId("64f000000000000000000011"),
  courseId: ObjectId("64f000000000000000000012"),
  enrolledAt: ISODate("2025-01-10T00:00:00Z"),
  status: "active"
}
  • Relationship attributes: Fields such as enrolledAt, status, role, or grade naturally belong to the association document.
  • Duplicate prevention: A compound unique index on { studentId: 1, courseId: 1 } prevents duplicate enrollment.
  • Bidirectional queries: Additional indexes may support finding all courses for a student and all students in a course.
  • Denormalized identifiers: Storing arrays on both entities speeds reads but requires synchronized updates and can leave inconsistent links.
  • Query mechanism: $lookup can combine referenced collections, but frequent large joins may indicate that the model does not match the application’s access patterns.

VII. Design Principles

A. Data Modeling Best Practices

Effective MongoDB models balance query performance, write behavior, consistency, and future growth.

  • Start with workloads: List high-frequency operations, required filters, sort orders, projected fields, and latency expectations before defining documents.
  • Store together what is accessed together: Co-locate data used in the same request when size and update patterns permit it.
  • Define aggregate boundaries: Keep values requiring atomic consistency in one document whenever practical.
  • Control duplication: Duplicate stable display data, such as an order’s product name, only when faster reads justify synchronization or snapshot semantics.
  • Plan for growth: Estimate array cardinality, document size, write rate, and retention period; move unbounded histories to separate collections.
  • Use appropriate BSON types: Store dates as BSON dates, numeric values as numeric types, and identifiers consistently as ObjectId or another chosen type.
  • Design indexes with queries: Compound index field order should reflect equality filters, sort operations, and range predicates.
  • Limit indexes: Every index consumes storage and adds work to inserts and updates.
  • Use schema versioning: A field such as schemaVersion: 2 helps applications migrate or interpret evolving document formats.
  • Measure decisions: Use explain() and production-like datasets to confirm index use, documents examined, and execution behavior.

VIII. Applied Modeling

A. Case Studies and Examples

An e-commerce order system demonstrates how embedding and referencing can be combined according to ownership and historical requirements.

  • Customer reference: The order stores customerId because the customer exists independently and may place many orders.
  • Embedded line items: Each purchased item contains productId, name, unitPrice, and quantity.
    • productId links to the current catalog product.
    • name and unitPrice preserve what was displayed and charged when the order was created.
  • Embedded shipping address: The address is copied into the order because later customer address changes must not alter historical delivery information.
  • Calculated total: total may be stored for rapid retrieval, but the application must calculate it from trusted line-item values during order creation.
  • Separate payment records: Payment attempts may be stored separately because they have independent security controls, statuses, and potentially repeated attempts.
  • Separate shipment events: Tracking events form an unbounded sequence and therefore should not grow indefinitely inside the order document.
  • Atomic transition: Updating an order’s status and its embedded summary fields in one operation preserves consistency within the aggregate.
  • Resulting model: The case uses references for independently managed entities, embedding for bounded owned data, and snapshot duplication for historical accuracy.

IX. Data Integrity

A. Validation Rules and Constraints

MongoDB supports collection validators that reject or warn about documents violating declared structural and value requirements.

  • JSON Schema validation: The $jsonSchema operator can require fields, restrict BSON types, validate arrays, and constrain values.
  • Concrete rule: An order can require a customer identifier, at least one item, and a recognized status.
JAVASCRIPT
db.createCollection("orders", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["customerId", "items", "status"],
      properties: {
        customerId: { bsonType: "objectId" },
        items: {
          bsonType: "array",
          minItems: 1,
          items: {
            bsonType: "object",
            required: ["productId", "quantity", "unitPrice"]
          }
        },
        status: {
          enum: ["pending", "paid", "shipped", "cancelled"]
        }
      }
    }
  },
  validationLevel: "strict",
  validationAction: "error"
})
  • Validation level: strict applies validation to all inserts and relevant updates; moderate provides a less disruptive path for collections containing older invalid documents.
  • Validation action: error rejects invalid writes, while warn records violations without rejecting them.
  • Index constraints: Unique indexes enforce uniqueness for keys such as usernames or relationship pairs; partial unique indexes can apply only to qualifying documents.
  • Application validation: Business rules involving authorization, external systems, or complex cross-document conditions still belong in application or service logic.
  • Referential integrity: MongoDB does not automatically enforce foreign keys, so applications or transactions must prevent missing references and handle deletions.
  • Defense in depth: Database validation protects all write paths, while application validation supplies clearer user-facing errors and domain-specific checks.