Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Mohamed-Code-309/1ba42d0455aff82589c5a6e1a7b66349 to your computer and use it in GitHub Desktop.
Save Mohamed-Code-309/1ba42d0455aff82589c5a6e1a7b66349 to your computer and use it in GitHub Desktop.

Data Projection in MongoDB Documents πŸ“Š

  1. $project
  2. $addFields
  3. $set
  4. $unset

Definition:

The $project stage allows you to reshape documents in the pipeline. This stage can be used to:

  1. select or exclude fields from the documents.
  2. rename an existing fields.
  3. create new fields that contain values based on existing fields.
  4. transform or modify the values of existing fields.

Syntax:

$project : { <field1>: <1>, <field2>: <0>, <newField>: <expression>}
  • 1: field will be included in the resulted document.
  • 0: field will not be included (excluded).
  • expression: an expression that specifies how to reshape the document.
  • $: the dollar sign can be used in the expression part to reference the value of an existing field as we will see in the examples.

Examples:

(1) To include or exclude specific fields from documents:

(1-1)

db.collection.aggregate([
   { $project: { _id: 0, name: 1, age: 1 } }
])

This will only include the name and age fields in the output, while excluding the _id field.

(1-2)

db.collection.aggregate([
   { $project: { _id: 0, age: 0 } }
])

This will only execlude the _id_ and age fields in the output, while including all other fields like name, email, ..etc.

(1-3)

The following query is not valid and will produce an error:

db.collection.aggregate([
   { $project: { _id: 0, name: 1, age: 0 } }
])

Importnat note to consider when include or exclude specific fields. MongoDB doesn't support include fields and exclude specific fields at the same time except the _id field.

Trying to exclude the age field while including other fields in the same $project stage is not allowed in MongoDB's aggregation framework.

If you want to exclude the age field, you would need to remove it from the inclusion list like this:

db.users.aggregate([
   { $project: { _id: 0, name: 1 } }
])

(2) To rename an existing field in a document:

Suppose you have a collection of documents with a field called fname and you want to rename it to firstName. You can use the following $project stage to rename the field:

db.collection.aggregate([
   {
      $project: {
         firstName: "$fname",
         fname: 0
      }
   }
])

In this example, the $project stage creates a new field called firstName and assigns it the value of the fname field. the dollar sign $ is used to reference the fname field.

The fname field is then excluded from the output by setting its value to 0.

Another example, if you have a document with a nested field called address.city, you can use the following $project stage to rename the city field to town:

db.collection.aggregate([
   {
      $project: {
         "address.town": "$address.city",
         "address.city": 0
      }
   }
])

Note that you can also rename multiple fields - not just one field - using a single $project stage and you also don't have to exclude the field to be renamed from the result.

(3) To create new fields that contain values based on existing fields:

Consider a collection of products that has fields such as name, price, and quantity. You might want to create a new field called totalValue that contains the total value of each product based on its price and quantity.

You can use the $multiply expression to calculate this value and create a new field using the $project stage as shown below:

db.products.aggregate([
  {
    $project: {
      name: 1,
      price: 1,
      quantity: 1,
      totalValue: { $multiply: ["$price", "$quantity"] }
    }
  }
])

In this example, the $multiply expression multiplies the values of the price and quantity fields for each document, and the result is stored in a new field called totalValue.

Another example, if you have a document that contain name and age of the user and you want to add a new field that indicates the year in which the person was born:

db.collection.aggregate([
   { $project: { name: 1, age: 1, birthYear: { $subtract: [2023, "$age"] } } }
])

This will create a new field called birthYear that contains the calculated value of subtracting the age field from the current year (2023).

(4) To transform the values of an existing field :

Let's say you have a collection of documents that contain a field called price and you want to transform the values of this field by multiplying them by a certain factor.

You can use the $multiply operator in the $project stage to achieve this:

db.collection.aggregate([
   { $project: { _id: 0, price: { $multiply: ["$price", 1.1] } } }
])

In this example, the $multiply operator multiplies the value of the price field in each document by 1.1, effectively increasing the price of each item by 10%.

Here's another example that uses the $toUpper operator to transform the values of a field called name to uppercase:

db.collection.aggregate([
   { $project: { _id: 0, name: { $toUpper: "$name" } } }
])

Table of contents ☝️


Definition:

The $addFields stage is equivalent to the $project stage. The only difference is that $project stage only includes fields that are explicitly specified in the stage, while $addFields automatically includes all existing fields in the document and adds new fields.

We can say that $addFields is like a shorthand for $project that includes all existing fields in addition to the newly created fields.

Syntax:

{ $addFields: { <newField>: <expression>, ... } }
//all other existing fields will automatically included

