Keywords: MongoDB | document size | BSON | Object.bsonsize | findOne
Abstract: This technical article provides an in-depth examination of accurately determining the size of individual documents in MongoDB. By analyzing the discrepancies between the Object.bsonsize() and db.collection.stats() methods, it identifies common misuse scenarios and presents effective solutions. The article explains why applying bsonsize directly to find() results returns cursor size rather than document size, and demonstrates the correct implementation using findOne(). Additionally, it covers supplementary approaches including the $bsonSize aggregation operator in MongoDB 4.4+ and scripting methods for batch document size analysis. Important concepts such as the 16MB document size limit are also discussed, offering comprehensive technical guidance for developers.
Introduction
In MongoDB development, accurately determining document size is crucial for performance optimization, storage planning, and data migration. However, developers often encounter unexpected discrepancies when attempting to measure individual document sizes, typically due to misunderstandings of MongoDB's internal mechanisms and API usage. This article analyzes the root causes of these differences through concrete examples and provides reliable solutions.
Problem Scenario and Phenomenon Analysis
Consider a typical test scenario: creating a database named "test" and inserting a simple document containing only a type field:
db.test.insert({type:"auto"})
Developers commonly attempt two approaches to obtain document size:
- Using the
db.collection.stats()method, which returns collection statistics including theavgObjSize(average object size) field. - Using the
Object.bsonsize()function, a JavaScript method that should theoretically return the BSON size of a document in bytes.
With a single document in the test collection, these methods return different values:
db.test.stats()showsavgObjSize: 40Object.bsonsize(db.test.find({type:"auto"}))returns481
This discrepancy raises fundamental questions: Why does the same document have two different size values? Which method is correct?
Root Cause: Confusion Between Cursors and Documents
The key issue is that db.test.find() returns a cursor object, not a single document. When Object.bsonsize() is applied to a cursor, MongoDB calculates the BSON representation size of the entire cursor object, which includes metadata, query result set information, and other overhead. Consequently, the returned value (481 bytes) is significantly larger than the actual document size.
To obtain the correct size of an individual document, one must first retrieve the document object itself. The proper approach is to use the findOne() method, which directly returns a single document object:
Object.bsonsize(db.test.findOne({type:"auto"}))
This method returns the actual BSON size of the document, aligning with the avgObjSize value from db.test.stats() (in single-document cases).
Technical Details: BSON Size Calculation Principles
BSON (Binary JSON) is the binary document storage format used by MongoDB. The BSON size of a document includes:
- Encoded lengths of field names and values
- Data type identifiers
- Terminators
- Length prefix of the document itself
For the example document {type:"auto"}, its BSON structure roughly consists of:
Total document length (4 bytes)
Field name "type" (with type identifier and terminator)
String value "auto" (with length prefix and content)
Document terminator (1 byte)
The calculated 40 bytes represents the sum of these components, reflecting the exact storage requirements on disk.
Supplementary Methods: Alternative Approaches in Modern MongoDB Versions
For MongoDB 4.4 and later, the $bsonSize aggregation operator offers a more flexible way to obtain document sizes:
db.test.aggregate([
{
"$project": {
"size_bytes": { "$bsonSize": "$$ROOT" },
"size_KB": { "$divide": [{"$bsonSize": "$$ROOT"}, 1000] },
"size_MB": { "$divide": [{"$bsonSize": "$$ROOT"}, 1000000] }
}
}
])
This approach is particularly suitable for calculating sizes of multiple documents within an aggregation pipeline.
Batch Processing and Document Size Limits
For scenarios requiring analysis of all documents in a collection, the following script can be used:
db.test.find().forEach(function(obj) {
var size = Object.bsonsize(obj);
print('_id: ' + obj._id + ' || Size: ' + size + 'B');
});
Alternatively, generate JSON-formatted output for further processing:
db.test.find().forEach(function(obj) {
var size = Object.bsonsize(obj);
var stats = {
'_id': obj._id,
'bytes': size,
'KB': Math.round(size/1000),
'MB': Math.round(size/(1000*1000))
};
print(tojson(stats));
});
It is essential to note that MongoDB enforces a strict 16MB size limit per document. This limit encompasses all BSON components of a document, necessitating consideration during data model design.
Practical Recommendations and Best Practices
- Understand API Return Types: Always remember that
find()returns a cursor, whilefindOne()returns a document object. - Version Compatibility: Choose appropriate document size calculation methods based on the MongoDB version.
- Performance Considerations: For large collections, be mindful of memory usage and performance impacts during batch processing.
- Monitoring and Optimization: Regularly check document size distributions to prevent individual documents from approaching the 16MB limit, which can affect query performance.
Conclusion
Accurately retrieving MongoDB document sizes requires correct understanding of API behavior differences. Applying Object.bsonsize() to document objects rather than cursors is key to resolving size calculation discrepancies. With MongoDB's evolution, the $bsonSize aggregation operator provides a modern alternative. Developers should select suitable methods based on specific needs and always consider the impact of document size limits on system design. Mastering these technical details enables more effective database performance optimization and storage management.