Converting BSON Types Safely
Applications frequently inherit inconsistent data. A numeric field may contain numbers in some documents and strings in others. A date may be represented as text in older records. MongoDB's aggregation framework provides type-conversion operators that can help normalize such data.
Common conversion operators
$toInt$toLong$toDouble$toDecimal$toString$toDate$toObjectId$convert
Conversion support depends on the source type and the target type. Not every conversion is valid, and some conversions can lose information.
Example: Convert a numeric string
db.legacyProducts.aggregate([
{
$project: {
name: 1,
numericPrice: { $toDouble: "$price" }
}
}
]);
If price contains a convertible numeric string, the expression can produce a Double value. If conversion fails, $toDouble can cause the aggregation to fail.
Using $convert for controlled failure handling
db.legacyProducts.aggregate([
{
$project: {
name: 1,
numericPrice: {
$convert: {
input: "$price",
to: "decimal",
onError: null,
onNull: null
}
}
}
}
]);
This example converts the input to Decimal128 and returns null if conversion fails or the input is null or missing. That makes the pipeline more tolerant, but it can also hide data-quality problems if invalid values are not tracked separately.
Converting strings to ObjectId
db.legacyUsers.aggregate([
{
$project: {
originalId: "$legacyId",
convertedId: { $toObjectId: "$legacyId" }
}
}
]);
This works only when the source values can be converted to valid ObjectIds. Invalid hexadecimal strings cause conversion errors unless you use $convert with an appropriate onError policy.
Converting text to dates
db.legacyEvents.aggregate([
{
$project: {
parsedDate: { $toDate: "$dateText" }
}
}
]);
Date conversion depends on the input format and supported conversion rules. Ambiguous local dates should be normalized deliberately instead of relying on an unspecified interpretation.
A safe migration workflow
- Profile the current field types.
- Identify valid and invalid values.
- Decide the target BSON type.
- Test conversions in a read-only aggregation.
- Count conversion failures.
- Back up or otherwise preserve the data according to your recovery requirements.
- Perform a controlled update or migration.
- Validate the resulting documents.
- Update the application and validation rules to prevent recurrence.
Do not treat type conversion as a substitute for defining a correct data contract.
Converting to a narrower numeric type can overflow or lose precision. Test conversions before writing changes, especially when handling money, identifiers, and historical timestamps.
db.legacyProducts.insertMany([
{ name: "A", price: "19.99" },
{ name: "B", price: 29.99 },
{ name: "C", price: "not-a-number" },
{ name: "D" }
]);
db.legacyProducts.aggregate([
{
$project: {
name: 1,
originalPrice: "$price",
normalizedPrice: {
$convert: {
input: "$price",
to: "decimal",
onError: null,
onNull: null
}
},
originalType: { $type: "$price" }
}
}
]);- MongoDB provides conversion operators for common BSON types.
$convertsupports explicit error and null handling.- Conversions may fail or lose precision.
- Test conversions before updating stored documents.
- A successful migration should include validation and prevention of future inconsistent writes.
Why might `$convert` be preferable to `$toDecimal` during a migration with inconsistent historical data?