0% completed
Data Types in MongoDB
On This Page
One Document With the Common Types
ObjectId and String
Numbers
Boolean, Null and Missing
Array and Object
Binary Data
Date
Types You Will Rarely Store
Type 30.0 in mongosh and MongoDB stores an integer. Type 2147483648, which is also a whole number, and it stores a floating point number.
db.users.drop() db.users.insertOne({ score: 30.0, price: 19.99, big: 2147483648 }) db.users.aggregate([{ $project: { _id: 0, score: { $type: "$score" }, price: { $type: "$price" }, big: { $type: "$big" } } }])
[ { score: 'int', price: 'double', big: 'double' } ]
The last command uses the $type expression to ask the server for the BSON type of each stored value. You do not need to follow the rest of its syntax yet. As this result shows, the type on the server is not always the one you might assume from the text you typed. The type affects what a value can represent, how much space it takes and which queries match it, so it is worth choosing deliberately.
One Document With the Common Types
We will use one user document, with one field for each common type:
db.users.drop() db.users.insertOne({ name: "John Doe", email: "john.doe@example.com", age: 30, rating: 4.5, visits: Long("9007199254740993"), balance: Decimal128("19.99"), isStudent: false, middleName: null, tags: ["mongodb", "database", "NoSQL"], address: { street: "123 Main St", city: "Anytown", zipcode: "12345" }, avatar: BinData(0, "SGVsbG8="), createdAt: new Date("2024-06-01T10:00:00Z") })
The same kind of command reports the type of each field. The last field in it, nickname, does not exist in the document.
db.users.aggregate([{ $project: { _id: { $type: "$_id" }, name: { $type: "$name" }, age: { $type: "$age" }, rating: { $type: "$rating" }, visits: { $type: "$visits" }, balance: { $type: "$balance" }, isStudent: { $type: "$isStudent" }, middleName: { $type: "$middleName" }, tags: { $type: "$tags" }, address: { $type: "$address" }, avatar: { $type: "$avatar" }, createdAt: { $type: "$createdAt" }, nickname: { $type: "$nickname" } } }])
[ { _id: 'objectId', name: 'string', age: 'int', rating: 'double', visits: 'long', balance: 'decimal', isStudent: 'bool', middleName: 'null', tags: 'array', address: 'object', avatar: 'binData', createdAt: 'date', nickname: 'missing' } ]
The sections below follow this result from top to bottom.
ObjectId and String
We did not supply _id, and a generated value of type ObjectId was added. You can also supply one yourself, as in { _id: ObjectId("507f1f77bcf86cd799439011") }.
name is a string, which contains UTF-8 text and is the most common type. Text and numbers are not interchangeable, however. Our document stores age as the number 30, so a query for the text "30" does not find it. The reverse is also true: an age stored as text would not match a query for the number.
db.users.find({ age: 30 }, { name: 1, _id: 0 }) db.users.find({ age: "30" }, { name: 1, _id: 0 })
The first query returns John, and the second returns nothing.
Numbers
Four types contain numbers, and they differ in range, precision and size.
| Type | Size | Contains | Written in mongosh as |
|---|---|---|---|
int | 4 bytes | Whole numbers from -2,147,483,648 to 2,147,483,647 | 30 or Int32(30) |
long | 8 bytes | Larger whole numbers | Long("9007199254740993") |
double | 8 bytes | Numbers with a fractional part, approximately | 4.5 or Double(30) |
decimal | 16 bytes | Decimal numbers, exactly | Decimal128("19.99") |
mongosh is a JavaScript environment, and ordinary JavaScript numbers have only one type. That explains the result at the start of this lesson. A number with no fractional part that fits in 32 bits is stored as int, and any other number is stored as double. The value 30.0 is simply the number 30 in JavaScript. Drivers for other languages map the number types of their own language in their own ways.
The quotes in Long("9007199254740993") are required for values this large. JavaScript cannot represent that number exactly, and without quotes it changes before MongoDB ever receives it:
print(9007199254740993)
9007199254740992
Leaving the quotes off Long is worse than rounding. In our test with mongosh 2.3.8, Long(9007199254740993) stored 0, so always use the quoted form for large values. Decimal128 also takes a string, for the same reason. The older names NumberLong and NumberDecimal still work, and you will see them in older material.
A double stores most fractions approximately. That is acceptable for a rating, but not for money, as this comparison shows. Both sums add 0.1 and 0.2, once as doubles and once as decimals:
db.money.insertOne({ a: 0.1, b: 0.2, x: Decimal128("0.1"), y: Decimal128("0.2") }) db.money.aggregate([{ $project: { _id: 0, doubleSum: { $add: ["$a", "$b"] }, decimalSum: { $add: ["$x", "$y"] } } }])
[ { doubleSum: 0.30000000000000004, decimalSum: Decimal128('0.3') } ]
The cost of decimal is twice the space of a double, and you should use it where exact decimal values matter, such as financial data.
Different number types can represent the same value, and MongoDB compares them by value. A query for { age: 30 } matches an int, a long, a double or a decimal containing 30. To select one specific type, use $type in a query, as in { age: { $type: "int" } }. The aliases int32, int64 and decimal128 do not exist, and the server answers Unknown type name alias.
Boolean, Null and Missing
isStudent is a Boolean, which is either true or false.
middleName is null, which is a stored value meaning that the field exists and has no content. Compare it with nickname in the report, whose type is missing because the document has no such field. The stored documents differ in these two cases, even though a filter such as { middleName: null } matches both a null value and an absent field.
Array and Object
tags is an array, an ordered list. An array can contain values of different types, including other arrays and objects.
address is an object: a nested document with its own field names and values of any BSON type. This allows data that belongs to one user to stay in that user's document.
Binary Data
avatar contains a byte array, which is useful for small images, files or encrypted values. The first argument of BinData is a subtype, where 0 means generic binary. The second is the data in Base64, a way of writing bytes as plain characters, and "SGVsbG8=" here is the five bytes of the word Hello.
The Base64 text must be valid, because mongosh does not check it for you. BinData(0, "binarydatahere") is accepted even though its text is not valid Base64, and it comes back as binarydataherQ==, which is different from what was written. A document can be at most 16 mebibytes, and a larger file does not fit in one binary field. MongoDB offers a separate facility called GridFS for such files, which divides a file into smaller pieces.
Date
createdAt is a Date, a signed 64-bit integer counting the milliseconds since the Unix epoch (January 1, 1970). Dates are stored in UTC, and a time zone in your input only changes the value that gets stored:
new Date("2024-06-01T10:00:00-08:00")
ISODate('2024-06-01T18:00:00.000Z')
Always write new Date() in mongosh. Without new, Date() returns the current time as a string, and that value is stored with type string:
Date() new Date()
Sun Nov 01 2026 07:12:05 GMT-0800 (Pacific Standard Time) ISODate('2026-11-01T15:12:05.606Z')
Types You Will Rarely Store
The remaining types are less common in application data.
Regular Expression. A value such as /abc/i can be stored as a field with type regex, although it is much more common to use a regular expression in a query. This one finds users whose email ends in example.com:
db.users.find({ email: /example\.com$/ }, { name: 1, _id: 0 })
JavaScript. This type stores code as data. A function in an insert, such as { code: function() { return "Hello, MongoDB!"; } }, is stored with type javascript, and Code("function() { return 1; }") does the same thing. MongoDB does not run this code when you read the document, and you receive the text back.
JavaScript with Scope. This type stores code together with a set of variables, and it is deprecated. The form Code("function() { return x; }", { x: 1 }) creates one. Writing { $code: "...", $scope: { x: 1 } } in an insert does not, because the server stores that as an ordinary nested document with type object.
Timestamp. MongoDB uses this type internally, and it is not related to Date. It has 64 bits: 32 for seconds since the epoch, and 32 for a counter of operations within that second. When you insert Timestamp() into a top-level field other than _id, the server fills in the current time, but in a nested field it stays at zero. For dates in your own data, use Date.
db.misc.insertOne({ createdTs: Timestamp(), nested: { t: Timestamp() } }) db.misc.findOne({}, { _id: 0 })
{ createdTs: Timestamp({ t: 1793546290, i: 6 }), nested: { t: Timestamp({ t: 0, i: 0 }) } }
Min Key and Max Key. MinKey() compares lower than every other BSON value, and MaxKey() compares higher than every other value. If a field contains a mix of MinKey(), null, a number, a string, a date and MaxKey(), an ascending sort returns them in exactly that order. MongoDB uses these two values for internal comparisons, and applications seldom store them.
Undefined. This type is deprecated. In mongosh, inserting { u: undefined } stores null.
Reading Progress
0%
On This Page
One Document With the Common Types
ObjectId and String
Numbers
Boolean, Null and Missing
Array and Object
Binary Data
Date
Types You Will Rarely Store