Skip to content
Advertisement

MongoDB attribute to array migration script

I’m trying to make a migration for a database, which has the following structure as of now:

{
    "site" : {
        "name" : "siteName1"
    },
    "subStages" : [ 
        "subStage1", 
        "subStage2"    
    ]
}

There have been some changes in how we need to handle a site, having now the possibility to have multiple sites, and not only one.

So I need to change the DB to look like this:

{

"sites" : [ 
    {
        "name" : "siteName1",
        "subStages" : [ 
            "subStage1", 
            "subStage2"
        ]
    }, 
    {
        "name" : "siteName2"
    }
]
}

Is there a way to change the schema with a simple migration script to have each site inside a ‘sites’ array?

Any help would be appreciated.

I can share more information if needed.

EDIT:

The structure where the site is an object, inside nested objects as follows:

Object1: {
   stages: [
       {
           stage1: {
                       attributes
            }
         .
         .
         .
            stageINeed: {
       add sites --->     sites: [
                               ]
            }
}

How can I access this specific stage I need?

EDIT 2: Full structure:

The collection ‘tickets’ will have numerous entries of tickets, whose structure for all needs to be changed to what we talked about before. The specific structure is as follows:

{
    "_id" : ObjectId("62bf0b8b4feee27711d65e0f"),
    "files" : [],
    "stages" : [
        {
            "name" : "Documents",
            "updatedAt" : ISODate("2022-07-01T14:58:19.108Z"),
            "state" : "none"
        },
        .
        .
        .
        {
            "name" : "Registry",
            "state" : "none",
            "subStages" : [ 
                {
                    "name" : "name1",
                    "completed" : false
                }, 
                {
                    "name" : "name2",
                    "completed" : false
                }, 
                {
                    "name" : "name3",
                    "completed" : false
                }
            ],
            "updatedAt" : ISODate("2022-07-01T14:58:19.108Z"),
        }
    ]
}

The structure I want to map is the following:

{
    "_id" : ObjectId("62e9219dfacb4b96823af822"),
    "files" : [],
    "stages" : [
        {
            "name" : "Documents",
            "updatedAt" : ISODate("2022-07-01T14:58:19.108Z"),
            "state" : "none"
        },
        .
        .
        .

        {
            "name" : "Registry",
            "state" : "none",
            "sites" : [ 
                {
                    "name" : "site1",
                    "completed" : false,
                    "status" : "none",
                    "subStages" : [ 
                        {
                            "name" : "name1",
                            "completed" : false
                        }, 
                        {
                            "name" : "name2",
                            "completed" : false
                        }, 
                        {
                            "name" : "name3",
                            "completed" : false
                        }
                    ]
                }
            ],
            "updatedAt" : ISODate("2022-08-02T13:07:41.688Z"),
        }
    ],
}

This is because future tickets will have two sites, so I want to change the structure of the existing ones to a sites configuration. Now, inside the ticket I don’t have anything that says ‘site’, I get everything from another database called configuration.

So far I have the following, but I don’t know if it’s correct:

const configSite= config.find(stage => stage.type === "Registry")?.site;
const tickets = await this.tickets.updateMany(
    { "stages.type": "Registry" },
    [
        {
            $replaceRoot: {
                newRoot: {
                    name: "$stages.name",
                    state: "$stages.state",
                    updatedAt: "$stages.updatedAt",
                    type: "$stages.type",
                    sites: [
                        {
                            name: configSite.name,
                            subStages: configSite.subStages
                        },
                    ],
                },
            },
        },
    ],
    {
        multi: false,
        upsert: false,
    }
);
return tickets;

Advertisement

Answer

Yes, you can do this in a single update using pipelined updates and $replaceRoot, like so:

db.collection.updateMany({},
[
  {
    $replaceRoot: {
      newRoot: {
        _id: "$_id",
        sites: [
          {
            name: "$site.name",
            substages: "$subStages"
          }
        ]
      }
    }
  }
])

Mongo Playground


EDIT

Here’s how to do it for the full structure you provided, I had to make some assumptions as some things weren’t exactly clear. For example a “stage” that has the “substages” field will be converted, others will not per you sample.

db.collection.updateMany({},
[
  {
    $set: {
      stages: {
        $map: {
          input: {
            $ifNull: [
              "$stages",
              []
            ]
          },
          in: {
            $cond: [
              "$$this.subStages",
              {
                name: "$$this.name",
                state: "$$this.state",
                sites: [
                  {
                    //where should this come from,
                    status: "$$this.state",
                    completed: {
                      $allElementsTrue: "$$this.subStages.completed"
                    },
                    //where should this come from,
                    name: "site1",
                    subStages: "$$this.subStages"
                  }
                ]
              },
              "$$this"
            ]
          }
        }
      }
    }
  }
])

Mongo Playground

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement