BSON 与类型
嵌入文档与 ObjectId。
30 分钟
BSON 与类型
这一课你将能做到
- 说清楚 BSON 和 JSON 的区别,以及为什么 MongoDB 选择 BSON
- 正确使用 ObjectId、Date、Decimal128、内嵌文档与数组,并知道每种类型的典型坑
- 用
$type和typeof排查线上「类型不一致」导致的查询异常
为什么要学
MongoDB 查询报错很少,但「查不到数据」很多,其中一大半是类型问题:Java 端存进去的 userId 是 Long(BSON int64),你在 mongosh 里用 1024(BSON int32)去查,结果能查到;但如果某条记录被 Node 服务写成了字符串 "1024",这条记录就「隐身」了——不报错,就是查不到。
BSON 是 MongoDB 的存储格式,搞懂它的类型系统,才能理解比较规则、索引行为、磁盘占用,以及为什么金额绝对不能存 double。这是后面 CRUD、查询、索引所有课的地基。
先建立图像
JSON 只有 6 种类型,BSON 扩展到 20 多种,常用的如下:
JSON 有的: BSON 额外增加的:
string ObjectId 12 字节主键
number Date 毫秒时间戳(UTC)
boolean Int32 / Int64 区分 32/64 位整数
null Decimal128 高精度十进制(钱!)
array Binary 二进制
object Regex / Timestamp / MinKey / MaxKey ...
一条混合类型的文档:
{
"_id": ObjectId("..."), // ObjectId
"userId": NumberLong(1024), // int64
"score": NumberInt(95), // int32
"price": NumberDecimal("9.99"), // Decimal128
"balance": 9.99, // double(危险!)
"tags": ["mongo", "bson"], // array
"profile": { "city": "上海" }, // 内嵌文档
"createdAt": ISODate("2026-10-07T00:00:00Z")
}
跟着做
步骤 1:观察 JavaScript 数字的「陷阱」
use codeforge
db.typeDemo.drop()
db.typeDemo.insertOne({ a: 1, b: NumberInt(1), c: NumberLong(1), d: 1.0 })
db.typeDemo.find().forEach(d => {
print(`a=${typeof d.a} b=${typeof d.b} c=${typeof d.c} d=${typeof d.d}`)
})
输出全是 number——mongosh 的 typeof 是 JavaScript 语义,看不出 BSON 类型差异。要看真实类型:
db.typeDemo.find().forEach(d => printjson(d))
或用聚合看 BSON 类型名:
db.typeDemo.aggregate([
{ $project: { aType: { $type: "$a" }, bType: { $type: "$b" }, cType: { $type: "$c" } } }
])
$type 返回 int / long / double,这才是存储层的真相。
步骤 2:ObjectId 拆解
const id = new ObjectId()
id.getTimestamp() // 从 ObjectId 里还原出创建时间
id.toString() // 24 位十六进制字符串