Chapter 02 · BSON, Documents, Collections, Flexible Schema, and Data Types
Inspect Documents and Collection Metadata with mongosh and Database Commands
Use mongosh and database commands to audit the server’s actual document types, sizes, collection options, indexes, and validation/storage metadata.
Learning outcomes
The chapter ends with an inspection workflow. AtlasMart receives a bug report: some product prices are decimals, one legacy row is a double, one seller field is missing, and the team is unsure whether the collection validator currently running in production matches the configuration in source control. Rather than guessing from application models, use mongosh and database commands to inspect the documents and collection metadata that the server actually sees.
Inspect documents with Extended JSON so BSON-specific values remain visible.
Use $type, $bsonSize, and
field-existence checks to profile actual document shapes.
Inspect collection options/validators with
listCollections or collection-info helpers.
Use dbStats, collStats, indexes,
and validate as evidence while respecting their
scope/cost.
Build a compact AtlasMart “document contract report” before later CRUD/modeling/index chapters modify the collection.
Mandatory server examples use a disposable loopback-only
standalone
mongodb/mongodb-community-server:8.3.8-ubuntu2204-slim. Driver examples pin pymongo==4.17.0. The lab
is intentionally unauthenticated only because it is
short-lived and published to 127.0.0.1; Chapter
01 already demonstrated the authenticated reusable lab. Do not
expose this container on an untrusted interface. Atlas is
optional and not required.
The build environment does not provide Docker, mongod, or mongosh, and cannot install PyMongo from the network. Product/driver commands were reviewed against current official documentation but were not executed here. Expected-output blocks describe stable evidence shape, not fabricated captured output. Learners should record their actual versions and outputs.
1. Seed a fixture that intentionally contains multiple valid shapes
A useful inspection lab needs variation. The fixture below contains an ObjectId, Decimal128 and legacy double prices, dates, arrays, embedded seller data, explicit null, and a missing field. This is controlled heterogeneity, not random data. Tag every lab document so cleanup cannot accidentally delete unrelated AtlasMart records.
docker rm -f atlasmart-mongo-ch02-l5 2>/dev/null || truedocker run --name atlasmart-mongo-ch02-l5 -p 127.0.0.1:27026:27017 -d mongodb/mongodb-community-server:8.3.8-ubuntu2204-slim
mongosh "mongodb://127.0.0.1:27026/atlasmart?directConnection=true" --quiet --eval 'db.inspect_products.drop();db.createCollection("inspect_products", {validator:{$jsonSchema:{ bsonType:"object", required:["lab","sku","schemaVersion"], properties:{lab:{bsonType:"string"},sku:{bsonType:"string"},schemaVersion:{bsonType:"int"}}}}});db.inspect_products.insertMany([ {lab:"ch02",schemaVersion:Int32(2),sku:"sku-laptop",price:Decimal128("899.00"),createdAt:ISODate("2026-09-02T05:00:00Z"),tags:["usb-c","portable"],seller:{id:"s-7",rating:Decimal128("4.80")}}, {lab:"ch02",schemaVersion:Int32(1),sku:"sku-legacy",price:18.5,createdAt:ISODate("2025-12-01T10:00:00Z"),tags:["legacy"],seller:null}, {lab:"ch02",schemaVersion:Int32(2),sku:"sku-missing-seller",price:Decimal128("35.00"),createdAt:ISODate("2026-08-30T10:00:00Z"),tags:[]}]);print("seeded", db.inspect_products.countDocuments({lab:"ch02"}));'
2. Display documents without erasing their BSON types
find()/findOne() in mongosh already
use BSON-aware display forms, but when copying evidence into
logs/tests use Extended JSON deliberately. Canonical EJSON is
particularly useful when you need to see whether a numeric value
is an Int32, Int64, double, or Decimal128 rather than relying on
visual formatting.
const docs = db.inspect_products.find({lab:"ch02"}).sort({sku:1}).toArray();print(EJSON.stringify(docs, null, 2, {relaxed:false}));
Store this report only in a safe test environment; real production documents may contain personal or secret data. Observability must respect data classification and redaction policies.
3. Profile types, missing fields, and BSON sizes
Aggregation expressions turn an inspection question into
reproducible evidence. $type reports the BSON type;
when an aggregation expression references a missing field, it
reports "missing". $bsonSize reports
BSON bytes for a document. Together they expose exactly the
drift this chapter has taught.
db.inspect_products.aggregate([ {$match:{lab:"ch02"}}, {$project:{ _id:0, sku:1, schemaVersion:1, priceType:{$type:"$price"}, sellerType:{$type:"$seller"}, tagType:{$type:"$tags"}, docBytes:{$bsonSize:"$$ROOT"} }}, {$sort:{sku:1}}]).toArray();print("seller explicit/missing together:", db.inspect_products.countDocuments({seller:null}));print("seller explicit null only:", db.inspect_products.countDocuments({seller:{$type:10}}));print("seller missing only:", db.inspect_products.countDocuments({seller:{$exists:false}}));
Expected shape: sku-laptop and
sku-missing-seller report price type
decimal; sku-legacy reports
double. Seller type is object,
null, or missing. The exact byte
counts differ by generated ObjectIds and field values; compare
distributions, not a hard-coded screenshot number.
4. Inspect collection metadata—not just documents
A collection carries state beyond its documents: options such as
validators, indexes, UUID/catalog identity, and storage
statistics. Use official database commands when you need stable
machine-readable evidence. listCollections can show
the validator/options the server actually has;
getIndexes() shows maintained index definitions;
collStats/dbStats report current
storage/catalog metrics; validate checks
collection/index structural consistency and can be more
expensive, especially with full validation, so treat it as an
operational command rather than a harmless UI refresh.
const info = db.runCommand({listCollections:1, filter:{name:"inspect_products"}});printjson(info.cursor.firstBatch[0]);printjson(db.inspect_products.getIndexes());printjson(db.runCommand({collStats:"inspect_products", scale:1}));printjson(db.runCommand({dbStats:1, scale:1}));// Disposable lab only: structural validation evidence.printjson(db.runCommand({validate:"inspect_products", full:false}));
Interpret each command narrowly. A successful
validate does not prove application semantics are
correct; it checks storage/index consistency according to the
server's validation logic. collStats does not prove
an index is useful. A validator in metadata does not prove every
historical document matches it. Evidence is only meaningful when
its scope is stated.
5. Build a document-contract report before changing the collection
Before Chapter 03 starts CRUD work, capture a compact baseline: server version/FCV, collection options, indexes, counts by schemaVersion, counts by BSON type for critical fields, missing/null counts, and BSON-size percentiles or at least min/max/sample values. This report creates a before-state for later migrations and performance work.
// Small deterministic lab report; for huge production collections, sample/aggregate carefully.printjson({ server: db.serverBuildInfo().version, fcv: db.adminCommand({getParameter:1,featureCompatibilityVersion:1}).featureCompatibilityVersion, docs: db.inspect_products.countDocuments({lab:"ch02"}), schemaVersions: db.inspect_products.aggregate([ {$match:{lab:"ch02"}}, {$group:{_id:"$schemaVersion",n:{$sum:1}}}, {$sort:{_id:1}} ]).toArray(), priceTypes: db.inspect_products.aggregate([ {$match:{lab:"ch02"}}, {$group:{_id:{$type:"$price"},n:{$sum:1}}}, {$sort:{_id:1}} ]).toArray()});
The deliberately wrong approach is to run a one-off
find(), visually inspect three documents, and
declare the schema understood. Sampling can miss rare oversized
documents, old schema versions, or type drift. In production,
use bounded/scalable aggregation strategies, metrics, backups,
and privacy-aware sampling rather than dumping entire
collections to logs.
Chapter 03 now builds CRUD operations on this exact mental model: inserts create BSON documents and identities; finds operate on typed fields; projection/sort/limit change response shape and work; deletes must be scoped and verified.
Cleanup
mongosh "mongodb://127.0.0.1:27026/atlasmart?directConnection=true" --quiet --eval 'db.inspect_products.deleteMany({lab:"ch02"})'docker rm -f atlasmart-mongo-ch02-l5
Verification checklist
- You can export Canonical Extended JSON without silently stringifying BSON-specific values.
- You can identify type drift with
$type. - You can distinguish explicit null from missing with type/existence predicates.
- You can measure BSON bytes and inspect collection options/indexes.
- You state what each command proves and what it does not prove.
Check your understanding
- Why prefer Canonical EJSON for a type-audit report?
- What does aggregation $type return for a missing field?
- What does $bsonSize measure?
- What does listCollections add beyond find()?
- Does validate:true prove the schema/business rules are correct?
Review the answers
It exposes BSON type wrappers explicitly, making distinctions such as Decimal128 versus double and Int64 versus other numeric forms visible.
The string "missing", which is useful for
separating absent fields from explicit BSON null.
The number of bytes the supplied object uses when encoded as BSON. It is not the size of pretty JSON text or total collection storage including indexes/compression.
It exposes collection metadata/options such as validation configuration and catalog information that are not stored as ordinary user documents.
No. Collection validation checks storage/index structural consistency. Business/schema correctness requires validators, application tests, type/shape analysis, and domain invariants.
Authoritative references
- MongoDB release notes — Official current stable server series and patch notes.
- MongoDB 8.3 release notes — Official 8.3 changes; 8.3.8 is the latest released patch at review time and 8.3.9 is upcoming.
- MongoDB Extended JSON v2 — Canonical and Relaxed Extended JSON representations and type-preservation rules.
- BSON types — Official BSON type definitions and ObjectId notes.
- PyMongo BSON data formats — Official Python driver mapping between Python dictionaries/types and BSON.
- $type aggregation expression — Official type inspection including missing fields.
- $bsonSize — Official encoded-document byte measurement.
- listCollections — Official collection metadata command.
- collStats — Official collection statistics command.
- validate — Official collection/index structural validation command and operational considerations.