MongoDB Views

Last update: 2021-07-25 21:42:07 +0800 +0800
Available translations: 日本語 / 中文(简体) / 中文(繁體)

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:

javascript
db.createView(
  "active_orders",
  "orders",
  [
    { $match: { status: "active" } },
    { $project: { customerId: 1, total: 1, createdAt: 1 } }
  ]
)

Clients query active_orders as they query a collection:

javascript
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():

javascript
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:

javascript
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.

References #

Comments

Read or comment on GitHub (sign-in required to comment)