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:
- Examine the relationship between
listingsandreviews. Is it a reference or an embedded relationship? - Find a specific listing by its
idand then find all its reviews in thereviewscollection (using thelisting_idfield) - Count how many reviews the listing with the most reviews has (hint: use
number_of_reviewsof the most popular listing) - What advantages are there to having reviews in a separate collection instead of embedded in the listing?
Pro:
- 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)
- What advantages and disadvantages would this hybrid model have compared to the current one?
- 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
This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.
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 bookWill you buy me a coffee?
This is how I keep writing without ads or paywalls.
Sure, it's on me!
Comments
There are no comments yet.