MongoDB: How to use a view to join collections (with examples)

Updated: February 1, 2024 By: Guest Contributor Post a comment

Overview

MongoDB is a powerful NoSQL database that offers a plethora of features tailored for flexibility, scalability, and performance. One such feature is the ability to create views that can simplify access to data, particularly when dealing with joined data from multiple collections. Views can be seen as the result of stored queries that can execute against collections as if they were actual collections themselves.

In this tutorial, we’ll explore how to use views in MongoDB to join collections. We’ll take you step by step from understanding the basics of MongoDB views to crafting complex joins. But before we dive into complex examples, let’s understand what views are and why would you use them.

What are MongoDB Views?

In MongoDB, a view is a read-only result set that is defined by an aggregation pipeline over one of the existing collections (referred to as the backing collection). Contrasting to views in SQL databases, these do not store data and are dynamically regenerated every time the view is queried. A View adjusts to data changes in collections instantly, ensuring your queries return up-to-date data without manual refreshes.

Why Use Views for Joining Collections?

Views simplify complex aggregations and transformations by encapsulating them. This means you can hide complex queries behind a simple view interface, facilitating data access for applications or users not familiar with aggregation. This is particularly useful when you have multiple collections that are related and you frequently need to query joint datasets.

Creating a Simple View to Join Collections

Let’s start with a basic example where we have two collections: customers and orders. Imagine you want to create a view that shows orders alongside customer information. You would use the $lookup aggregation stage to join these two collections.

// customers collection document example
{
  "_id": ObjectId("somecustomerid"),
  "name": "John Doe"
}

// orders collection document example
{
  "_id": ObjectId("someorderid"),
  "customerId": ObjectId("somecustomerid"),
  "orderTotal": 100
}

The following MongoDB shell command creates a view named customerOrders that includes customer names alongside their orders:

db.createView(
  "customerOrders",
  "orders",
  [
    {
      $lookup: {
        from: "customers",
        localField: "customerId",
        foreignField: "_id",
        as: "customerInfo"
      }
    }
  ]
)

With this view in place, you can now query the customerOrders view to get a list of orders with customer names:

db.customerOrders.find().pretty()

Here’s an example of what a document in the customerOrders view might look like:

{
  "_id": ObjectId("someorderid"),
  "orderTotal": 100,
  "customerInfo": [
    {
      "_id": ObjectId("somecustomerid"),
      "name": "John Doe"
    }
  ]
}

Handling Multiple Joins in Views

As your data model becomes more complex, you’ll likely need to join more than two collections. Continuing with our previous example, suppose there’s now a third collection called products, and you want to include product details in the customerOrders view. First, let’s consider a document from the products collection:

// products collection document example
{
  "_id": ObjectId("someproductid"),
  "name": "Widget",
  "price": 25
}

Assuming our orders collection now includes an array of product IDs for each order, we can update our view to also join the products collection:

db.customers.aggregate([
  {
    $lookup: {
      from: "orders",
      localField: "_id",
      foreignField: "customerId",
      as: "ordersInfo"
    }
  },
  {
    $unwind: "$ordersInfo"
  },
  {
    $lookup: {
      from: "products",
      localField: "ordersInfo.productIds",
      foreignField: "_id",
      as: "productDetails"
    }
  },
]).forEach(printjson)

Each document in the view would now contain information about the customer, the order, and the products associated with that order.

Advanced Join Conditions and View Creation

Creating views that join collections is useful, but sometimes you might need to join on more than just simple field equality. MongoDB’s $lookup also supports a let clause and a pipeline argument, allowing you to specify more advanced conditions and transformations during the join.

Performance Considerations and Best Practices

Since views in MongoDB are not materialized, the underlying aggregation pipeline runs each time you query the view. This can have a significant impact on performance, especially for views with complex joins or large data sets. To mitigate performance impact, you should:

  • Ensure proper indexing on the fields used for joining.
  • Consider caching frequently accessed views when feasible.
  • Use the $project aggregation stage to limit the fields included in the view, thereby reducing the amount of data processed.

Conclusion

In conclusion, MongoDB views offer a powerful way to join collections transparently, camouflaging complex joins and fostering cleaner, more maintainable queries.