'Mongo convert embedded document to array

Is there a way to convert a nested document structure into an array? Below is an example:

Input

"experience" : {
        "0" : {
            "duration" : "3 months",
            "end" : "August 2012",
            "organization" : {
                "0" : {
                    "name" : "Bank of China",
                    "profile_url" : "http://www.linkedin.com/company/13801"
                }
            },
            "start" : "June 2012",
            "title" : "Intern Analyst"
        }
    },

Expected Output:

"experience" : [
           {
            "duration" : "3 months",
            "end" : "August 2012",
            "organization" : {
                "0" : {
                    "name" : "Bank of China",
                    "profile_url" : "http://www.linkedin.com/company/13801"
                }
            },
            "start" : "June 2012",
            "title" : "Intern Analyst"
        }
    ],

Currently I am using a script to iterate over each element, convert them to an array & finally update the document. But it is taking a lot of time, is there a better way of doing this?



Solution 1:[1]

For mongoDB version >4.2 :

db.doc.aggregate([{ $match: {'experience.0': { $exists: false } } },
    {$project:{experience:["$experience.0"]}}, { $merge: { into: "doc", on: "_id" }
])

Note : Here we're merging the updated field/document with existing, but not replacing/updating entire document, default behavior of $merge is merge whenMatched document is found, You can pass other options like replace/keepExisting etc.

Ref: $merge

Solution 2:[2]

See if this query works with your MongoDB version

For MongoDB version 3.2+:

db.doc.aggregate([
    {$project:{experience:["$experience.0"]}}
])

MongoDB < 3.2:

db.doc.aggregate([
    {$group: {_id:"$_id", experience:{$push:"$experience.0"}}}
])

It should transform your document into:

{ 
    "_id" : ObjectId("56f1b046a65ea8a72c34839c"), 
    "experience" : [
        {
            "duration" : "3 months", 
            "end" : "August 2012", 
            "organization" : {
                "0" : {
                    "name" : "Bank of China", 
                    "profile_url" : "http://www.linkedin.com/company/13801"
                }
            }, 
            "start" : "June 2012", 
            "title" : "Intern Analyst"
        }
    ]
}

A better approach if you want to alter documents in collection permanently using aggregation framework.

Lets assume your collection name is doc

db.doc.aggregate([
    {$group: {_id:"$_id", experience:{$push:"$experience.0"}}},
    {$out: "doc"}
])

Query above will transform all of your documents in place.

Solution 3:[3]

I am not sure, why aren't there any good answers yet.

It's super easy with aggregation "$set", set is used to add a new field. here you can add a new field with same name into an array. So it will override the older field.

Refer below example:

db.collectionName.aggregate[
   // match/other aggregations
   {$set: { "experience": ["$experience"] } }
];

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 whoami - fakeFaceTrueSoul
Solution 2 Cerbrus
Solution 3 Ravi Soni