How do I rename fields when performing search/projection in MongoDB?

MongodbMongodb QueryAggregation Framework

Mongodb Problem Overview


Is it possible to rename the name of fields returned in a find query? I would like to use something like $rename, however I wouldn't like to change the documents I'm accessing. I want just to retrieve them differently, something that works like SELECT COORINATES AS COORDS in SQL.

What I do now:

db.tweets.findOne({}, {'level1.level2.coordinates': 1, _id:0})
{'level1': {'level2': {'coordinates': [10, 20]}}}

What I would like to be returned is: {'coords': [10, 20]}

Mongodb Solutions


Solution 1 - Mongodb

So basically using .aggregate() instead of .find():

db.tweets.aggregate([
    { "$project": {
        "_id": 0,
        "coords": "$level1.level2.coordinates"
    }}
])

And that gives you the result that you want.

MongoDB 2.6 and above versions return a "cursor" just like find does.

See $project and other aggregation framework operators for more details.


For most cases you should simply rename the fields as returned from .find() when processing the cursor. For JavaScript as an example, you can use .map() to do this.

From the shell:

db.tweets.find({},{'level1.level2.coordinates': 1, _id:0}).map( doc => {
  doc.coords = doc['level1']['level2'].coordinates;
  delete doc['level1'];
  return doc;
})

Or more inline:

db.tweets.find({},{'level1.level2.coordinates': 1, _id:0}).map( doc => 
  ({ coords: doc['level1']['level2'].coordinates })
)

This avoids any additional overhead on the server and should be used in such cases where the additional processing overhead would outweigh the gain of actual reduction in size of the data retrieved. In this case ( and most ) it would be minimal and therefore better to re-process the cursor result to restructure.

Solution 2 - Mongodb

As mentioned by @Neil Lunn this can be achieved with an aggregation pipeline:

And starting Mongo 4.2, the $replaceWith aggregation operator can be used to replace a document by a sub-document:

// { level1: { level2: { coordinates: [10, 20] }, b: 4 }, a: 3 }
db.collection.aggregate(
  { $replaceWith: { coords: "$level1.level2.coordinates" } }
)
// { "coords" : [ 10, 20 ] }

Since you mention findOne, you can also limit the number of resulting documents to 1 as such:

db.collection.aggregate([
  { $replaceWith: { coords: "$level1.level2.coordinates" } },
  { $limit: 1 }
])

Prior to Mongo 4.2 and starting Mongo 3.4, $replaceRoot can be used in place of $replaceWith:

db.collection.aggregate(
  { $replaceRoot: { newRoot: { coords: "$level1.level2.coordinates" } } }
)

Solution 3 - Mongodb

As we know, in general, $project stage takes the field names and specifies 1 or 0/true or false to include the fields in the output or not, we also can specify the value against a field instead of true or false to rename the field. Below is the syntax

    db.test_collection.aggregate([
        {$group: {
            _id: '$field_to_group',
            totalCount: {$sum: 1}
        }},
        {$project: {
            _id: false,
            renamed_field: '$_id',    // here assigning a value instead of 0 or 1 / true or false effectively renames the field.
            totalCount: true
        }}
    ])

Solution 4 - Mongodb

Stages (>= 4.2)

  • $addFields : {"New": "$Old"}
  • $unset : {"$Old": 1}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionthemiurgoView Question on Stackoverflow
Solution 1 - MongodbNeil LunnView Answer on Stackoverflow
Solution 2 - MongodbXavier GuihotView Answer on Stackoverflow
Solution 3 - MongodbAbhilashView Answer on Stackoverflow
Solution 4 - MongodbKevin FaircloughView Answer on Stackoverflow