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

  1. Profile the current field types.
  2. Identify valid and invalid values.
  3. Decide the target BSON type.
  4. Test conversions in a read-only aggregation.
  5. Count conversion failures.
  6. Back up or otherwise preserve the data according to your recovery requirements.
  7. Perform a controlled update or migration.
  8. Validate the resulting documents.
  9. Update the application and validation rules to prevent recurrence.

Do not treat type conversion as a substitute for defining a correct data contract.

Conversion can lose information

Converting to a narrower numeric type can overflow or lose precision. Test conversions before writing changes, especially when handling money, identifiers, and historical timestamps.

Convert inconsistent price values safely
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" }
 }
 }
]);
💡 The pipeline attempts to convert each price to Decimal128. Valid numeric values can be converted, while the invalid string and missing value produce null under the configured error-handling policy. In production, separately report or record conversion failures so invalid data is not silently discarded.
Key Takeaways
  • MongoDB provides conversion operators for common BSON types.
  • $convert supports 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.
Knowledge Check

Why might `$convert` be preferable to `$toDecimal` during a migration with inconsistent historical data?