CSE 341: Web Services

W02 Learning Activity: Database Relationships in MongoDB

Overview

In the CRUD activity, you learned how one collection can create, read, update, and delete documents. Many real applications also need documents to relate to each other. For example, an ecommerce store might have products that belong to categories.

In this activity, you will learn how simple relationships work in MongoDB and how an API can represent those relationships clearly.

Discipleship: Relationships

When it comes to databases and applications, one collection by itself can only do so much. The real power often comes when data is connected through relationships.

In a similar way, our relationships with others are some of the most important parts of life. They can make life richer, more meaningful, and more beautiful.

Elder M. Russell Ballard, a modern-day Apostle of Jesus Christ, taught in Remember What Matters Most:

What matters most is our relationships with Heavenly Father and His Beloved Son, our families, and our neighbors, and allowing the Spirit of the Lord to guide us in those relationships.

Invitation: Think about the relationships in your life. Are there any that need care and attention? Seek the guidance of the Spirit to guide you in those relationships.

Preparation Material

Relationships in MongoDB

MongoDB stores data in documents inside collections. Unlike a relational database, MongoDB does not require foreign keys or table joins. Instead, you decide whether related data should be embedded inside one document or referenced from another document.

Common MongoDB relationship choices

Use embedded data when the related information is small and usually read together, such as storing a product's dimensions directly inside the product document. Use references when the related information is shared, changes independently, or belongs in its own collection.

Example: Products and categories

Imagine an ecommerce API with two collections: products and categories. A category document might look like this:

{
  "id": "c1",
  "name": "Electronics",
  "description": "Devices, accessories, and small electronics"
}

A product document can then store a reference to that category by using a field such as categoryId:

{
  "id": "p4",
  "categoryId": "c1",
  "name": "Wireless Mouse",
  "price": 24.99,
  "inStock": true
}

In this example, categoryId: "c1" means the product belongs to the category whose id is c1. MongoDB will store that value, but your API code is responsible for deciding what to do with it.

References do not automatically join data

If you query the products collection, MongoDB returns the product document. It does not automatically fetch the matching category document just because the product has a categoryId field.

Your API has a few choices:

More advanced joins

MongoDB has more advanced ways to combine related data, such as using an aggregation stage called $lookup. A $lookup can query another collection and attach matching documents to the results, which is similar to joining related data in a relational database.

const productsWithCategories = await getDb()
  .collection('products')
  .aggregate([
    {
      $lookup: {
        from: 'categories',
        localField: 'categoryId',
        foreignField: 'id',
        as: 'category'
      }
    }
  ])
  .toArray();

In this example, MongoDB starts with documents from the products collection. For each product, it compares the product's categoryId field with the id field in the categories collection. Matching category documents are added to a new category array on each product result.

At this point in the course, we will keep things simple. For the Week 02 assignment, a simple reference field is enough. You do not need to build a full join system or use $lookup.

Validating relationship fields

When a client creates or updates a product, the API should decide whether to accept any categoryId value or verify that the category exists first.

For example, before creating a product with categoryId: "c1", the controller or model could query the categories collection to make sure a category with id: "c1" exists. If the category does not exist, the API could return 400 Bad Request because the request data is invalid.

Deleting related data

Relationships also affect delete behavior. If a client tries to delete a category that still has products, what should happen?

Your API should choose and document a clear rule. For example:

The safest starting choice is usually to block the delete when related documents still depend on that record.

Activity Instructions

Think through a relationship

Use the product and category example to think through the questions below. Then apply the same reasoning to the books and authors relationship in your Week 02 assignment.

  1. Which field on the product identifies its category?
    Example answer (click to expand)

    The product uses categoryId. That field stores the id of a document in the categories collection.

  2. Should category details be embedded in each product or stored in a separate collection?
    Example answer (click to expand)

    A separate categories collection is a good choice because many products can share the same category and category details may change independently from product details.

  3. What should the API do if a product is created with a category id that does not exist?
    Example answer (click to expand)

    The API can return 400 Bad Request because the client sent a relationship value that does not point to a valid category.

  4. What should the API do if a client tries to delete a category that still has products?
    Example answer (click to expand)

    The API can block the delete and return an error, such as 400 Bad Request or 409 Conflict, because deleting the category would leave related products with an invalid reference.

Submission

After you have completed all the learning activities for this week, return to Canvas to take the W02 Learning Activities quiz.

Other Links: