Loránd Péter
Loránd Péter

Reputation: 314

How to use the new $getField operator in the $project stage in MongoDB 5.0?

MongoDB now supports key names which contain a period (.) or starts with a $. (new in version 5.0)

They introduced new operators in the aggregation framework like $getField and $setField (docs) to work with these kind of keys but they didn't provide any examples on how to use them in the $project stage.

If I have the following collection (from the docs):

{ "_id" : 1, "item" : "sweatshirt", "price.usd": 45.99, qty: 300 }
{ "_id" : 2, "item" : "winter coat", "price.usd": 499.99, qty: 200 }
{ "_id" : 3, "item" : "sun dress", "price.usd": 199.99, qty: 250 }

how to project only the price.usd field?

Upvotes: 1

Views: 4259

Answers (3)

drew91
drew91

Reputation: 1

Use $getField operator when you want to get some specific fields from an object that is returned by an expression.

Take as example the following collection: you have a field "items" with an array of objects and a field "selectedItemId" that refers to one of the objects inside the "items" array.

[{
    "_id": 111,
    "items": [
      {
        "_id": 211,
        "name": "table",
        "price.usd": 45.99,
        "quantity": 3
      },
      {
        "_id": 212,
        "name": "shoe",
        "price.usd": 45.99,
        "quantity": 3
      },
    ],
    "selectedItemId": 212,
}]

Never mind the background of how you got to this collection... You want to check the objects inside "items" and return only the name of the selected item, like that:

[{
    "_id": 111,
    "name": "shoe"
}]

If you have to do that in the $project stage, you could run a $filter to return the object you want, and wrap everything with the $getField operator to specify you only want the field "name":

db.collection.aggregate([
  {
    $project: {
      _id: "$_id",
      name: {
        "$getField": {
          field: "name", // return field "name"
          input: { // from the obj returned by the expr below
            $first: { // destruct array to obj as you know you only get one item anyway
              $filter: {
                input: "$items", // filter array "items"
                as: "item", // naming every item as "item"
                cond: { // by
                  $eq: [ // comparing the fields
                    "$selectedItemId",
                    "$$item._id"
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
])

I guess the key is to think it as a projection inside a projection.

Upvotes: 0

Murat Colyaran
Murat Colyaran

Reputation: 2189

That's not right. You can use $getField in the project stage. However, you'd need to make some additional operations if you want to preserve price.usd key

Usage:

db.collection.aggregate([
  {
    "$project": {
      "price": {
        $getField: "price.usd"
      }
    }
  }
])

Playground

Upvotes: 2

Tom Slabbaert
Tom Slabbaert

Reputation: 22296

These new functions can't be used in the $project stage as it has very clear structure restrictions:

_id: <0 or false>
<field>: <1 or true>
<field>: <expression>
<field>:<0 or false>

You can either use field:0 in the $project stage for all other fields:

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

Mongo Playground

Or if you have too many fields / an unstructured schema that changes and you can't rely on that you could convert each document to an array using $objectToArray, filter that array to contains only the relevant keys then finally convert it back to an object using arrayToObject, like so:

db.collection.aggregate([
  {
    $project: {
      rootAsArray: {
        "$objectToArray": "$$ROOT"
      }
    }
  },
  {
    $project: {
      newRoot: {
        $filter: {
          input: "$rootAsArray",
          as: "field",
          cond: {
            $eq: [
              "$$field.k",
              "price.usd"
            ]
          }
        }
      }
    }
  },
  {
    $replaceRoot: {
      newRoot: {
        "$arrayToObject": "$newRoot"
      }
    }
  }
])

Mongo Playground

Upvotes: 0

Related Questions