If the name of the new field is the same as an existing field name (including _id), $addFields overwrites the existing value of that field with the value of the specified expression.

Examples:

(1) Consider a collection of products that has fields such as name, price, and quantity. You want to add a new field called total to each document that represents the total value of the sale (i.e., quantity * price).

db.sales.aggregate([
  {
    $addFields: {
      total: { $multiply: [ "$quantity", "$price" ] }
    }
  }
])

After running this pipeline, the output documents will include the original fields as well as the new total field:

[
   { "_id": 1, "name": "A", "quantity": 2, "price": 1.50, "total": 3.00 }
   { "_id": 2, "name": "B", "quantity": 3, "price": 0.75, "total": 2.25 }
   { "_id": 3, "name": "c", "quantity": 4, "price": 0.50, "total": 2.00 }
]

(2) Suppose you have a collection of blog posts, where each document includes an _id field and other fields like title and author. You want to replace the _id field with a new field called postId, but keep all the other fields in the document.

db.posts.aggregate([
  {
    $addFields: {
      postId: "$_id",
      _id: 0
    }
  }
])

In this pipeline, the $addFields stage creates a new field called postId and assigns it the value of the existing _id field. Then it sets the _id field to 0, effectively removing it from the output.

After running this pipeline, the output documents will look like that:

[
   { "title": "First Post", "author": "Ahmed", "postId": 1 }
   { "title": "Second Post", "author": "Omnia", "postId": 2 }
   { "title": "Third Post", "author": "Mohamed", "postId": 3 }
]

Table of contents ☝️


Definition:

The $set act the same as $addFields stage. It was introduced in MongoDB 4.2

Syntax:

{ $set: { <newField>: <expression>, ... } }

Examples:

Consider the following documents in a collection called vehicles :

[
   { "_id": 1, "type": "car", "specs": { "doors": 4, "wheels": 4 } },
   { "_id": 2, "type": "motorcycle", "specs": { "doors": 0, "wheels": 2 } },
   { "_id": 3, "type": "jet ski" }
]

To add a new field fuel_type to the embedded document specs:

db.vehicles.aggregate( [
   { $set: { "specs.fuel_type": "unleaded" } }
] )

The operation returns the following results:

[
   { "_id": 1, "type": "car", "specs": { "doors": 4, "wheels": 4, "fuel_type": "unleaded" } },
   { "_id": 2, "type": "motorcycle", "specs": { "doors": 0, "wheels": 2, "fuel_type": "unleaded" } },
   { "_id": 3, "type": "jet ski", "specs": {"fuel_type": "unleaded"} }
]

The $addFields stage will produce the same result as we did here with the $set stage.

Table of contents ☝️


Definition:

The $unset stage is used to exclude fields from documents in a collection.

Syntax:

The $unset stage has the two following syntax:

  1. To remove a single field :

    { $unset: "<field>" }
  2. To remove multiple fields :

    { $unset: [ "<field1>", "<field2>", ... ] }

The $unset is an alias for the $project stage that excludes fields:

{ $project: { "<field1>": 0, "<field2>": 0, ... } }

Examples:

Consider the following documents in a collection called books :

 [
   { 
      "_id" : 1, 
      "title": "Antelope Antics", 
      "isbn": "0001122223334", 
      "author": { "last":"An", "first": "Auntie" }, 
      "copies": [ 
         { "warehouse": "A", "qty": 5 }, 
         { "warehouse": "B", "qty": 15 } 
      ] 
   },
   { 
      "_id" : 2, 
      "title": "Bees Babble", 
      "isbn": "999999999333", 
      "author": { "last":"Bumble", "first": "Bee" }, 
      "copies": [ 
         { "warehouse": "A", "qty": 2 }, 
         { "warehouse": "B", "qty": 5 } 
      ]
   }
]

To remove multiple fields:

db.books.aggregate([
   { $unset: [ "isbn", "author.first", "copies.warehouse" ] }
])

The previous operation outputs the following documents:

{
    "_id": 1,
    "title": "Antelope Antics",
    "author": { "last": "An" },
    "copies": [ 
      { "qty": 5 }, 
      { "qty": 15 }
    ]
}
{
    "_id": 2,
    "title": "Bees Babble",
    "author": { "last": "Bumble" },
    "copies": [
        { "qty": 2 },
        { "qty": 5 }
    ]
}

Table of contents ☝️


Related Content:

βœ”οΈ How To Query Array Fields in MongoDB Documents

βœ”οΈ How To Add and Remove Array Elements in MongoDB Documents

βœ”οΈ How To Update Array Elements in MongoDB Documents


Links:

πŸ•΄οΈ Linkedin: Dragon Slayer 🐲
πŸ“ Articles: All Articles written by D.S

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment