Labs ICT
โญ Pro Login

$project Stage

reshaping your documents.

$project Stage

The $project stage is like a lens that focuses on specific parts of your documents. It reshapes, renames, and transforms documents as they pass through the pipeline. Think of it as the final touch that makes your data presentation-ready.

Need to rename fields for clarity? Calculate new fields based on existing ones? Remove sensitive information from results? $project does it all. It's the finishing touch that makes your aggregation results shine.

This stage is incredibly powerful because it lets you control exactly what your output looks like. No more messy data structures โ€“ shape your results however you want.

Including and Excluding Fields

The simplest use of $project is controlling which fields appear in the output. Set a field to 1 to include it, or 0 to exclude it. The _id field is included by default โ€“ set it to 0 if you don't want it.

Here's how to return only specific fields from your orders:

db.orders.aggregate([
  { $match: { status: "completed" } },
  { $project: { 
    _id: 0, 
    orderId: 1, 
    amount: 1, 
    customerName: 1 
  }}
])

This returns only the orderId, amount, and customerName fields โ€“ no _id, no status, no other fields. It's like creating a custom view of your data for specific use cases.

You can also use $project to reshape embedded documents. Want to flatten a nested structure? Extract specific fields from an array? $project handles it all with its flexible syntax.

Computed Fields

This is where $project really shines. You can create new fields based on calculations using existing fields. It's like having a formula column in Excel, but way more powerful.

Let's say you want to calculate the profit margin for each order and create a formatted status message:

db.orders.aggregate([
  { $project: { 
    orderId: 1,
    amount: 1,
    cost: 1,
    profitMargin: { 
      $multiply: [{ $divide: [{ $subtract: ["$amount", "$cost"] }, "$amount"] }, 100] 
    },
    statusMessage: { 
      $concat: ["Order ", "$orderId", " is ", "$status"] 
    }
  }}
])

This creates two new fields: profitMargin (calculated as a percentage) and statusMessage (a formatted string). You can use arithmetic operators ($add, $subtract, $multiply, $divide), string operators ($concat, $toUpper, $toLower), and many more.

Computed fields are great for creating derived data that doesn't exist in your original documents. They let you enrich your results without modifying the source data.

Array Operations

$project also excels at working with arrays. You can filter, transform, and reshape arrays directly in the pipeline. It's like having a mini-array processing engine built into your aggregation.

Here's an example that filters an array to only include items above a certain price and calculates the total:

db.orders.aggregate([
  { $project: { 
    orderId: 1,
    expensiveItems: { 
      $filter: { 
        input: "$items", 
        as: "item", 
        cond: { $gte: ["$$item.price", 100] } 
      }
    },
    totalExpensive: { 
      $sum: { 
        $map: { 
          input: { 
            $filter: { 
              input: "$items", 
              as: "item", 
              cond: { $gte: ["$$item.price", 100] } 
            }
          }, 
          as: "expensive", 
          in: "$$expensive.price" 
        }
      } 
    }
  }}
])

This filters the items array to only include expensive items (price >= 100) and calculates their total. You can use $filter to select items, $map to transform them, and $reduce to aggregate them.

Array operations in $project are perfect for e-commerce (filtering products by price), social media (selecting specific post types), or any application where you need to work with embedded arrays.

Try it Yourself โ†’

๐Ÿงช Quick Quiz

What does the $project stage do?