MongoDB change or update field type

Many a times developer do ask a common question that, we need to change existing data bearing field’s data type to another data type with out hampering existing data. Such as example, in database date field was in string value, but need to convert it in ISODate() format. Before going to nitty gritty details of the MongoDB data types, one need to know- How many BSON type are there in MongoDB? and What is $type, and what does it do?

In this aspect we do have two expectations,
1 ) While fetching you need to convert the data, no need to update inside collection. OR
2 )Need to update those field data type in the collection and update without loss or tampering the data.

For the Scenario #1 You can use following operators to fetch your data in a particular data type.

$convert$toBool$toDate
$toInt$toDecimal$toDouble
$toLong$toObjectId$toString
This are all aggregation operator, can be used in $project, $group stage to fetch the details.

For the Scenario #2 , that means you need to do update field data type in the existing database, in existing collection with out hampering existing data. For this scenario, I will give you following few Example by which you can probably understood, how to do this.

MongoDB change field type from array to null

Please check the image, here requirement is nested array object one of the field is in array and nee to update this field to null.

Here BSON Type array is 4 check the following code for updating the data in fewer number of count of filter criteria.

db.collection_name.update(
    {
        userAssets:{
            $elemMatch:{
                entitlements:{
                    $elemMatch:{
                        action:{ $exists:true, $type: 4 }
                    }
                }
            }
        }
    },
    { $set: { "userAssets.$[].entitlements.$[etl].action" : null } },
    { arrayFilters: [ { "etl.action": { $type: 4 } } ], multi: true}
)

When you are dealing with huge data, and want to update millions of data then you make batches to update it. After checking count you probably understood what should be your batch size and how may times the script will run. Check the following code. Here I am updating 100 millions data but making 50K batch for update single time.

db.collection_name.find(
    {
        userAssets:{
            $elemMatch:{
                entitlements:{
                    $elemMatch:{
                        action:{ $exists:true, $type: 4 }
                    }
                }
            }
        }
    },
    {
        _id:1
    }
).limit(50000).forEach(function(doc){
    db.user_info.update(
        {
            _id:doc._id
        },
        { $set: { "userAssets.$[].entitlements.$[etl].action" : null } },
        { arrayFilters: [ { "etl.action": { $type: 4 } } ], multi: true}
    )
})

MongoDB change field type from string to date

updatedDate- was string, after update it will become ISODate().
db.sampleCollection.find(
    {
        updatedDate:{
            $exists: true,
            $type: 2
        }
    },
    {
        updatedDate:1
    }
).forEach(function(doc){
    var date=doc.updatedDate;
 // Saving each original date value
    db.sampleCollection.update(
        { _id:doc._id },
        {
            $set:{
                updatedDate:ISODate(date)
 // Updating date type
            }
        }
    )
})

$type : 2 is the string filter and ISODate() is the wrapper, you can use new Date() also, result will be the same for both of this.

MongoDB change field type from Double to Int32 integer

MongoDB bydefault store Double in place of integer if you do not define anything, if you want to change it to int32 then use NumberInt() method.
db.sampleCollection.find(
    {
        value:{
            $exists: true,
            $type: 1
        }
    },
    {
        value:1
    }
).forEach(function(doc){
    var data=doc.value;
    db.sampleCollection.update(
        { _id:doc._id },
        {
            $set:{
                value:NumberInt(data)
            }
        }
    )
})

$type : 1 is the Double filter and NumberInt() is the wrapper result will be the convert Double to int32.

MongoDB change field type from Int32 to Int64 integer

Trying to convert int32 to int64
db.sampleCollection.find(
    {
        value:{
            $exists: true,
            $type: 16
        }
    },
    {
        value:1
    }
).forEach(function(doc){
    var data=doc.value;
    db.sampleCollection.update(
        { _id:doc._id },
        {
            $set:{
                value:NumberLong(data)
            }
        }
    )
})

$type : 16 is the Int32 filter and NumberLong() is the wrapper result will be the convert Double to int64.

MongoDB change field type from Int64 to NumberDecimal() or Decimal128 integer

This NumberDecimal() or Decimal128 data type is very much required for the project like financial institutes. Financial data should be keep in Decmal128 datatype to avoid decimal related discrepancy.

db.sampleCollection.find(
    {
        value:{
            $exists: true,
            $type: 18
        }
    },
    {
        value:1
    }
).forEach(function(doc){
    var data=doc.value;
    db.sampleCollection.update(
        { _id:doc._id },
        {
            $set:{
                value:NumberDecimal(data)
            }
        }
    )
})

$type : 18 is the Int64 filter and NumberDecimal() is the wrapper result will be the convert BigInt to Decimal128.

MongoDB removing fields having empty string

Need to remove fields which have empty string
db.sampleCollection.find(
    {
        concent:{
            $exists: true,
            $type: 2
        },
        $expr: { $eq: [ { "$strLenCP": "$concent" }, 0 ] } 
// Checking empty length of string
    },
    {
        concent:1
    }
).forEach(function(doc){
    var data=doc.concent;
    printjson(data);
    db.sampleCollection.update(
        { _id:doc._id },
        {
            $unset:{
                concent:1
            }
        }
    )
})

Conclusion

This is how we can change the type of the field, either by only fetching by aggregation query, or we can update in the data into the database. For millions of data updating need to update data batch wise. Use limit() clause after find() and make the batch checking the count of the field. Example: ).limit(50000).forEach(function(doc){ this is 50K batch.