MongoDB Views
A view is a saved query that behaves like a collection. Applications read from it with find and aggregation commands, yet the view stores no data of its own. Each time someone queries the view, MongoDB runs the saved pipeline on the source collection and returns fresh results.
The Window Picture
A view works like a window in a wall. The window shows part of the room behind it, and nobody can rearrange the furniture through the glass. Change the furniture inside the room, and the view through the window changes too.
Source collection: employees View: publicEmployees
+---------------------------------+ +--------------------------+
| name | dept | salary | active | | name | dept |
| Asha | Sales | 50000 | true | ---> | Asha | Sales |
| Ravi | IT | 65000 | true | | Ravi | IT |
| Neha | HR | 48000 | false | +--------------------------+
+---------------------------------+ active staff only, no salaryWhy Use Views
- Hide sensitive fields. A view can leave out salary, phone, or password fields.
- Simplify complex queries. Developers query the view with a short command instead of rewriting a long pipeline.
- Keep one shared definition. A single view serves every team and keeps results consistent.
- Control access. Roles can grant read access to the view and deny access to the source collection.
Create a View
The createView method takes three parts: the view name, the source collection, and a pipeline.
Sample Data
db.employees.insertMany([
{ name: "Asha", dept: "Sales", salary: 50000, active: true },
{ name: "Ravi", dept: "IT", salary: 65000, active: true },
{ name: "Neha", dept: "HR", salary: 48000, active: false }
])Define the View
db.createView(
"publicEmployees",
"employees",
[
{ $match: { active: true } },
{ $project: { _id: 0, name: 1, dept: 1 } }
]
)Query the View
db.publicEmployees.find()The output lists Asha and Ravi with only the name and dept fields. Neha stays out because she is inactive, and salaries stay hidden.
Normal query commands work on the view:
db.publicEmployees.find({ dept: "IT" })
db.publicEmployees.countDocuments()
db.publicEmployees.aggregate([{ $group: { _id: "$dept", total: { $sum: 1 } } }])Views Update Automatically
Add a new active employee to the source collection:
db.employees.insertOne({ name: "Kiran", dept: "IT", salary: 70000, active: true })
db.publicEmployees.find({ dept: "IT" })Kiran appears in the view immediately. No refresh step exists, because MongoDB rebuilds the result from the source on every query.
Manage Views
List Views
show collections
db.getCollectionInfos({ type: "view" })Views appear in the collection list with the type view.
Change a View
Use collMod to replace the pipeline of an existing view:
db.runCommand({
collMod: "publicEmployees",
viewOn: "employees",
pipeline: [
{ $match: { active: true } },
{ $project: { _id: 0, name: 1, dept: 1, active: 1 } }
]
})Remove a View
db.publicEmployees.drop()Dropping a view deletes only the saved definition. The source data stays safe.
Limits of Views
| Limit | What It Means |
|---|---|
| Read-only | Inserts, updates, and deletes on a view fail. Change the source collection instead. |
| No own indexes | A view cannot hold indexes. Queries use the indexes of the source collection. |
| Computed on demand | Heavy pipelines run again on every query, which costs time. |
| Limited commands | Some operations, such as text search with $text and $near, do not work on views. |
Speed Tips
- Put
$matchearly in the view pipeline so MongoDB can use source indexes. - Create indexes on the source fields that the pipeline filters and sorts on.
- Keep the pipeline short for views that applications call often.
Views for Access Control
Security teams use views to share only safe fields. A role grants the find action on publicEmployees and grants nothing on employees. A user with that role sees names and departments and never touches salary data.
Reporting user ---> publicEmployees (allowed) ---> employees (blocked directly)On-Demand Materialized Views
A standard view recalculates on every query. When a pipeline takes too long, save the result into a real collection with the $merge stage and refresh it on a schedule:
db.orders.aggregate([
{ $group: { _id: "$item", totalQty: { $sum: "$qty" } } },
{ $merge: { into: "itemTotals", whenMatched: "replace", whenNotMatched: "insert" } }
])The itemTotals collection stores real documents, supports its own indexes, and answers queries quickly. The data stays as fresh as the last refresh.
Summary
A view stores a pipeline and presents the result as a read-only collection. Views hide sensitive fields, simplify repeated queries, and always show current source data. They cannot hold indexes or accept writes, and heavy pipelines run again on every read. A $merge stage builds a stored result for cases that need faster reads.
