Creating and Querying Views in MongoDB
Learn how to create and query a read-only view in MongoDB by combining data from multiple collections.
Photo by Rostislav Uzunov: https://www.pexels.com/photo/purple-and-pink-diamond-on-blue-background-5011647/
A MongoDB view is a read-only queryable object whose contents are defined by an aggregation pipeline on other collections or views. — Views (MongoDB Manual)
There are two types of views in MongoDB:
Standard views (in-memory, computed on demand)
On-Demand Materialized views (stored and read from disk, do not refresh automatically)
This article focuses on standard views. I believe the best way to understand standard Mongo views is by creating and querying one. We’ll start by defining two collections.
The Initial Collections
The aircraft collection
// Create and populate aircraft collection
db.createCollection("aircraft");
db.aircraft.insertMany([
{ _id: 1, model: "Boeing 737-700", nbPassengers: 200, range: 5000, operator: "Austrian" },
{ _id: 2, model: "Airbus A321", nbPassengers: 220, range: 6800, operator: "Air France" },
{ _id: 3, model: "Embraer E175-E2", nbPassengers: 80, range: 3000, operator: "LOT" },
]);
The flights collection
// Create and populate flights (link via aircraftid)
db.createCollection("flights");
db.flights.insertMany([
{ from: "Vienna", to: "Amsterdam", aircraftId: 1 },
{ from: "Paris", to: "Lyon", aircraftId: 2 }
])
The flight documents reference an aircraft via the aircraftId field. Let’s go ahead and create a view that will display the aircraft model and operator instead of the id. We’ll take an iterative approach, but the result should look like this:
+--------+-----------+----------------+------------+
| from | to | aircraft | operatedBy |
+--------+-----------+----------------+------------+
| Vienna | Amsterdam | Boeing 737-700 | Austrian |
| paris | Lyon | Airbus A321 | Air France |
+--------+-----------+----------------+------------+
Creating a Simple MongoDB Lookup View
With the collections in place, we can create a simple lookup view that merges documents from both collections.
db.createView("detailedFlights", "flights", [
{
$lookup: {
from: "aircraft", localField: "_id", foreignField: "aircraftId", as: "lookup"
}
}
])
Explanation:
db.createView("detailedFlights", "flights", [...])
: This line creates a view named "detailedFlights" in the current database. The view will be based on the "flights" collection, and the aggregation pipeline stages specified in the array will be applied to create the view's data.
$lookup
Stage: The $lookup
stage performs a left outer join between two collections. In this case, it's joining the "flights" collection with the "aircraft" collection based on certain fields.
from: "aircraft"
: Specifies the collection to join with, which is the "aircraft" collection in this case.localField: "_id"
: Specifies the field from the "flights" collection that will be used for the join. In this case, it's using the "_id" field of the "flights" collection.foreignField: "aircraftId"
: Specifies the field from the "aircraft" collection corresponding to the join field in the "flights" collection. In this case, it's using the "aircraftId" field from the "aircraft" collection.as: "lookup"
: Specifies the name of the new array field that will hold the joined documents from the "aircraft" collection. The joined documents will be stored in the "lookup" field.
The result of this stage is an array of “flights” documents, each of which has an additional “lookup” field containing information from the corresponding “aircraft” document, as an array.
Flattening the $lookup Array
The Mongo view we created earlier works, but the results are not very friendly. All the aircraft information is stored as an array, even though there is only one id we can bind to. Let's go ahead and transform the array into a simple object.
db.createView("detailedFlights", "flights", [
{
$lookup: {
from: "aircraft", localField: "aircraftId", foreignField: "_id", as: "lookup"
}
},
{
$unwind: {
path: "$lookup"
}
},
}
Explanation:
$unwind Stage: The $unwind stage is used to destructure an array field created by the previous $lookup stage. It takes an array field and produces multiple documents, each containing one value from the array field.
path: "$lookup": Specifies the path to the array field that needs to be unwound. In this case, it's the "lookup" field that was created by the previous $lookup stage.
Showing Only the Fields We Need by Using $project
We are not interested in all the fields, so let's add a projection stage and only display the following fields: "from", "to", "aircraft ($lookup.model)" and "operatedBy ($lookup.operator)
db.createView("detailedFlights", "flights", [
{
$lookup: {
from: "aircraft", localField: "aircraftId", foreignField: "_id", as: "lookup"
}
},
{
$unwind: {
path: "$lookup"
}
},
{
$project:{
_id: 0,
from: 1,
to: 1,
aircraft: "$lookup.model",
operatedBy: "$lookup.operator"
}
}
])
Explanation:
$project Stage: The $project stage allows you to reshape documents and include or exclude fields.
_id: 0: Excludes the "_id" field from the output documents.
from: 1 and to: 1: Includes the "from" and "to" fields in the output documents.
aircraft: "$lookup.model": Includes a new field named "aircraft" in the output documents. The value of this field is taken from the "model" field of the "lookup" array element created by the $lookup stage.
operatedBy: "$lookup.operator": Includes a new field named "operatedBy" in the output documents. The value of this field is taken from the "operator" field of the "lookup" array element created by the $lookup stage.
Querying MongoDB Views
With our view created, we can execute queries against it, just as if it were a regular collection.
// all the documents in the view
db.getCollection("detailedFlights").find({})
// paging
db.getCollection("detailedFlights").find({}).skip(1).take(1)
// count all the documents in the view
db.getCollection("detailedFlights").countDocuments({})
// all the documents departing from Paris
db.getCollection("detailedFlights").find({"from": "Paris"})
// all the documents where aircraft contains Airbus
db.getCollection("detailedFlights").find({"aircraft": {$regex: "Airbus"}})
MongoDB Standard View Limitations
As you can see, views can be a powerful tool that can help simplify your queries. However, they have a few limitations that you should be aware of:
Views are read-only. You can only perform queries, not change documents.
Standard views are stored in memory and are computed when you read the view. This might incur some performance penalties if you work with large amounts of data.