MongoDB数据类型 #

一、BSON简介 #

1.1 什么是BSON #

BSON(Binary JSON)是一种二进制编码的文档格式,MongoDB使用BSON存储文档。

text
JSON vs BSON

JSON:
{
    "name": "John",
    "age": 25
}

BSON (二进制表示):
\x16\x00\x00\x00\x02name\x00\x05\x00\x00\x00John\x00
\x10age\x00\x19\x00\x00\x00\x00

1.2 BSON特点 #

特点 说明
二进制 高效存储和传输
类型丰富 支持更多数据类型
有序 保持字段顺序
长度前缀 快速遍历

1.3 BSON类型列表 #

类型 编号 别名
Double 1 “double”
String 2 “string”
Object 3 “object”
Array 4 “array”
Binary 5 “binData”
ObjectId 7 “objectId”
Boolean 8 “bool”
Date 9 “date”
Null 10 “null”
Regular Expression 11 “regex”
32-bit Integer 16 “int”
Timestamp 17 “timestamp”
64-bit Integer 18 “long”
Decimal128 19 “decimal”
Min Key -1 “minKey”
Max Key 127 “maxKey”

二、基本数据类型 #

2.1 字符串(String) #

javascript
// UTF-8编码字符串
db.users.insertOne({
    name: "张三",
    email: "zhangsan@example.com",
    description: "这是一个很长的描述文本..."
})

// 查询字符串
db.users.find({ name: "张三" })

// 字符串长度限制
// 单个文档最大16MB

2.2 数值类型 #

32位整数(Int)

javascript
// Shell中默认将整数存储为Double
// 使用NumberInt明确指定32位整数
db.products.insertOne({
    name: "Product A",
    quantity: NumberInt(100),
    minStock: NumberInt(10)
})

64位整数(Long)

javascript
// 使用NumberLong存储大整数
db.transactions.insertOne({
    transactionId: NumberLong("9007199254740993"),
    amount: NumberLong("1000000000000")
})

// 查询时需要使用NumberLong
db.transactions.find({
    transactionId: NumberLong("9007199254740993")
})

浮点数(Double)

javascript
// 默认数值类型
db.products.insertOne({
    name: "Product A",
    price: 99.99,
    discount: 0.15
})

// 科学计数法
db.scientific.insertOne({
    value: 1.23e10
})

高精度小数(Decimal128)

javascript
// 金融场景使用Decimal128
db.accounts.insertOne({
    accountId: "ACC001",
    balance: NumberDecimal("123456789.1234567890123456789"),
    interest: NumberDecimal("0.000123456789012345")
})

// 精确计算
db.accounts.aggregate([
    {
        $project: {
            accountId: 1,
            balance: 1,
            interestAmount: {
                $multiply: ["$balance", "$interest"]
            }
        }
    }
])

2.3 布尔值(Boolean) #

javascript
db.users.insertOne({
    name: "John",
    isActive: true,
    isVerified: false,
    hasSubscription: true
})

// 查询布尔值
db.users.find({ isActive: true })
db.users.find({ isVerified: false })

2.4 日期(Date) #

javascript
// 当前日期
db.events.insertOne({
    name: "Event A",
    createdAt: new Date(),
    updatedAt: ISODate()
})

// 指定日期
db.events.insertOne({
    name: "Event B",
    eventDate: ISODate("2024-12-31T00:00:00Z"),
    deadline: new Date("2024-06-30")
})

// 日期操作
db.events.aggregate([
    {
        $project: {
            name: 1,
            year: { $year: "$eventDate" },
            month: { $month: "$eventDate" },
            day: { $dayOfMonth: "$eventDate" },
            hour: { $hour: "$eventDate" }
        }
    }
])

// 日期比较
db.events.find({
    eventDate: {
        $gte: ISODate("2024-01-01"),
        $lt: ISODate("2025-01-01")
    }
})

2.5 Null #

javascript
// Null值
db.users.insertOne({
    name: "John",
    middleName: null,
    nickname: null
})

// 查询Null值
db.users.find({ middleName: null })  // 匹配null和不存在

// 只匹配null值
db.users.find({ middleName: { $type: 10 } })

// 匹配字段不存在
db.users.find({ middleName: { $exists: false } })

三、特殊数据类型 #

3.1 ObjectId #

javascript
// 自动生成的_id
db.users.insertOne({
    name: "John"
})
// _id: ObjectId("65f1a2b3c4d5e6f7g8h9i0j1")

// 手动指定_id
db.users.insertOne({
    _id: ObjectId(),
    name: "Jane"
})

// ObjectId结构
// 65f1a2b3c4d5e6f7g8h9i0j1
// └─────┬─────┘└┬┘└─┬─┘└┬┘
//   时间戳    机器ID 进程ID 计数器

// 提取时间戳
db.users.aggregate([
    {
        $project: {
            name: 1,
            createdTime: { $toDate: "$_id" }
        }
    }
])

