MongoDB Views or Materialized Views: When to Use?
Intoduction MongoDB offers powerful tools to simplify data access and improve performance. Among these tools are Standard Views and Materialized Views, two approaches that allow you to shape how data is read and presented, each with its own trade-offs and use cases. In this article, we’ll explore the differences between the two and when to use each effectively. I created a small Node.js server to compare between them and guide you how to create and refresh both programmatically, Find it here. What Is a MongoDB Standard View? So, you're working with MongoDB and hear about these "view" things. Are they the same? Not quite. A MongoDB Standard View (or MongoDB View) is essentially a saved query (aggregation pipeline). It acts like a virtual collection. When you query the view, MongoDB runs the saved query against the real collections at that moment. It's logical: Doesn't store any data itself, just the definition. It's read-only: You can't write data to a view. It uses aggregation pipelines: This is how you define what the view shows. It's metadata: The view definition is stored, not the results. So, zero extra storage footprint for the data itself. Basically, it's a shortcut; instead of writing a very long, complex aggregation pipeline over and over, you save this definition as a virtual collection for later use. What is a Materialized View? Unlike standard views, a materialized view actually stores data. It's basically a regular collection that holds the pre-computed results of an aggregation query. Guess what? MongoDB doesn't natively support them like some SQL databases do (not yet, anyway). When we talk about materialized views in MongoDB, we mean a pattern you implement yourself, another collection (the materialized view) that holds pre-computed results. Stores results physically: You run an aggregation query and save its output into a real, separate collection. Precomputed: The results are already calculated and stored. Simulates denormalization: Kinda like creating a purpose-built table holding exactly the data you need for fast reads. So, you're trading real-time data freshness for speed, because reading from this pre-built collection is way faster than running a complex aggregation every time. MongoDB Views Vs Materialized Views Well, let's compare them based on some criteria. Data Freshness Views: Always show the latest data because they run the query live. What you see is what's in the base collection right now. Materialized Views: Only as fresh as the last refresh. If you refresh it once a day, the data could be up to 24 hours old. Performance Implications Views: Read performance depends entirely on the underlying query and collections. Complex aggregations on large datasets? Will be slow. Simple filter? Probably fast. Materialized Views: Reads are typically super fast because it's just querying a regular collection with precomputed data. Writes, though? You add overhead because you need to run the aggregation and write the results periodically (the refresh). Storage Considerations Views: Tiny footprint (neglicable). Just stores the query definition. Materialized Views: Can use significant storage, because you're duplicating data (the results of the query). The size depends on the output of your aggregation. Maintenance Overhead Views: Almost none. Define it once, use it. Maybe update the definition if your needs change. Materialized Views: You have to build and manage the refresh process. How often? How to handle failures? What if the source data changes structure? It's definitely more work. Native Support in MongoDB Views: Fully supported feature. Materialized Views: Not supported at this time. It's a pattern. You use standard MongoDB features like aggregation ($out or $merge) and maybe schedulers or Change Streams to build it. Security & Abstraction Capabilities Views: Great for this. You can grant read access only to the view, effectively hiding fields or documents from users who shouldn't see them. Materialized Views: It's just another collection. You need to manage its security access separately, just like any other collection. When To Use Views vs Materialized Views? Why bother with regular views then? Query Abstraction: Hide complex aggregation logic behind a simple view name. Makes life easier for application devs who just want to query order_analytics_view instead of writing a complex pipeline. Data Security: You can create views that only show certain fields or documents to specific users or roles. Kinda like a security layer without needing complex application logic. Like, maybe show user_basic_info_view to regular staff, but the full users collection only to admins. On the other hand, materialized views come into play when performance is key, especially for reads. Speeding up Reads: If you have dashboards or analytics hitting the database with

