Set _id to a new UUID using $addFields

Viewed 1163

Well, someone done goofed. Wasn't me, but I get to fix it. What I need to do, is replace the _id of each and every document in the collection with a new UUID.

My plan was aggregating the entire collection, use $addFields to replace the _id, then merge it back into itself, replacing the records. Fortunately, there is a secondary (composite) key I can use for merging, so I'm good there — I think, because I haven't gotten there yet.

The problem I face is generating a unique (that's kinda the point of it) UUID during the $addFields stage.

db.foo.agregate([
    {
        $addFields: {
            // I really just need the UUID string itself, hence the split
            '_id': UUID().toString().split('"')[1]
        }
    }
    // here's where the merge would go
])

The problem here is that UUID() is called only once, so I get all the same IDs. That's not quite what I want.

Now the tedious way would be to do this one by one, from a loop. But is there a way to do this from within an aggregation pipeline in one go?


Now I found this question asking basically the same question, but it was solved by not using an UUID, using sequential keys instead, so it really isn't an answer to my question.

2 Answers

So this answer is just to generate unique UUIDs by calling UUID() from a $function. Try this:

db.collectionName.aggregate([
    {
        $addFields: {
            _id: {
                $function: {
                    body: function() {
                        return UUID().toString().split('"')[1];
                    },
                    args: [],
                    lang: "js"
                }
            }
        }
    }
])

btw, its only works in MongoDB version >= 4.4. Output:

/* 1 */
{
    "_id" : "5dc9316a-50a8-4013-8090-06fc66cdce9f",
    "dummy" : "data"
},

/* 2 */
{
    "_id" : "062ebc8f-4455-4a81-a9e0-34f6c7132cab",
    "dummy" : "data"
},

/* 3 */
{
    "_id" : "94eef4b8-9c0e-4910-a1cb-58443a63a036",
    "dummy" : "data"
}

If what you need to do is

What I need to do, is replace the _id of each and every document in the collection with a new UUID.

... and you can use ObjectIds instead of UUIDs, then:

  • Project out the _id
  • $out into a new collection

The server should generate new _ids for each document during the insert.

Then:

  • Drop the original collection
  • Rename the new collection into the original name
Related