3.2 二进制数据(Binary) #

javascript
// 存储二进制数据
db.files.insertOne({
    name: "document.pdf",
    data: BinData(0, "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC..."),
    contentType: "application/pdf",
    size: NumberInt(1024)
})

// 二进制子类型
// 0: 通用二进制
// 1: 函数
// 2: 旧二进制
// 3: 旧UUID
// 4: UUID
// 5: MD5
// 6: 加密

// UUID示例
db.sessions.insertOne({
    sessionId: UUID("123e4567-e89b-12d3-a456-426614174000"),
    userId: ObjectId(),
    createdAt: new Date()
})

3.3 正则表达式 #

javascript
// 存储正则表达式
db.patterns.insertOne({
    name: "email",
    pattern: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
})

// 查询中使用正则
db.users.find({
    email: /^[a-zA-Z0-9._%+-]+@example\.com$/
})

// $regex操作符
db.users.find({
    name: { $regex: /^John/i }
})

// 复杂正则查询
db.users.find({
    name: {
        $regex: "john",
        $options: "i"  // 不区分大小写
    }
})

3.4 时间戳(Timestamp) #

javascript
// 内部使用,主要用于复制
db.oplog.insertOne({
    ts: Timestamp(1234567890, 1),
    op: "i",
    ns: "mydb.users",
    o: { _id: ObjectId(), name: "John" }
})

// 获取当前时间戳
db.users.insertOne({
    name: "John",
    createdAt: Timestamp(0, 0)  // 自动设置为当前时间
})

3.5 JavaScript代码 #

javascript
// 存储JavaScript代码
db.scripts.insertOne({
    name: "calculateDiscount",
    code: function(price, discount) {
        return price * (1 - discount);
    }
})

// 使用$where执行JavaScript
db.users.find({
    $where: function() {
        return this.age > 20 && this.age < 30;
    }
})

// 注意:$where性能较低,谨慎使用

四、复合数据类型 #

4.1 数组(Array) #

javascript
// 简单数组
db.users.insertOne({
    name: "John",
    tags: ["mongodb", "nosql", "database"],
    scores: [85, 90, 78, 92]
})

// 对象数组
db.orders.insertOne({
    orderId: "ORD001",
    items: [
        { productId: ObjectId(), name: "Product A", quantity: 2, price: 99.99 },
        { productId: ObjectId(), name: "Product B", quantity: 1, price: 149.99 }
    ],
    totalAmount: 349.97
})

// 数组查询
db.users.find({ tags: "mongodb" })
db.users.find({ tags: { $all: ["mongodb", "nosql"] } })
db.users.find({ tags: { $size: 3 } })

// 数组元素查询
db.orders.find({
    "items.name": "Product A"
})

db.orders.find({
    items: {
        $elemMatch: {
            quantity: { $gt: 1 },
            price: { $lt: 100 }
        }
    }
})

// 数组更新
db.users.updateOne(
    { name: "John" },
    { $push: { tags: "newTag" } }
)

db.users.updateOne(
    { name: "John" },
    { $pull: { tags: "oldTag" } }
)

4.2 嵌套文档(Embedded Document) #

javascript
// 单层嵌套
db.users.insertOne({
    name: "John",
    address: {
        street: "123 Main St",
        city: "Beijing",
        country: "China",
        zipCode: "100000"
    }
})

// 多层嵌套
db.companies.insertOne({
    name: "Tech Corp",
    headquarters: {
        address: {
            street: "456 Tech Ave",
            city: "Shanghai",
            country: "China"
        },
        coordinates: {
            lat: 31.2304,
            lng: 121.4737
        }
    }
})

// 嵌套文档查询
db.users.find({ "address.city": "Beijing" })
db.users.find({ "address.zipCode": "100000" })

// 嵌套文档更新
db.users.updateOne(
    { name: "John" },
    { $set: { "address.city": "Shanghai" } }
)

4.3 文档结构设计 #

嵌入式文档

javascript
// 一对一关系
db.users.insertOne({
    name: "John",
    profile: {
        avatar: "avatar.jpg",
        bio: "Software Developer",
        website: "https://example.com"
    }
})

// 一对少关系
db.posts.insertOne({
    title: "My First Post",
    content: "...",
    comments: [
        { user: "Jane", text: "Great post!", createdAt: new Date() },
        { user: "Bob", text: "Thanks for sharing", createdAt: new Date() }
    ]
})

引用式文档

javascript
// 一对多关系
db.users.insertOne({
    _id: ObjectId(),
    name: "John"
})

db.orders.insertOne({
    _id: ObjectId(),
    userId: ObjectId(),  // 引用用户
    items: [...],
    totalAmount: 199.99
})

// 使用$lookup关联
db.orders.aggregate([
    {
        $lookup: {
            from: "users",
            localField: "userId",
            foreignField: "_id",
            as: "user"
        }
    }
])

五、类型检查与转换 #

5.1 类型检查 #

