Convert ObjectId to String and vice versa inside MongoDB aggregate map

Mikhail picture Mikhail · Apr 4, 2017 · Viewed 7.1k times · Source

I have the following document in a collection:

Input:

{
    "_id" : ObjectId("***"),
    "oldItems" : [ 
        {
            "_id" : ObjectId("***"),
            "name" : "ItemId***",
            "nestedItemsToExtract" : { }
        }
    ]
}

I need to iterate through the oldItems array and create a resultant output document with another array, whose values should be mapped from the original one.

Output:

{
    "_id" : ObjectId("***"),
    "newItems" : [ 
        {
            "oldItemId" : "***", // String Value Of Parent's / Mapped Item Id aka ObjectId.toString()
            "_id" : ObjectId("***") // New ObjectId Here aka ObjectId()
        }
    ]
}

How to convert the mapped (old) _id ObjectId value to a String, but generate a new ObjectId for the (new) _id property in the resultant array?

Edit:

I have managed to find a workaround for generating a new ObjectId value via the "{ $literal: ObjectId() }" expression. I have corrected my "Output" code snippet:

use DB_NAME_HERE

db.getCollection('COLLECTION_NAME_HERE').aggregate([
    { $match: {} },
    { $project: {
        newItems: {
            $map: {
                input: '$oldItems',
                as: 'oldItem',
                in: {
                    oldItemId: '$$oldItem._id' // Returns ObjectId
                    _id: ObjectId() // Fails With 'disallowed field type OID in object expression (at '_id')"'
                    //Edit
                    //_id: { $literal: ObjectId() } // Works
                }
            }
        }       
    }
}])

Answer

Filipp Shestakov picture Filipp Shestakov · Apr 5, 2017

You can not do type conversion from ObjectId to string(or vice vesa) in an aggregation pipe-line in the current version of Mongodb(3.4). There are some issues on Mongodb official bug tracker pointing to that problem:

SERVER-11400: Need a type conversion mechanism to convert between strings and numbers

SERVER-22781: Allow $lookup between ObjectId (_id.str) and string

SERVER-24947: Need a type conversion mechanism for booleans, ISODates, ObjectID

As for an error during ObjectId generation - try this:

use DB_NAME_HERE

db.getCollection('COLLECTION_NAME_HERE').aggregate([
    { $match: {} },
    { $project: {
        newItems: {
            $map: {
                input: '$oldItems',
                as: 'oldItem',
                in: {
                    oldItemId: '$$oldItem._id', // Returns ObjectId
                    _id: { $literal: ObjectId() }
                }
            }
        }       
    }
}])