Skip to main content

Command Palette

Search for a command to run...

MongoDB Data Modeling

Updated
โ€ขView as Markdown

Steps to take

  1. Identify workload

  2. Map relationship

  3. Apply design patterns

  4. Optimization - creating indexes

Identify workload

At this step, we mostly need to identify if our application is read or write-heavy and which resources are critical.

Example

ActionTypeInformationFrequencyPriority
Submit a new articleWriteauthor, text10 per dayHigh
Submit a comment on an articleWriteuser, text1,000 per day (100 per article)Medium
View an articleReadarticle id, text, comments1,000,000 per dayHigh
View article analyticsReadarticle id, comments, clicks10 per hourLow

Data relationship

Referencing or Embedding

Example of referencing

// Referencing
// books
{
  "_id": "book1",
  "title": "MongoDB",
  "authors": ["author1", "author2"]
}

// authors
[
  { "author_id": "author1", "name": "Huy" },
  { "author_id": "author2", "name": "Steve" }
]

Example of embedding

{
  "_id": "book1",
  "title": "MongoDB",
  "authors": [
    { "author_id": "author1", "name": "Huy" },
    { "author_id": "author2", "name": "Steve" }
  ]
}

When to use which?

In MongoDB, the recommended way to handle related data is to embed it into a sub-document - retrieve information without $lookup. The other way is to use references.

๐Ÿ‘‰ The golden rule: Data that is accessed together should be stored together

Letโ€™s take a look at the above books and authors relationship

Here are the guidelines

  • Simplicity - would keeping the pieces together lead to a simpler data model and code? => Yes - Embed

  • Go Together - Do the pieces of information have a โ€œhas-aโ€, โ€œcontainsโ€ relationship? Yes - Embed

  • Query Automicity - Does the app query the pieces of info together? Yes - Embed

  • Update Complexity - Are the pieces of info updated together? No - Reference

    • We can update the book and author separately
  • Archival - should the pieces of information be archived at the same time? No - Reference

  • Cardinality * - is there a high growth in the child side of the relationship? No - Embed

  • Data Duplication - Would data duplication be too complicated to manage and undesired? No - Embed

  • Document Size - Would the combined size take too much memory or transfer bandwidth? No - Embed

  • Document Growth - would the embedded piece grow without bound? No - Embed

  • Workload - are the pieces of info written at different times in a write-heavy workload? No - Embed

    • If we frequently update related data, then better to use a reference
  • Individuality - for the child side, can the pieces exist by themselves without a parent? No - Embed

    • An author cannot exist in our app without a book

๐Ÿ‘‰ But mostly consider

  • Array size

  • Update frequency of embedded data

  • Whether the embedded entity is often queried independently

One-to-one

Example: one company has one headquarters

Embedded - recommended in this case โœ…

{
  "_id": "publisher123",
  "name": "MongoDB Press",
  "street": "123 Data Lane",
  "city": "San Francisco",
  "state": "CA",
  "country": "USA",
  "zip": "94107"
}

// it's better to group - nested object
{
  "_id": "publisher123",
  "name": "MongoDB Press",
  "headquarters": {
    "street": "123 Data Lane",
    "city": "San Francisco",
    "state": "CA",
    "country": "USA",
    "zip": "94107"
  }
}

Reference

Option 1: Array of references within the parent document

โœ… Use when - the app primarily reads publisher documents

// publishers
{
  "_id": "publisher123",
  "name": "MongoDB Press",
  "headquarter_id": "hq1"
}

// headquarters
{
  "_id": "hq1",
  "street": "123 Data Lane",
  "city": "San Francisco",
  "state": "CA",
  "country": "USA",
  "zip": "94107"
}

Option 2: a child document to reference the parent document

โœ… Use when - the app primarily reads headquarters documents

// publishers
{
  "_id": "publisher123",
  "name": "MongoDB Press"
}

// headquarters
{
  "_id": "hq1",
  "publisher_id": "publisher123",
  "street": "123 Data Lane",
  "city": "San Francisco",
  "state": "CA",
  "country": "USA",
  "zip": "94107"
}

One-to-Many

Example: one printed book has many reviews

Embedded

Good when

  • No duplication of information - i.e, in this case, review can be attached to one book only

  • The option on many sides cannot exist by itself - i.e, review cannot exist without the printed book

Bad when

  • Children are an unbounded array => it can make the document super large

Option 1: As an array

{
  "_id": "book0456",
  "title": "Practical MongoDB Aggregations",
  "author": "Paul Done",
  "reviews": [
    {
      "user_id": "user0956",
      "title": "Lorem Ipsum",
      "review_text": "dolor sit amet...",
      "rating": 3
    },
    {
      "user_id": "user0345",
      "title": "Ut enim",
      "review_text": "ad minim veniam...",
      "rating": 4
    }
  ]
}