javascript
// $type操作符
db.users.find({ age: { $type: "number" } })
db.users.find({ name: { $type: "string" } })
db.users.find({ createdAt: { $type: "date" } })

// 检查多种类型
db.users.find({
    field: { $type: ["string", "null"] }
})

// 使用类型编号
db.users.find({ age: { $type: 16 } })  // 32位整数
db.users.find({ age: { $type: 1 } })   // Double

5.2 类型转换 #

javascript
// $convert操作符
db.users.aggregate([
    {
        $project: {
            name: 1,
            ageString: {
                $convert: {
                    input: "$age",
                    to: "string",
                    onError: "Error",
                    onNull: "Null"
                }
            }
        }
    }
])

// 便捷转换操作符
db.users.aggregate([
    {
        $project: {
            name: 1,
            ageString: { $toString: "$age" },
            ageInt: { $toInt: "$ageString" },
            ageDouble: { $toDouble: "$age" },
            createdDate: { $toDate: "$createdAt" }
        }
    }
])

5.3 类型判断函数 #

javascript
// 判断类型
typeof "hello"        // "string"
typeof 123            // "number"
typeof true           // "boolean"
typeof {}             // "object"
typeof []             // "object"
typeof null           // "object"

// 判断数组
Array.isArray([1, 2, 3])  // true

// 判断ObjectId
ObjectId.isValid("507f1f77bcf86cd799439011")  // true

六、数据类型选择 #

6.1 数值类型选择 #

场景 推荐类型 说明
计数器 NumberInt 整数计数
大整数 NumberLong 超过16位整数
价格 NumberDecimal 精确计算
科学计算 Double 浮点运算
百分比 NumberDecimal 精确百分比

6.2 日期类型选择 #

场景 推荐类型 说明
创建时间 Date 用户可见时间
更新时间 Date 用户可见时间
复制操作时间 Timestamp 内部使用
时序数据 Date 时序集合

6.3 文档结构选择 #

场景 推荐结构 说明
一对一 嵌入式 简单直接
一对少 嵌入式 减少查询
一对多 引用式 避免文档过大
多对多 引用式 灵活关联

七、数据类型限制 #

7.1 文档大小限制 #

javascript
// 单个文档最大16MB
// 超过限制需要使用GridFS

// 检查文档大小
Object.bsonsize(db.users.findOne())

// 大文件存储
// 使用GridFS
db.fs.files.insertOne({
    filename: "large_file.pdf",
    length: NumberLong(104857600),  // 100MB
    chunkSize: 261120,
    uploadDate: new Date()
})

7.2 嵌套深度限制 #

javascript
// MongoDB支持最多100层嵌套
// 但实际使用中建议不超过3-4层

// 推荐
{
    user: {
        profile: {
            address: {
                city: "Beijing"
            }
        }
    }
}

// 不推荐(嵌套过深)
{
    level1: {
        level2: {
            level3: {
                level4: {
                    level5: {
                        // ...
                    }
                }
            }
        }
    }
}

7.3 字段名限制 #

javascript
// 字段名不能包含.和$
// _id是保留字段

// 错误示例
{
    "user.name": "John",  // 包含点号
    "$price": 99.99       // 包含$
}

// 正确示例
{
    "userName": "John",
    "price": 99.99
}

八、最佳实践 #

8.1 数据类型一致性 #

javascript
// 保持字段类型一致
// 推荐
db.users.insertMany([
    { name: "John", age: 25 },
    { name: "Jane", age: 28 }
])

// 不推荐(类型不一致)
db.users.insertMany([
    { name: "John", age: 25 },
    { name: "Jane", age: "28" }  // 字符串
])

8.2 使用合适的数据类型 #

javascript
// 金额使用Decimal128
db.products.insertOne({
    name: "Product A",
    price: NumberDecimal("99.99")
})

// 大整数使用NumberLong
db.counters.insertOne({
    name: "page_views",
    value: NumberLong("9007199254740993")
})

// 日期使用Date而不是字符串
db.events.insertOne({
    name: "Event A",
    date: new Date()  // 不是 "2024-01-01"
})

8.3 文档设计原则 #

javascript
// 1. 就近原则:相关数据放在一起
db.users.insertOne({
    name: "John",
    address: {
        street: "123 Main St",
        city: "Beijing"
    }
})

// 2. 避免过深嵌套
// 3. 考虑查询模式
// 4. 考虑更新频率

九、总结 #

数据类型速查表:

类型 用途 示例
String 文本数据 “Hello”
Int 整数 NumberInt(100)
Long 大整数 NumberLong(“…”)
Double 浮点数 99.99
Decimal 高精度 NumberDecimal(“…”)
Boolean 布尔值 true/false
Date 日期时间 new Date()
ObjectId 文档ID ObjectId()
Array 数组 [1, 2, 3]
Object 嵌套文档
Binary 二进制 BinData(0, “…”)
Null 空值 null

下一步,让我们学习数据库操作!

最后更新:2026-03-27