Data modeling best practices in Firebase - Time & Space Complexity
When designing data models in Firebase, it's important to understand how the number of operations grows as your data grows.
We want to know how the structure of data affects the speed and cost of reading and writing.
Analyze the time complexity of reading user posts stored in a flat vs nested structure.
// Flat structure example
const postsRef = firebase.database().ref('posts');
postsRef.orderByChild('userId').equalTo(userId).once('value').then(snapshot => {
// process posts
});
// Nested structure example
const userPostsRef = firebase.database().ref(`user-posts/${userId}`);
userPostsRef.once('value').then(snapshot => {
// process posts
});
This code reads posts for a user either by querying a flat list or by reading a nested list under the user.
Look at what happens repeatedly when data grows.
- Primary operation: Reading posts from the database.
- How many times: Number of posts for the user (n).
As the number of posts grows, the time to read them changes depending on the data model.
| Input Size (n) | Approx. Api Calls/Operations |
|---|---|
| 10 | 10 reads in flat query, 1 read in nested |
| 100 | 100 reads in flat query, 1 read in nested |
| 1000 | 1000 reads in flat query, 1 read in nested |
Pattern observation: Flat queries require more operations as data grows; nested reads stay constant.
Time Complexity: O(n) for flat queries, O(1) for nested reads
This means reading data from a flat list grows linearly with the number of items, but reading from a nested path stays constant.
[X] Wrong: "Storing all data in one big list is always simpler and faster."
[OK] Correct: As data grows, queries on big lists become slower and cost more, unlike nested data which can be accessed directly.
Understanding how data structure affects operation cost shows you can design efficient, scalable apps in Firebase.
"What if we indexed the flat list by userId? How would that change the time complexity of reading posts?"
Practice
Solution
Step 1: Understand Firestore structure
Firestore organizes data into collections and documents to keep data structured and easy to access.Step 2: Benefits of collections and documents
This structure allows efficient queries and scaling as your app grows.Final Answer:
To organize data in a way that is easy to query and scale -> Option DQuick Check:
Firestore uses collections/documents for scalable data [OK]
- Thinking all data should be in one JSON object
- Ignoring the need for security rules
- Assuming data is read-only by default
Solution
Step 1: Recall Firestore syntax
Firestore usescollection()to select a collection anddoc()to select a document.Step 2: Check each option
db.collection('users').doc('user123') correctly usescollection('users').doc('user123'). Others do not follow Firestore syntax.Final Answer:
db.collection('users').doc('user123') -> Option BQuick Check:
Firestore document reference uses collection().doc() [OK]
- Using incorrect method names like get() or document()
- Swapping collection and document order
- Trying to access documents directly from db without collection
users (collection)
user123 (document)
name: 'Alice'
posts (collection)
post1 (document)
title: 'Hello'What will
db.collection('users').doc('user123').collection('posts').get() return?Solution
Step 1: Understand nested collections
In Firestore, collections can be nested under documents. Here, posts is a subcollection under user123.Step 2: What does the query do?
The query gets all documents in the posts collection under user123, so it returns all posts documents.Final Answer:
A list of all posts documents under user123 -> Option AQuick Check:
Subcollections return their documents on get() [OK]
- Thinking subcollections are fields inside documents
- Expecting get() to return parent document data
- Assuming get() returns all users and posts together
match /users/{userId} {
allow read, write: if request.auth.uid == userId;
}What is the main issue if you want users to read their posts stored in a subcollection
posts under their user document?Solution
Step 1: Analyze the rule scope
The rule matches only the /users/{userId} document, not any subcollections like posts.Step 2: Effect on subcollections
Without explicit rules for /users/{userId}/posts/{postId}, access to posts is denied by default.Final Answer:
The rule does not cover the posts subcollection, so access is denied -> Option AQuick Check:
Security rules must cover subcollections explicitly [OK]
- Assuming parent document rules apply to subcollections
- Believing rules allow all reads by default
- Confusing read and write permissions
Solution
Step 1: Consider scalability needs
Storing many messages inside one document or array can cause size limits and slow queries.Step 2: Use subcollections for many related items
Creating a messages subcollection under each user document allows efficient queries and scales well.Final Answer:
Create a messages subcollection under each user document -> Option CQuick Check:
Use subcollections for large, related data sets [OK]
- Putting many items in arrays inside documents
- Using one big collection without filtering
- Storing all data in one document causing size errors
Ready to prove it?
Pass the test and this pattern moves unknown
