<返回目录     Powered by claud/xia兄

第10课: 数据建模

嵌入式文档 vs 引用

MongoDB提供两种主要的数据建模方式:

嵌入式文档(Embedded Documents)

// 将相关数据嵌入到同一个文档中
{
    _id: ObjectId("..."),
    name: "张三",
    email: "zhangsan@example.com",
    address: {
        street: "长安街1号",
        city: "北京",
        zipCode: "100000"
    },
    orders: [
        {
            orderId: "order001",
            product: "笔记本电脑",
            price: 5999,
            date: ISODate("2026-01-15")
        },
        {
            orderId: "order002",
            product: "鼠标",
            price: 99,
            date: ISODate("2026-01-20")
        }
    ]
}
优点: 缺点:

引用(References)

// 用户集合
{
    _id: "user123",
    name: "张三",
    email: "zhangsan@example.com"
}

// 订单集合
{
    _id: "order001",
    userId: "user123",  // 引用用户ID
    product: "笔记本电脑",
    price: 5999,
    date: ISODate("2026-01-15")
}

// 查询时需要关联
db.orders.aggregate([
    {
        $lookup: {
            from: "users",
            localField: "userId",
            foreignField: "_id",
            as: "user"
        }
    }
])
优点: 缺点:

选择建模方式的原则

场景 推荐方式 原因
一对一关系 嵌入式 数据紧密相关,一起查询
一对少(1-100) 嵌入式 数组不会太大
一对多(100+) 引用 避免文档过大
多对多 引用 避免数据冗余
读多写少 嵌入式 优化读性能
写多读少 引用 减少更新复杂度

设计模式:属性模式

// 传统方式(字段过多)
{
    _id: 1,
    name: "商品A",
    color_red: true,
    color_blue: false,
    size_S: true,
    size_M: false,
    size_L: true
    // ... 更多属性
}

// 属性模式(灵活扩展)
{
    _id: 1,
    name: "商品A",
    attributes: [
        { key: "color", value: "red" },
        { key: "color", value: "blue" },
        { key: "size", value: "S" },
        { key: "size", value: "L" }
    ]
}

// 查询
db.products.find({
    "attributes": {
        $elemMatch: { key: "color", value: "red" }
    }
})

// 创建索引
db.products.createIndex({ "attributes.key": 1, "attributes.value": 1 })

设计模式:桶模式

// 物联网传感器数据(每秒一条)
// 传统方式:每条数据一个文档(数据量巨大)
{
    sensorId: "sensor001",
    temperature: 25.5,
    timestamp: ISODate("2026-02-03T10:00:00Z")
}

// 桶模式:按小时聚合数据
{
    sensorId: "sensor001",
    date: ISODate("2026-02-03T10:00:00Z"),
    measurements: [
        { time: 0, temp: 25.5 },
        { time: 1, temp: 25.6 },
        { time: 2, temp: 25.4 },
        // ... 3600条数据
    ],
    count: 3600,
    avgTemp: 25.5,
    minTemp: 24.8,
    maxTemp: 26.2
}

设计模式:子集模式

// 电影评论(可能有数万条)
// 在电影文档中只嵌入最新的10条评论
{
    _id: "movie123",
    title: "电影名称",
    recentReviews: [
        { user: "user1", rating: 5, comment: "很好看" },
        { user: "user2", rating: 4, comment: "不错" }
        // ... 最多10条
    ],
    totalReviews: 15000
}

// 所有评论存储在单独的集合中
// reviews集合
{
    movieId: "movie123",
    user: "user1",
    rating: 5,
    comment: "很好看",
    date: ISODate("2026-02-03")
}

设计模式:扩展引用

// 订单中嵌入常用的用户信息
{
    _id: "order001",
    userId: "user123",
    // 嵌入常用字段,避免每次都关联查询
    userName: "张三",
    userEmail: "zhangsan@example.com",
    items: [...],
    total: 5999
}

// 完整的用户信息仍在users集合中
// 如需更多信息,再查询users集合

设计模式:版本控制

// 保存文档的历史版本
{
    _id: "doc123",
    title: "文档标题",
    content: "当前内容",
    version: 3,
    history: [
        {
            version: 1,
            content: "初始内容",
            updatedAt: ISODate("2026-01-01"),
            updatedBy: "user1"
        },
        {
            version: 2,
            content: "修改后的内容",
            updatedAt: ISODate("2026-01-15"),
            updatedBy: "user2"
        }
    ]
}

反范式化设计

// 适度的数据冗余可以提升性能
// 订单中冗余商品信息
{
    _id: "order001",
    userId: "user123",
    items: [
        {
            productId: "prod123",
            // 冗余商品信息(下单时的快照)
            productName: "笔记本电脑",
            productPrice: 5999,
            productImage: "laptop.jpg",
            quantity: 1
        }
    ],
    total: 5999,
    createdAt: ISODate("2026-02-03")
}

// 好处:
// 1. 查询订单时不需要关联商品表
// 2. 保存下单时的商品信息(价格可能会变)
// 3. 即使商品被删除,订单信息仍完整

实战案例:博客系统

// 文章集合(嵌入评论和标签)
{
    _id: ObjectId("..."),
    title: "MongoDB数据建模最佳实践",
    author: {
        id: "user123",
        name: "张三",
        avatar: "avatar.jpg"
    },
    content: "文章内容...",
    tags: ["MongoDB", "数据库", "NoSQL"],
    comments: [
        {
            id: "comment1",
            user: "李四",
            content: "写得很好",
            createdAt: ISODate("2026-02-01")
        }
        // 只嵌入最新的20条评论
    ],
    stats: {
        views: 1500,
        likes: 89,
        commentCount: 156
    },
    createdAt: ISODate("2026-01-15"),
    updatedAt: ISODate("2026-02-03")
}

// 所有评论存储在单独的集合
// comments集合
{
    _id: "comment1",
    postId: ObjectId("..."),
    userId: "user456",
    userName: "李四",
    content: "写得很好",
    createdAt: ISODate("2026-02-01")
}

Schema验证

// 创建集合时定义Schema
db.createCollection("users", {
    validator: {
        $jsonSchema: {
            bsonType: "object",
            required: ["name", "email", "age"],
            properties: {
                name: {
                    bsonType: "string",
                    description: "必须是字符串"
                },
                email: {
                    bsonType: "string",
                    pattern: "^.+@.+$",
                    description: "必须是有效的邮箱"
                },
                age: {
                    bsonType: "int",
                    minimum: 0,
                    maximum: 150,
                    description: "年龄必须在0-150之间"
                }
            }
        }
    }
})
练习题:
  1. 设计一个电商系统的数据模型(用户、商品、订单)
  2. 设计一个社交网络的数据模型(用户、帖子、评论、点赞)
  3. 为物联网传感器数据设计桶模式
  4. 实现一个文档的版本控制系统
  5. 为商品属性设计灵活的属性模式
  6. 设计一个支持多对多关系的课程选课系统