MongoDB views are read-only query definitions over a collection or another view. A view stores its aggregation pipeline, not the pipeline result, so each read applies the pipeline to the current source data. A standard view therefore does not provide materialized-view caching.
Creating a View #
db.createView() takes a target collection name, a source namespace, and an
aggregation pipeline:
db.createView(
"active_orders",
"orders",
[
{ $match: { status: "active" } },
{ $project: { customerId: 1, total: 1, createdAt: 1 } }
]
)Clients query active_orders as they query a collection:
db.active_orders.find({ customerId: customerId })The server combines the view definition with the client query and plans the
result against the source collection. Standard views use indexes on their
underlying collections; general-purpose indexes cannot be created, dropped, or
rebuilt directly on a standard view. Place selective $match stages early and
index source fields used by the pipeline and client query.
Views can also be created with db.createCollection():
db.createCollection("recent_orders", {
viewOn: "orders",
pipeline: [
{ $match: { createdAt: { $gte: ISODate("2026-01-01") } } }
],
collation: { locale: "en", strength: 2 }
})The view and its source must be in the same database. A view’s default collation is fixed when the view is created, and a query cannot override it with a different collation.
Design Constraints #
A view can expose a subset of fields, rename fields, join another collection
with $lookup, or aggregate data. It cannot accept inserts, updates, or
deletes. Unsupported aggregation stages, including $out and $merge, cannot
appear in a standard view definition because they write data.
Views can depend on other views, but the dependency graph must not contain a
cycle. Keep the pipeline deterministic and bounded. A pipeline that performs a
large $lookup, $group, or sort on every request transfers operational cost
from writes to reads.
Views do not support point-in-time or snapshot read isolation. Blocking aggregation stages are also subject to the server’s aggregation memory and temporary-file behavior. Explain and load-test the final query rather than assuming that hiding a pipeline behind a view makes it inexpensive.
Authorization and Schema Boundaries #
When authentication is enabled, creating a view requires createCollection
privilege on its database and the relevant read privileges for referenced
namespaces. Querying the view requires find privilege on the view namespace;
the client does not also need direct find privilege on the source
collections.
The authorization separation can expose a controlled projection, such as employee records without personally identifiable fields. A view is not a complete security boundary if other privileges still expose the source collection, metadata, or side channels. Grant the application account only the namespaces and commands it requires.
A standard view has no independent document-validation schema because it cannot
accept writes. Validation on the source collection constrains writes to that
collection but does not promise that a projection, $lookup, or computed field
matches an application schema forever. Version the view definition and consumer
contract together. The view and every namespace it references must remain in
the same database; a view cannot cross the database namespace boundary.
Standard and On-Demand Materialized Views #
A standard view is appropriate when freshness matters and the underlying query is inexpensive. When a derived result is expensive but can tolerate refresh latency, MongoDB calls a collection populated by an aggregation pipeline an on-demand materialized view:
db.orders.aggregate([
{ $group: { _id: "$customerId", lifetimeValue: { $sum: "$total" } } },
{ $merge: { into: "customer_lifetime_value", whenMatched: "replace",
whenNotMatched: "insert" } }
])The destination is a real collection, so it can be indexed and queried
efficiently. $merge can incrementally replace matching documents, whereas
$out replaces the destination collection. A scheduler, change-stream
consumer, Atlas Stream Processing pipeline, or application workflow must
refresh the result. State its freshness contract explicitly because a normal
aggregation does not continuously maintain an on-demand materialized view.
Readers can observe a partially refreshed destination when a $merge job
updates many documents. If consumers require one-version visibility, write to a
versioned collection and switch a small metadata pointer after validation, then
retire the old version. Account for duplicate storage, index builds, change
capture during the refresh, and cleanup failure in the operating procedure.
Use $merge when consumers need to watch the destination through change
streams. A collection replaced through $out cannot be watched as the same
materialized-view update mechanism.
Comments
Read or comment on GitHub (sign-in required to comment)