25

I have an object stored in MongoDB which looks like:

{
_id: 123
name: "xyz"
    attrib: 
    {
       address: "123 xyz rd",
       phone: "123-456-7890"
    }
}

I want to flatted this structure, so that there is no attrib field, I just have address and phone field along with name and _id.

So far, this is what I've tried:

db.emp.aggregate(
    { 
    $project : {
            { addr : '$review.attrib.address' },
            { phn : '$review.votes.phone' },
        }
    }
);

Can anyone help me further?

4
  • Why not just flatten it on the client? There's no practical way to flatten the fields in MongoDB efficiently with all sizes of collections. Commented Jul 30, 2013 at 4:31
  • What do you mean by flattening on the client? How can I do that? Commented Jul 30, 2013 at 4:35
  • Use whatever programming language you plan on using and do your own client side projection of the data. Commented Jul 30, 2013 at 5:44
  • Does this answer your question? MongoDB - $project nested document to root level Commented May 12, 2022 at 7:02

3 Answers 3

36

I tried it:

db.abc.insert({
  _id: 123,
  name: "xyz",
  attrib: {
     address: "123 xyz rd",
     phone: "123-456-7890"
  }
});
db.abc.aggregate(
{ 
  $project : {
    _id:1,
    name:1,
    addr : '$attrib.address',
    phn : '$attrib.phone' 
  }
}
);

More detail, you can see:use $project to rename fields http://docs.mongodb.org/manual/reference/aggregation/project/

Sign up to request clarification or add additional context in comments.

3 Comments

I get this: "errmsg" : "exception: aggregation result exceeds maximum document size (16MB)"
The aggregation framework currently returns the results as a single document as a result, the result is limited to the 16MB document size limit. The next release should include the ability to pipe the results into a collection eliminating this limitation. In the meantime, writing a MapReduce process is the way to go if you encounter the 16MB limit. If you only happen to need to retrieve a few documents at a time, you should start your pipe with a $match operator and/or use a $limit to possibly work within the 16MB limit.
I am struggling with writing a map/reduce function on MongoVUE. Any hint regarding that will be greatly appreciated.
8

Since mongodb version 4.2, you can use one update with aggregation pipeline of $set and $unset.

The short and literal option is:

db.collection.update({},
  [
    {$set: {
        address: "$attrib.address",
        phone: "$attrib.phone"
    }},
    {$unset: "attrib"}
  ],
  {multi: true}
)

playground - literal

While the generic option, if you don't wan to specify all inner field, can be:

db.collection.update({},
[
  {$replaceRoot: {newRoot: {$mergeObjects: ["$$ROOT", "$attrib"]}}},
  {$unset: "attrib"}
],
{multi: true})

See how it works on the playground example

For older versions you can simply use $rename/$set and $unset:

($rename is just a combination of $set and $unset)

db.collection.update({},
{
  $rename: {
    "attrib.address": "address",
    "attrib.phone": "phone"
  }
}, {multi: true})

As you can see here

And later:

db.collection.update({}, {$unset: {attrib: ""}}, {multi: true})

As you can see here

Comments

2

If you are intending to change all the documents in the database, then neither the Aggregation Framework or Map/Reduce are they way to go. You instead write a script in your favourite language and loop over all the documents in the collection to modify them one by one.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.