Intoduction
MongoDB offers powerful tools to simplify data access and improve performance. Among these tools are Standard Views and Materialized Views, two approaches that allow you to shape how data is read and presented, each with its own trade-offs and use cases.
In this article, we’ll explore the differences between the two and when to use each effectively.
I created a small Node.js server to compare between them and guide you how to create and refresh both programmatically, Find it here.
What Is a MongoDB Standard View?
So, you're working with MongoDB and hear about these "view" things. Are they the same? Not quite.
A MongoDB Standard View (or MongoDB View) is essentially a saved query (aggregation pipeline). It acts like a virtual collection. When you query the view, MongoDB runs the saved query against the real collections at that moment.
- It's logical: Doesn't store any data itself, just the definition.
- It's read-only: You can't write data to a view.
- It uses aggregation pipelines: This is how you define what the view shows.
- It's metadata: The view definition is stored, not the results. So, zero extra storage footprint for the data itself.
Basically, it's a shortcut; instead of writing a very long, complex aggregation pipeline over and over, you save this definition as a virtual collection for later use.
What is a Materialized View?
Unlike standard views, a materialized view actually stores data. It's basically a regular collection that holds the pre-computed results of an aggregation query.
Guess what? MongoDB doesn't natively support them like some SQL databases do (not yet, anyway). When we talk about materialized views in MongoDB, we mean a pattern you implement yourself, another collection (the materialized view) that holds pre-computed results.
- Stores results physically: You run an aggregation query and save its output into a real, separate collection.
- Precomputed: The results are already calculated and stored.
- Simulates denormalization: Kinda like creating a purpose-built table holding exactly the data you need for fast reads.
So, you're trading real-time data freshness for speed, because reading from this pre-built collection is way faster than running a complex aggregation every time.
MongoDB Views Vs Materialized Views
Well, let's compare them based on some criteria.
Data Freshness
- Views: Always show the latest data because they run the query live. What you see is what's in the base collection right now.
- Materialized Views: Only as fresh as the last refresh. If you refresh it once a day, the data could be up to 24 hours old.
Performance Implications
- Views: Read performance depends entirely on the underlying query and collections. Complex aggregations on large datasets? Will be slow. Simple filter? Probably fast.
- Materialized Views: Reads are typically super fast because it's just querying a regular collection with precomputed data. Writes, though? You add overhead because you need to run the aggregation and write the results periodically (the refresh).
Storage Considerations
- Views: Tiny footprint (neglicable). Just stores the query definition.
- Materialized Views: Can use significant storage, because you're duplicating data (the results of the query). The size depends on the output of your aggregation.
Maintenance Overhead
- Views: Almost none. Define it once, use it. Maybe update the definition if your needs change.
- Materialized Views: You have to build and manage the refresh process. How often? How to handle failures? What if the source data changes structure? It's definitely more work.
Native Support in MongoDB
- Views: Fully supported feature.
- Materialized Views: Not supported at this time. It's a pattern. You use standard MongoDB features like aggregation (
$out
or$merge
) and maybe schedulers or Change Streams to build it.
Security & Abstraction Capabilities
- Views: Great for this. You can grant read access only to the view, effectively hiding fields or documents from users who shouldn't see them.
- Materialized Views: It's just another collection. You need to manage its security access separately, just like any other collection.
When To Use Views vs Materialized Views?
Why bother with regular views then?
- Query Abstraction: Hide complex aggregation logic behind a simple view name. Makes life easier for application devs who just want to query
order_analytics_view
instead of writing a complex pipeline. - Data Security: You can create views that only show certain fields or documents to specific users or roles. Kinda like a security layer without needing complex application logic. Like, maybe show
user_basic_info_view
to regular staff, but the fullusers
collection only to admins.
On the other hand, materialized views come into play when performance is key, especially for reads.
- Speeding up Reads: If you have dashboards or analytics hitting the database with the same heavy aggregations over and over, pre-calculating results in a materialized view makes those reads lightning fast.
- Expensive Aggregations: Some aggregations just take a long time and use lots of resources. Running them once and storing the result is way more efficient if the data doesn't need to be absolutely live.
- Denormalized Data: Perfect when you need data structured in a specific format, often combining data from multiple collections.
How to Implement a MongoDB View?
Alright, let's make a view. It's pretty straightforward.
Create a View Using MongoDB Shell
Say you have a users
collection and just want a view of the active ones.
// Connect to your database first
// Example: use my_database
db.createView(
"active_users_view", // The name of your new view
"users", // The source collection
[ // The aggregation pipeline
{ $match: { status: "active" } }
]
)
Now you can query it like a regular collection: db.active_users_view.find()
and you'll only get users with status: "active"
. MongoDB runs that $match
pipeline every time you query the view.
You could even add $group
stages or use cool pipeline stages like $facet
for multi-faceted results.
Create a View Using MongoDB Driver (With Node.js)
Say you want to create a script to create a view programmatically, you can do the following:
const client = await MongoClient.connect(process.env.MONGODB_URI);
const db = client.db(process.env.DATABASE_NAME);
await db.createCollection("active_users_view", {
viewOn: "users",
pipeline: [
{ $match: { status: "active" } }
]
});
The same functionality as before.
Updating or Modifying a View
MongoDB views are immutable. You can't alter an existing view's definition directly. What do you do?
- Drop and Recreate: The most common way.
db.active_users_view.drop();
// Now create it again with the new definition
db.createView("active_users_view", "users", [ /* new pipeline */ ]);
- Versioning: Create a new view with a version number (e.g.,
active_users_v2
) and update your application code to use the new one. You can drop the old one later.
How to Implement a MongoDB Materialized View?
MongoDB doesn't have a built-in CREATE MATERIALIZED VIEW
command like some SQL databases (not yet, anyway). So we have to implement it manually.
Keep in mind, when we talk about Materialized Views, the create operation is the same as the refresh operation. Because the refresh operation needs an existing collection, and if it is not there, it will create a new one. So they are not separate.
- Create/Refresh with Aggregation (
$out
)
This is the simplest. Run an aggregation pipeline and tell it to write the results to a new collection using the $out
stage.
db.orders.aggregate([
{
$match: { status: "completed" } // Only completed orders
},
{
$group: {
_id: "$customerId", // Group by customer
totalSpent: { $sum: "$amount" } // Sum their order amounts
}
},
{
$out: "customer_spending_mv" // Write results to this collection (materialized view)
}
]);
Now you have a customer_spending_mv
collection, and, you can query it like this db.customer_spending_mv.find()
Warning: $out
replaces the entire output collection every time it runs. If the aggregation fails mid-way, you might lose the collection temporarily. Good for infrequent updates or simple use.
- Create/Refresh with Aggregation (
$merge
)
Run an aggregation and merge the results into an existing collection. This is more flexible; you can update existing documents, insert new ones, or even fail the operation based on matching conditions. Better for incremental updates.
db.orders.aggregate([
{
$match: { status: "completed" } // Only completed orders
},
{
$group: {
_id: "$customerId", // Group by customer
totalSpent: { $sum: "$amount" } // Sum their order amounts
}
},
{
$merge: {
into: "customer_spending_mv", // The target collection
on: "_id", // Field to match documents on
whenMatched: "replace", // What to do if a document matches (update it)
whenNotMatched: "insert" // What to do if no document matches (insert it)
}
}
])
- Completely Manual Create/Refresh
We knew that the materialized view is just another collection, right? Then, we can write a custom script that queries the source data and updates the target materialized view collection. Makes sense? Gives you maximum control but requires more effort as it is your responsibility to keep both collections in sync.
On-Demand vs. Incremental Refresh Strategies
Since materialized views store data snapshots, you have to decide how to keep them updated.
- On-Demand Refresh: You update the view whenever you need to, maybe manually or via an API call. Simple, but data can get quite stale. Useful if updates are infrequent or data changes predictably.
- Scheduled Refresh (Batch): Use a scheduler (like
cron
or MongoDB Atlas Scheduled Triggers) to run your$out
or$merge
aggregation periodically (e.g., every hour, every night). This is common for reports. Data freshness depends on the schedule frequency. - Incremental Refresh (Near Real-Time): Use MongoDB Change Streams. These let you watch for changes (inserts, updates, deletes) in the source collection(s) in real-time. When a change happens, you trigger a small, targeted update to your materialized view collection. This keeps the view much more up-to-date but is more complex to set up and manage and it is available only on the replica-set setup.
Choosing the right strategy depends completely on how fresh your data needs to be and how much load the refresh process puts on your system.
Performance Benchmark: Views vs Materialized Views
Talking performance is nice, but seeing is believing, right? For that reason, I built a small app where I have the orders
collection that has tons of documents. And created a View and a Materialized View to measure the query performance of both. Let's see.
First, run the following commands in sequence:
npm run setup-db # setup the `sales` database
npm run seed-data # seed the `orders` collection
npm run create-standard-view # create the `order_analytics_view` view
npm run create-materialized-view # create the `order_analytics_materialized` materialized view
npm run start # run the Node.js server
Then, open your browser and hit this endpoint:
http://localhost:3000/api/analytics/compare
This request returns 100 documents for both the View, the Materialized View, and some performance metrics. And technically uses the same aggregation query under the hood.
Let's have a look at the performance metrics (in my environment):
"performance": {
"standardView": {
"queryTimeMs": 274,
"resultCount": 100
},
"materializedView": {
"queryTimeMs": 6,
"resultCount": 100,
"lastUpdated": "2025-04-20T06:19:40.416Z"
},
"comparison": {
"timeDifferenceMs": 268,
"percentageDifference": "97.81%"
}
}
If you noticed the huge gap in performance between both, the Materialized View is 97.81% faster than the Standard View, and that's expected.
Use Cases For Each Approach
So, when should you pick which?
MongoDB Views – Ideal Scenarios
- Role-Based Access: Show different data slices to different user roles without complex app logic. Expose
limited_user_profile_view
vs fullusers
collection. - Admin Dashboards (Light Filtering): Simple dashboards that need real-time data but don't involve massive aggregations.
- Rapid Prototyping: Quickly define a data shape without restructuring collections.
- Simplifying Application Queries: Hide complex joins (
$lookup
) or calculations behind a simple view name.
Materialized Views – Ideal Scenarios
- Analytics Dashboards: Dashboards needing fast responses on complex aggregations (e.g., sales summaries, user activity trends). Hourly or daily freshness is often fine.
- Heavy Reporting: Generating complex reports that would time out or overload the DB if run live. Run the aggregation overnight into a materialized view.
- Time-Series Aggregations: Summarizing large amounts of time-series data (e.g., daily sensor readings into monthly averages).
- Denormalization for Microservices: Create a specialized, denormalized collection (your materialized view) optimized for a specific microservice's read patterns, fed from other source collections/services.
Frequently Asked Questions (FAQ)
Q1: Can you create an index on a MongoDB View?
No, you cannot create indexes directly on a MongoDB View itself. Performance depends on the indexes present on the underlying source collection(s) that the view's aggregation pipeline uses.
Q2: Can you update data through a MongoDB View?
No, MongoDB Views are strictly read-only. You cannot perform insert, update, or delete operations through a view. You must modify the data in the base collection(s).
Q3: How often should I refresh a Materialized View in MongoDB?
It depends entirely on your application's tolerance for stale data versus the overhead of the refresh process. It could be every few minutes, hourly, daily, or even weekly. Analyze your specific needs. For near real-time, consider Change Streams, but that's much more complex.
Q4: Does using $out
or $merge
lock the source collection during materialized view refresh?
The aggregation pipeline reads from the source collection(s), which will acquire the necessary read locks or yield like any other aggregation. The $out
stage briefly acquires an exclusive lock on the destination collection while replacing it. $merge
acquires locks on the destination collection as it performs writes/updates. The impact on the source collection is primarily the read load of the aggregation itself.
Q5: Is there a size limit for MongoDB Views?
No, because views don't store data. However, the aggregation pipeline defining the view has limits (e.g., 100MB memory limit by default unless allowDiskUse: true
is specified, though this option isn't always usable in the first stage of a view definition). The performance will degrade significantly if the view's query processes massive amounts of data without good indexing on the base collection.
Conclusion
Okay. We covered a lot.
We learned that a Standard View is a saved query and a Materialized View is a saved collection.
And, as usual, everything is a trade-off:
- Standard Views = Simplicity + Real-time Data (but potentially slow reads)
- Materialized Views = Fast Reads (but Complexity + Stale Data + Storage Costs)
So, pick the tool that fits the job. Don't over-engineer if a simple view works, but don't shy away from materialized views when you really need that speed boost.
And keep in mind, you don't always have to choose just one. Sometimes, a mix is best.
Think about it
If you enjoyed this article, I’d truly appreciate it if you could share it—it really motivates me to keep creating more helpful content!
If you’re interested in exploring more, check out these articles.
- MongoDB GridFS Made simple
- Isolation Levels In SQL Server With Examples
- Mastering MongoDB $facet for Powerful Aggregations
Thanks for sticking with me until the end—I hope you found this article valuable and enjoyable!