10. Data modeling

MongoDB is schemaless, but that does not mean you shouldn't think about your data model.

Differences from SQL

In SQL, you define columns at the table level. In MongoDB, you define fields at the document level. This means each document can have different fields.

// This is valid in MongoDB
db.users.insertMany([
    { name: 'Ana', age: 28 },
    { name: 'Carlos', age: 35, city: 'Madrid' },
    { name: 'Laura', email: 'laura@example.com' }
])

References vs Embedded documents

You have two main ways to relate data:

References (similar to foreign keys)

// employees collection
{
    _id: 698,
    name: 'Duncan',
    manager: 485  // Reference to the manager
}

// To get the manager:
var employee = db.employees.findOne({ _id: 698 })
var manager = db.employees.findOne({ _id: employee.manager })

Embedded documents

{
    _id: 501,
    name: 'Ghanima',
    family: {
        mother: 'Chani',
        father: 'Paul',
        brother: 485
    }
}

// Searching by embedded fields uses dot notation
db.employees.find({ 'family.mother': 'Chani' })

When to use each one?

Use references when:

  • The data is used in different contexts
  • The data changes frequently
  • You need to avoid duplication
  • The documents are very large

Use embedded documents when:

  • The data is almost always queried together
  • The data doesn't change much
  • You want better performance (a single query)
  • The documents are small

Arrays of values

MongoDB handles arrays as first-class citizens:

{
    name: 'Ana',
    hobbies: ['reading', 'programming', 'music']
}

// Find users that have a specific hobby
db.users.find({ hobbies: 'programming' })

Arrays can also contain documents:

{
    name: 'Ana',
    addresses: [
        { type: 'home', city: 'Madrid', street: 'Gran Vía 1' },
        { type: 'work', city: 'Madrid', street: 'Alcalá 42' }
    ]
}

// Search by fields within the array
db.users.find({ 'addresses.city': 'Madrid' })

Denormalization

Unlike SQL where you avoid duplicating data, in MongoDB sometimes duplicating data is the best option:

// Instead of just the user_id
{
    post: 'My first post',
    user_id: 123
}

// You can also store the name
{
    post: 'My first post',
    user: {
        id: 123,
        name: 'Ana'
    }
}

Advantages:

  • A single query to get everything
  • Better performance
  • The data is read together

Disadvantages:

  • If Ana changes her name, you must update multiple documents
  • More storage space

Size limit

Each document has a 16 MB limit. If you need to store large files, use GridFS.

Practical example: blog

For a blog, you could model:

Option 1: Comments in a separate collection

// Posts
{ _id: 1, title: 'My post', content: '...' }

// Comments
{ post_id: 1, author: 'Ana', text: 'Great post' }

Option 2: Embedded comments

{
    _id: 1,
    title: 'My post',
    content: '...',
    comments: [
        { author: 'Ana', text: 'Great post' },
        { author: 'Carlos', text: 'Interesting' }
    ]
}

Option 3: Hybrid (recommended)

{
    _id: 1,
    title: 'My post',
    content: '...',
    // First comments embedded
    recent_comments: [
        { author: 'Ana', text: 'Great post' },
        { author: 'Carlos', text: 'Interesting' }
    ],
    total_comments: 152
}

// The rest in a separate collection

This gives you performance (first comments in one query) and scalability (if there are many comments).

Activity 1

Analyze how the Airbnb data is related:

  1. Examine the relationship between listings and reviews. Is it a reference or an embedded relationship?
  2. Find a specific listing by its id and then find all its reviews in the reviews collection (using the listing_id field)
  3. Count how many reviews the listing with the most reviews has (hint: use number_of_reviews of the most popular listing)
  4. What advantages are there to having reviews in a separate collection instead of embedded in the listing?

Pro:

  1. Propose an alternative model where the last 10 reviews are embedded in the listing document and the rest are in a separate collection (just describe the design, don't implement it)
  2. What advantages and disadvantages would this hybrid model have compared to the current one?
  3. Find a listing with many reviews. Then get its 5 most recent reviews using two separate queries: one for the listing and another for the reviews sorted by date

Solutions

This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.

Desafíos de programación atemporales y multiparadigmáticos

Desafíos de programación atemporales y multiparadigmáticos

Te encuentras ante un librillo de actividades, divididas en 2 niveles de dificultad. Te enfrentarás a los casos más comunes que te puedes encontrar en pruebas técnicas o aprender conceptos elementales de programación.

Buy the book

Will you buy me a coffee?

This is how I keep writing without ads or paywalls.

Comments

There are no comments yet.