Option 2: As an object

{
  "_id": "book0456",
  "title": "Practical MongoDB Aggregations",
  "author": "Paul Done",
  "reviews": {
    "user0956": {
      "title": "Lorem Ipsum",
      "review_text": "dolor sit amet...",
      "rating": 3
    },
    "user0345": {
      "title": "Ut enim",
      "review_text": "ad minim veniam...",
      "rating": 4
    }
  }
}

Reference

Option 1: Array of references to child documents in parent document

Use when: The list of children is small and often accessed together with the parent

// books
{
  "_id": "book0456",
  "title": "Practical MongoDB Aggregations",
  "author": "Paul Done",
  "reviews": [
    "review001",
    "review002"
  ]
}

// reviews
[
  {
    "_id": "review001",
    "user_id": "user0956",
    "title": "Lorem Ipsum",
    "review_text": "dolor sit amet...",
    "rating": 3
  },
  {
    "_id": "review002",
    "user_id": "user0345",
    "title": "Ut enim",
    "review_text": "ad minim veniam...",
    "rating": 4
  }
]

Option 2: a child document to reference the parent document - recommended in this case due to an unbounded array โœ…

Use when:

  • The number of children is large or grows unbounded.

  • You rarely need all the children at once.

// books
{
  "_id": "book0456",
  "title": "Practical MongoDB Aggregations",
  "author": "Paul Done"
}

// reviews
[
  {
    "_id": "review001",
    "book_id": "book0456",
    "user_id": "user0956",
    "title": "Lorem Ipsum",
    "review_text": "dolor sit amet...",
    "rating": 3
  },
  {
    "_id": "review002",
    "book_id": "book0456",
    "user_id": "user0345",
    "title": "Ut enim",
    "review_text": "ad minim veniam...",
    "rating": 4
  }
]

Many-to-many

Example: a book has many authors, and an author can write many books

Embedded - recommended in this case, check the โ€œWhen to use whichโ€ section above โœ…

Notice that here we have data duplication. However, itโ€™s not always an issue

Option 1: As an array

{
  "_id": "book0008",
  "title": "Mastering MongoDB 7.0",
  "authors": [
    {
      "author_id": "author0021",
      "name": "Elie Hannouch"
    },
    {
      "author_id": "author0087",
      "name": "Malak Abu Hammad"
    },
    {
      ๐Ÿ‘‰ "author_id": "author0046",
      "name": "Rachelle Palmer"
    }
  ]
}

// another book
{
  "_id": "book0017",
  "title": "Building Rich and Resilient Applications with MongoDB",
  "authors": [
    {
      ๐Ÿ‘‰ "author_id": "author0046",
      "name": "Rachelle Palmer"
    }
  ]
}

Option 2: As an object

{
  "_id": "book0008",
  "title": "Mastering MongoDB 7.0",
  "authors": {
    "author0021": {
      "name": "Elie Hannouch"
    },
    "author0087": {
      "name": "Malak Abu Hammad"
    },
    "author0046": {
      "name": "Rachelle Palmer"
    }
  }
}

Reference

Option 1: Array of references to child documents in parent document

// books
[
  {
    "_id": "book0008",
    "title": "Mastering MongoDB 7.0",
    "authors": [
      "author0021",
      "author0087",
      ๐Ÿ‘‰ "author0046"
    ]
  },
  {
    "_id": "book0017",
    "title": "Building Rich and Resilient Applications with MongoDB",
    "authors": [
      ๐Ÿ‘‰ "author0046"
    ]
  }
]

// authors
[
  {
    "_id": "author0021",
    "name": "Elie Hannouch"
  },
  {
    "_id": "author0087",
    "name": "Malak Abu Hammad"
  },
  {
    "_id": "author0046",
    "name": "Rachelle Palmer"
  }
]

Option 2: a child document to reference the parent document

// books
[
  {
    "_id": "book0008",
    "title": "Mastering MongoDB 7.0"
  },
  {
    "_id": "book0017",
    "title": "Building Rich and Resilient Applications with MongoDB"
  }
]

// authors
[
  {
    "author_id": "author0021",
    "name": "Elie Hannouch",
    // not that unlike the one-to-many
    // this is an array
    ๐Ÿ‘‰ "books": ["book0008"]
  },
  {
    "author_id": "author0087",
    "name": "Malak Abu Hammad",
    ๐Ÿ‘‰ "books": ["book0008"]
  },
  {
    "author_id": "author0046",
    "name": "Rachelle Palmer",
    "books": ["book0008", "book0017"]
  }
]

Resources