Practical Lab: Build a BSON Type Audit Dataset

This lab combines the chapter's most important type concepts in a single dataset. Use a dedicated development database and collection.

Step 1: Insert deliberately varied documents

use mongodb_course_lab

db.bsonLab.insertMany([
 {
 name: "Arun",
 age: NumberInt(28),
 salary: NumberDecimal("55000.50"),
 active: true,
 skills: ["Node.js", "MongoDB"],
 address: { city: "Chennai" },
 createdAt: ISODate("2026-10-01T10:00:00Z")
 },
 {
 name: "Meera",
 age: NumberLong("35"),
 salary: NumberDecimal("72000.00"),
 active: false,
 skills: ["React", "TypeScript"],
 address: { city: "Bengaluru" },
 createdAt: ISODate("2026-10-05T12:00:00Z")
 },
 {
 name: "Ravi",
 age: "30",
 salary: "60000.00",
 active: "true",
 skills: ["JavaScript"],
 createdAt: "2026-10-10T09:00:00Z"
 },
 {
 name: "Kavitha",
 age: NumberInt(42),
 salary: null,
 active: true,
 skills: [],
 createdAt: null
 },
 {
 name: "Divya",
 age: NumberInt(26),
 salary: NumberDecimal("48000.00"),
 active: true,
 skills: ["MongoDB", "Node.js"],
 createdAt: ISODate("2026-10-15T09:30:00Z")
 }
]);

The collection intentionally includes numeric strings, a Boolean string, a date string, explicit null values, and missing fields.

Step 2: Inspect the documents

db.bsonLab.find();

Identify fields whose types differ across documents.

Step 3: Inspect field types using aggregation

db.bsonLab.aggregate([
 {
 $project: {
 name: 1,
 ageType: { $type: "$age" },
 salaryType: { $type: "$salary" },
 activeType: { $type: "$active" },
 createdAtType: { $type: "$createdAt" }
 }
 }
]);

Notice the difference between missing, null, string, int, long, decimal, bool, and date where applicable.

Step 4: Find incorrectly typed ages

db.bsonLab.find({
 age: { $type: "string" }
});

This identifies documents whose age value is a string.

Step 5: Find numeric salary values

db.bsonLab.aggregate([
 {
 $project: {
 name: 1,
 salary: 1,
 salaryIsNumber: { $isNumber: "$salary" }
 }
 }
]);

Use this output to identify documents with numeric salary values and documents whose salary is null or a string.

Step 6: Convert values in a read-only pipeline

db.bsonLab.aggregate([
 {
 $project: {
 name: 1,
 normalizedSalary: {
 $convert: {
 input: "$salary",
 to: "decimal",
 onError: null,
 onNull: null
 }
 }
 }
 }
]);

This illustrates how conversion could help normalize values. It does not modify the stored documents.

Step 7: Query a nested field

db.bsonLab.find({ "address.city": "Chennai" });

This returns documents whose nested city field matches Chennai.

Step 8: Query a date range

db.bsonLab.find({
 createdAt: {
 $gte: ISODate("2026-10-01T00:00:00Z"),
 $lt: ISODate("2026-10-16T00:00:00Z")
 }
});

Only BSON Date values in the requested interval match this date range. A string that looks like a date is not treated as a BSON Date.

Step 9: Explore the array field

db.bsonLab.find({ skills: "MongoDB" });

This matches documents whose skills array contains MongoDB.

Step 10: Explain the data-quality risks

Write down the problems caused by inconsistent types:

  • Numeric range queries may not include numeric strings.
  • Boolean filters do not treat "true" as Boolean true.
  • Date range queries do not automatically interpret date strings as BSON Dates.
  • Null and missing values may have different business meanings.
  • Aggregation and arithmetic can fail or produce unexpected results when types are inconsistent.

Lab challenge

Design a migration plan that converts age to a numeric type, salary to Decimal128, and createdAt to BSON Date. Your plan should identify invalid values, avoid overwriting data without inspection, test conversion results, and apply schema validation after the data is normalized.

Audit a collection's field types
db.bsonLab.aggregate([
 {
 $project: {
 name: 1,
 ageType: { $type: "$age" },
 salaryType: { $type: "$salary" },
 activeType: { $type: "$active" },
 createdAtType: { $type: "$createdAt" },
 skillsType: { $type: "$skills" },
 salaryIsNumber: { $isNumber: "$salary" }
 }
 }
]);
💡 This aggregation produces a per-document type report. It is a useful starting point for data-quality investigations and migration planning. Inspect the results before attempting to modify the stored records.
Key Takeaways
  • A single collection can contain inconsistent field types unless appropriate controls prevent them.
  • The aggregation $type expression is useful for data audits.
  • Numeric strings, Boolean strings, and date strings are not equivalent to their BSON counterparts.
  • A read-only conversion pipeline allows you to inspect normalization results before modifying data.
  • Data migrations should be tested, observable, and followed by validation.
Knowledge Check

In the lab dataset, why does the date range query not match Ravi's `createdAt` value?