Several consumers may need the same filtered and projected view of a collection. A standard MongoDB view stores that query definition under a reusable name. It can present an active catalog without exposing internal cost fields in its output, and it reflects source changes when read. It is not an independently stored snapshot or a writable table. This reference targets MongoDB 8.0 and creates complete views in a disposable mongosh fixture database. It contrasts dynamic standard views with the materialized summary lifecycle explained in the separate merge reference.
Store a query contract under a name
createView specifies the view name, source collection or supported source view, and aggregation pipeline. A consumer can then query the view through supported read operations without repeating the definition. This reduces accidental disagreement between several callers that all claim to list active products. The pipeline should express the intended population and output fields, with names that describe the contract clearly. A view's definition is schema metadata, not a collection of precomputed result documents.
The fixture filters active products and projects the public name and price. Internal cost is absent from its result shape. A later query can filter or aggregate that output under the supported interface, but it does not make hidden source fields appear merely because the caller requests them. Define required collation and supported operations deliberately; views have documented restrictions that differ from ordinary collections. Do not promise every write, index, search, or aggregation capability just because basic find works. Check the exact operation and server version your application needs.
Understand dynamic results and write rejection
A standard view computes from its source when queried. Updating a product price in the underlying collection changes a subsequent view result without running a refresh job. That behavior is useful for a shared current-data interface. It also means a view does not freeze historical data or supply a point-in-time report by itself. Concurrent source changes follow the relevant read semantics, not an imagined stored snapshot.
The view is read-only. An insert or ordinary update through the view is not a route for editing the underlying products. Write through an explicit source-facing service with authorization and validation, or choose another data design. A pipeline used to define a standard view must obey its read restrictions, including the prohibition on turning the view definition into an out or merge write job. The main fixture deliberately attempts a view insert and reports the expected rejection before changing the source directly. That makes the read-only boundary visible as behavior rather than a label.
Plan performance on the underlying collections
A standard view does not own a separate set of stored result indexes. Its reads can benefit from suitable indexes on underlying data when the pipeline and query allow it. Filtering active products before projecting fields can give the optimizer a useful source predicate, but the actual plan depends on the entire pipeline. Inspect representative queries with explain under the documented supported interface. A view does not automatically make an expensive lookup or sort cheap.
If every read repeatedly computes a costly historical aggregation, a materialized summary may be a better design. That stores results and introduces a refresh policy, staleness, and reconciliation requirements. The view tradeoff is simpler current-data semantics with computation on reads. Keep the report period bounded and project only fields consumers need. Avoid constructing a long chain of views that hides complex dependencies from maintainers. Document source collections and expected index support so an apparently simple view query can still be diagnosed when data volume grows.
Treat privileges as a separate explicit design
A view can help provide a restricted result shape, but merely creating it does not revoke access to the source collection. A user granted broad database read privileges may still query the underlying products and see internal fields. Configure roles and collection privileges according to the actual access boundary. Test with the restricted identity rather than with an administrator and assume the same results imply the same authority.
Keep view definitions free of secrets and review metadata visibility under the chosen roles. Row filtering can be useful, but a shared tenant view must not become an excuse to accept arbitrary caller tenant identities. Authorization still needs a defined source of trust. Changing a view definition is a contract change that can affect all consumers immediately, so version or coordinate it as appropriate. The practice focuses on a paid tenant-A read interface and source updates; it does not claim to establish a complete multi-tenant security system without the separate role configuration.
MongoDB documentation and version notes
MongoDB Read-Only Collection Views Examples
Each example shows the code, the output it produces and what each part does.
Hiding cost fields behind a read-only catalog view
Run this complete script on the MongoDB 8.0 fixture server. It resets a products collection and recreates a named active_catalog view in the teaching database. Two active products appear without their internal cost fields, while the inactive product is excluded. An insert through the view is rejected. Updating the source product price then changes the next view read to ten without refreshing stored summary rows. The output uses a sorted identifier query and explicit JSON mapping to keep the result stable. The dropped object is only this fixture's named view; no existing application database is involved.
const demo = db.getSiblingDB('codevra_ref_views');
const products = demo.getCollection('products');
products.deleteMany({});
products.insertMany([{ _id: 1, name: 'Mug', price: 8, cost: 3, active: true },
{ _id: 2, name: 'Lamp', price: 20, cost: 9, active: true },
{ _id: 3, name: 'Old item', price: 2, cost: 1, active: false }]);
demo.getCollection('active_catalog').drop();
demo.createView('active_catalog', 'products', [
{ $match: { active: true } }, { $project: { name: 1, price: 1 } }
]);
const view = demo.getCollection('active_catalog');
print(JSON.stringify(view.find().sort({ _id: 1 }).toArray().map(x => ({ name: x.name, price: x.price }))));
try { view.insertOne({ name: 'Attempt', price: 1 }); }
catch (error) { print('read-only ' + (error.code === 166)); }
products.updateOne({ _id: 1 }, { $set: { price: 10 } });
print(view.findOne({ _id: 1 }).price);[{"name":"Mug","price":8},{"name":"Lamp","price":20}]
read-only true
10MongoDB Read-Only Collection Views in Real Projects
Build a paid-orders view for one tenant
Create a view selecting paid orders for tenant A, then change one source order to unpaid. Query the view before and after that change and print the original collection count. The view initially contains two identifiers and later only one; the source still contains all three orders. This independent exercise distinguishes filtering from deletion and dynamic view evaluation from a refresh job. Its tenant predicate is a fixed fixture contract. A production tenant interface must combine its data rule with correctly configured access and trusted tenant identity, rather than allowing users to choose an unrestricted source collection.
const demo = db.getSiblingDB('codevra_ref_views');
const orders = demo.getCollection('orders');
orders.deleteMany({});
orders.insertMany([{ _id: 1, tenant: 'A', paid: true, total: 10 },
{ _id: 2, tenant: 'B', paid: true, total: 20 }, { _id: 3, tenant: 'A', paid: true, total: 30 }]);
demo.getCollection('paid_a').drop();
demo.createView('paid_a', 'orders', [{ $match: { tenant: 'A', paid: true } }, { $project: { total: 1 } }]);
const view = demo.getCollection('paid_a');
print(JSON.stringify(view.find().sort({ _id: 1 }).toArray().map(x => x._id)));
orders.updateOne({ _id: 3 }, { $set: { paid: false } });
print(JSON.stringify(view.find().sort({ _id: 1 }).toArray().map(x => x._id)));
print(orders.countDocuments({}));[1,3]
[1]
3MongoDB Read-Only Collection Views FAQ
Does a standard view store a snapshot?
No. It stores a query definition and computes results from source data when read. A later source update can change the next result without a refresh job. Use a materialized summary or an appropriate consistency strategy when the product needs stored or point-in-time data. The main price update demonstrates the dynamic behavior, while the merge reference handles the different lifecycle of persisted summaries.
Can I insert through the view?
Not through the standard view as though it were an ordinary writable collection. Write through the underlying application service under its authorization and validation rules. The fixture attempts a view insert and verifies the read-only rejection. A view definition also cannot become a hidden out or merge job. Read interfaces and write workflows should have explicit separate contracts.
Where should indexes for a view query live?
On the relevant underlying collections. A standard view does not have an independently stored result index. Whether a source index helps depends on the pipeline and query, so inspect representative execution plans. A reusable view improves consistency of query definitions but does not erase computation cost. For repeatedly expensive summaries, consider persisted materialization with a deliberate refresh and staleness policy.
Does projecting out cost secure the source collection?
Only the view output excludes that field. A caller with direct source privileges may still read it. Configure and test the intended collection-level privileges with the restricted identity. Do not equate a narrow result shape with a complete access-control boundary. Views can support that design, but roles, metadata visibility, tenant trust, and other permitted operations still need explicit review.
MongoDB Read-Only Collection Views Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
After order 3 becomes unpaid, how many source orders remain in the worked collection?
MongoDB Read-Only Collection Views Quiz
2 questions to check what you learned.
Why does the main view show price ten after the source update?
- Standard view results are computed from current source data
- Every view automatically stores a new snapshot after each write
- The rejected view insert changed the price
What is missing from a broad-read user security design that only creates a narrow view?
- Restrictions on direct access to the underlying collection
- A second copy of the same view name
- A random output sort that hides cost