Skip to content

文档操作

文档(Document)是MongoDB中数据的基本单位,类似于关系数据库中的行。本章将详细介绍文档的插入、查询、更新和删除操作。

文档结构

文档基本概念

javascript
// MongoDB文档是键值对的集合,使用BSON格式存储

// 基本文档结构
{
    "_id": ObjectId("..."),    // 文档唯一标识,自动生成
    "name": "张三",             // 字符串字段
    "age": 25,                  // 数字字段
    "active": true              // 布尔字段
}

// 文档特点:
// 1. 字段名必须是字符串
// 2. 字段名不能包含$和.字符
// 3. 字段名不能以$开头
// 4. _id字段是保留字段,用作主键
// 5. 文档中不能有重复的字段名

// 支持的数据类型
{
    "_id": ObjectId("507f1f77bcf86cd799439011"),  // ObjectId
    "string": "字符串",                            // 字符串
    "int": NumberInt(42),                         // 32位整数
    "long": NumberLong(42),                       // 64位整数
    "double": 3.14,                               // 浮点数
    "decimal": NumberDecimal("3.14159"),          // 高精度小数
    "boolean": true,                              // 布尔值
    "date": ISODate("2024-01-01T00:00:00Z"),     // 日期
    "null": null,                                 // null值
    "array": [1, 2, 3],                          // 数组
    "object": { "key": "value" },                // 嵌套文档
    "binary": BinData(0, "aGVsbG8="),            // 二进制数据
    "regex": /^pattern$/,                        // 正则表达式
    "code": Code("function() {}")                // JavaScript代码
}

插入文档

insertOne - 插入单个文档

javascript
// 插入单个文档
db.users.insertOne({
    name: "张三",
    age: 25,
    email: "zhangsan@example.com",
    hobbies: ["阅读", "编程"],
    address: {
        city: "北京",
        street: "朝阳路"
    },
    createdAt: new Date()
})

// 输出示例:
// {
//   acknowledged: true,
//   insertedId: ObjectId("65a1b2c3d4e5f6g7h8i9j0k1")
// }

// 插入时指定_id
db.users.insertOne({
    _id: "user001",           // 自定义_id
    name: "李四",
    age: 30
})
// 如果_id已存在,会报错

// 插入文档的选项
db.users.insertOne(
    { name: "王五", age: 28 },
    { 
        writeConcern: { w: "majority", j: true },  // 写入关注级别
        ordered: true  // 是否按顺序执行
    }
)

insertMany - 批量插入文档

javascript
// 批量插入多个文档
db.users.insertMany([
    { name: "用户1", age: 20, city: "北京" },
    { name: "用户2", age: 25, city: "上海" },
    { name: "用户3", age: 30, city: "广州" }
])

// 输出示例:
// {
//   acknowledged: true,
//   insertedIds: {
//     '0': ObjectId("..."),
//     '1': ObjectId("..."),
//     '2': ObjectId("...")
//   }
// }

// 批量插入选项
db.users.insertMany(
    [
        { name: "用户A", age: 22 },
        { name: "用户B", age: 23 },
        { _id: "user001", name: "用户C", age: 24 }  // 如果_id冲突
    ],
    { ordered: false }  // 即使某条失败,继续插入其他文档
)
// ordered: true(默认)- 遇到错误停止
// ordered: false - 遇到错误继续插入其他文档

insert - 兼容方法

javascript
// insert方法(已弃用,但仍可用)
// 可以插入单个或多个文档

// 插入单个文档
db.users.insert({ name: "测试用户", age: 20 })

// 插入多个文档
db.users.insert([
    { name: "批量用户1", age: 21 },
    { name: "批量用户2", age: 22 }
])

批量插入最佳实践

javascript
// 批量插入的最佳实践

// 1. 分批插入大量数据
var batchSize = 1000;
var totalDocs = 10000;

for (var i = 0; i < totalDocs; i += batchSize) {
    var batch = [];
    for (var j = 0; j < batchSize && i + j < totalDocs; j++) {
        batch.push({
            name: "用户" + (i + j),
            age: Math.floor(Math.random() * 50) + 18,
            createdAt: new Date()
        });
    }
    db.users.insertMany(batch);
    print("已插入 " + Math.min(i + batchSize, totalDocs) + " 条文档");
}

// 2. 使用ordered: false提高性能
db.users.insertMany(largeArray, { ordered: false })

// 3. 关闭索引后批量插入(大量数据时)
db.users.dropIndexes();  // 删除非必要索引
db.users.insertMany(largeArray);
db.users.createIndex({ name: 1 });  // 重建索引

查询文档

find - 查询多个文档

javascript
// 查询所有文档
db.users.find({})

// 输出示例(使用pretty()格式化):
// { "_id" : ObjectId("..."), "name" : "张三", "age" : 25 }
// { "_id" : ObjectId("..."), "name" : "李四", "age" : 30 }

// 格式化输出
db.users.find({}).pretty()

// 条件查询
db.users.find({ age: 25 })              // 查询age等于25的文档
db.users.find({ "address.city": "北京" })  // 查询嵌套字段

// 指定返回字段(投影)
db.users.find(
    { age: { $gte: 20 } },   // 查询条件
    { name: 1, age: 1, _id: 0 }  // 只返回name和age,不返回_id
)

// 查询结果计数
db.users.find({ age: { $gte: 20 } }).count()
// 或
db.users.countDocuments({ age: { $gte: 20 } })

findOne - 查询单个文档

javascript
// 查询单个文档
db.users.findOne({ name: "张三" })

// 输出示例:
// {
//   "_id": ObjectId("..."),
//   "name": "张三",
//   "age": 25,
//   "email": "zhangsan@example.com"
// }

// 查询第一个匹配的文档
db.users.findOne({ age: { $gte: 20 } })

// 指定返回字段
db.users.findOne(
    { name: "张三" },
    { name: 1, email: 1, _id: 0 }
)

查询操作符

javascript
// 比较操作符
db.users.find({ age: { $eq: 25 } })      // 等于
db.users.find({ age: { $ne: 25 } })      // 不等于
db.users.find({ age: { $gt: 20 } })      // 大于
db.users.find({ age: { $gte: 20 } })     // 大于等于
db.users.find({ age: { $lt: 30 } })      // 小于
db.users.find({ age: { $lte: 30 } })     // 小于等于
db.users.find({ age: { $in: [20, 25, 30] } })  // 在列表中
db.users.find({ age: { $nin: [20, 25, 30] } }) // 不在列表中

// 逻辑操作符
db.users.find({ 
    $and: [
        { age: { $gte: 20 } },
        { city: "北京" }
    ]
})  // 与操作

db.users.find({
    $or: [
        { city: "北京" },
        { city: "上海" }
    ]
})  // 或操作

db.users.find({
    age: { $not: { $gt: 30 } }
})  // 非操作

db.users.find({
    age: { $nor: [{ $lt: 20 }, { $gt: 50 }] }
})  // 既不...也不...

// 存在性检查
db.users.find({ email: { $exists: true } })   // 字段存在
db.users.find({ email: { $exists: false } })  // 字段不存在

// 类型检查
db.users.find({ age: { $type: "int" } })      // 类型为int
db.users.find({ age: { $type: 16 } })         // 类型码16为int

查询结果处理

javascript
// 限制返回数量
db.users.find().limit(10)           // 只返回前10条

// 跳过指定数量
db.users.find().skip(10)            // 跳过前10条

// 分页查询
var page = 2;
var pageSize = 10;
db.users.find().skip((page - 1) * pageSize).limit(pageSize)

// 排序
db.users.find().sort({ age: 1 })    // 按age升序
db.users.find().sort({ age: -1 })   // 按age降序
db.users.find().sort({ age: -1, name: 1 })  // 多字段排序

// 组合使用
db.users.find({ age: { $gte: 20 } })
    .sort({ age: -1 })
    .skip(0)
    .limit(10)

// 遍历查询结果
db.users.find({ age: { $gte: 20 } }).forEach(function(user) {
    print("用户: " + user.name + ", 年龄: " + user.age);
})

// 将结果转为数组
var users = db.users.find({ age: { $gte: 20 } }).toArray()

更新文档

updateOne - 更新单个文档

javascript
// 更新单个文档

// 使用$set更新字段
db.users.updateOne(
    { name: "张三" },              // 查询条件
    { $set: { age: 26 } }         // 更新操作
)
// 输出:{ acknowledged: true, matchedCount: 1, modifiedCount: 1 }

// 更新多个字段
db.users.updateOne(
    { name: "张三" },
    { $set: { age: 26, city: "上海", email: "newemail@example.com" } }
)

// 更新嵌套字段
db.users.updateOne(
    { name: "张三" },
    { $set: { "address.city": "深圳" } }
)

// 使用$inc增加数值
db.users.updateOne(
    { name: "张三" },
    { $inc: { age: 1 } }  // age加1
)

// 使用$mul乘以数值
db.users.updateOne(
    { name: "张三" },
    { $mul: { salary: 1.1 } }  // salary乘以1.1
)

// 使用$rename重命名字段
db.users.updateOne(
    { name: "张三" },
    { $rename: { "oldField": "newField" } }
)

// 使用$unset删除字段
db.users.updateOne(
    { name: "张三" },
    { $unset: { tempField: "" } }  // 删除tempField字段
)

// 使用$currentDate设置当前日期
db.users.updateOne(
    { name: "张三" },
    { $currentDate: { updatedAt: true } }
)

// 如果不存在则插入(upsert)
db.users.updateOne(
    { name: "新用户" },
    { $set: { name: "新用户", age: 20 } },
    { upsert: true }
)

updateMany - 批量更新文档

javascript
// 批量更新文档

// 更新所有符合条件的文档
db.users.updateMany(
    { city: "北京" },              // 查询条件
    { $set: { region: "华北" } }   // 更新操作
)
// 输出:{ acknowledged: true, matchedCount: 100, modifiedCount: 100 }

// 批量增加数值
db.users.updateMany(
    { department: "技术部" },
    { $inc: { salary: 1000 } }
)

// 批量添加数组元素
db.users.updateMany(
    { city: "北京" },
    { $push: { tags: "首都" } }
)

// 批量删除数组元素
db.users.updateMany(
    {},
    { $pull: { tags: "临时标签" } }
)

replaceOne - 替换文档

javascript
// 替换整个文档(除了_id)

db.users.replaceOne(
    { name: "张三" },              // 查询条件
    {                             // 新文档内容
        name: "张三",
        age: 30,
        city: "深圳",
        updatedAt: new Date()
    }
)

// 注意:replaceOne会完全替换文档,原有字段会被删除

数组更新操作符

javascript
// 数组更新操作符

// $push - 添加元素到数组
db.users.updateOne(
    { name: "张三" },
    { $push: { hobbies: "游泳" } }
)

// $push添加多个元素
db.users.updateOne(
    { name: "张三" },
    { $push: { hobbies: { $each: ["游泳", "跑步", "健身"] } } }
)

// $push并限制数组长度
db.users.updateOne(
    { name: "张三" },
    { 
        $push: { 
            logs: { 
                $each: ["log1", "log2"],
                $slice: -10  // 只保留最后10个元素
            } 
        } 
    }
)

// $addToSet - 添加元素(不存在时才添加,去重)
db.users.updateOne(
    { name: "张三" },
    { $addToSet: { hobbies: "编程" } }  // 如果已存在则不添加
)

// $addToSet添加多个元素
db.users.updateOne(
    { name: "张三" },
    { $addToSet: { hobbies: { $each: ["编程", "阅读"] } } }
)

// $pop - 删除数组第一个或最后一个元素
db.users.updateOne(
    { name: "张三" },
    { $pop: { hobbies: 1 } }  // 1删除最后一个,-1删除第一个
)

// $pull - 删除匹配的元素
db.users.updateOne(
    { name: "张三" },
    { $pull: { hobbies: "游泳" } }  // 删除值为"游泳"的元素
)

// $pull按条件删除
db.users.updateOne(
    { name: "张三" },
    { $pull: { scores: { $lt: 60 } } }  // 删除小于60的分数
)

// $pullAll - 删除多个元素
db.users.updateOne(
    { name: "张三" },
    { $pullAll: { hobbies: ["游泳", "跑步"] } }
)

// 更新数组中特定位置的元素
db.users.updateOne(
    { name: "张三" },
    { $set: { "hobbies.0": "新爱好" } }  // 更新第一个元素
)

// 更新数组中匹配条件的元素
db.users.updateOne(
    { name: "张三", "grades.subject": "数学" },
    { $set: { "grades.$.score": 95 } }  // $表示匹配的元素位置
)

删除文档

deleteOne - 删除单个文档

javascript
// 删除单个文档
db.users.deleteOne({ name: "张三" })
// 输出:{ acknowledged: true, deletedCount: 1 }

// 删除第一个匹配的文档
db.users.deleteOne({ age: { $gt: 30 } })

// 删除并返回被删除的文档
db.users.findOneAndDelete({ name: "张三" })

// 删除并返回,按排序
db.users.findOneAndDelete(
    { age: { $gt: 20 } },
    { sort: { age: -1 } }  // 删除年龄最大的
)

deleteMany - 批量删除文档

javascript
// 批量删除文档
db.users.deleteMany({ city: "北京" })
// 输出:{ acknowledged: true, deletedCount: 100 }

// 删除所有文档
db.users.deleteMany({})
// 输出:{ acknowledged: true, deletedCount: 1000 }

// 按条件批量删除
db.users.deleteMany({ 
    createdAt: { $lt: new Date("2023-01-01") } 
})

remove - 兼容方法

javascript
// remove方法(已弃用,但仍可用)

// 删除所有匹配的文档
db.users.remove({ name: "张三" })

// 只删除一个
db.users.remove({ name: "张三" }, { justOne: true })

删除操作最佳实践

javascript
// 删除操作最佳实践

// 1. 删除前先查询确认
var toDelete = db.users.find({ status: "inactive" }).count()
print("将删除 " + toDelete + " 条文档")

// 确认后再删除
db.users.deleteMany({ status: "inactive" })

// 2. 大批量删除时分批进行
var batchSize = 1000;
var totalDeleted = 0;

while (true) {
    var result = db.users.deleteMany(
        { createdAt: { $lt: new Date("2023-01-01") } },
        { limit: batchSize }
    );
    totalDeleted += result.deletedCount;
    print("已删除 " + totalDeleted + " 条文档");
    
    if (result.deletedCount < batchSize) {
        break;  // 删除完成
    }
}

// 3. 使用事务确保数据一致性
// (在事务章节详细介绍)

批量写入操作

bulkWrite - 批量写入

javascript
// bulkWrite支持多种操作混合批量执行

db.users.bulkWrite([
    // 插入操作
    { insertOne: { document: { name: "用户1", age: 20 } } },
    { insertOne: { document: { name: "用户2", age: 25 } } },
    
    // 更新操作
    { updateOne: {
        filter: { name: "张三" },
        update: { $set: { age: 26 } }
    }},
    
    // 批量更新
    { updateMany: {
        filter: { city: "北京" },
        update: { $set: { region: "华北" } }
    }},
    
    // 删除操作
    { deleteOne: { filter: { name: "删除用户" } } },
    
    // 批量删除
    { deleteMany: { filter: { status: "deleted" } } },
    
    // 替换操作
    { replaceOne: {
        filter: { name: "李四" },
        replacement: { name: "李四", age: 30, city: "上海" }
    }}
], { ordered: false })  // ordered: false表示遇到错误继续执行

// 输出示例:
// {
//   acknowledged: true,
//   insertedCount: 2,
//   insertedIds: { '0': ObjectId("..."), '1': ObjectId("...") },
//   matchedCount: 3,
//   modifiedCount: 3,
//   deletedCount: 2
// }

本章小结

本章详细介绍了MongoDB文档的CRUD操作:

  1. 文档结构:理解文档的基本概念和支持的数据类型
  2. 插入文档:掌握insertOne、insertMany的使用方法
  3. 查询文档:学会使用find、findOne和各种查询操作符
  4. 更新文档:掌握updateOne、updateMany和各种更新操作符
  5. 删除文档:学会使用deleteOne、deleteMany删除文档
  6. 数组操作:掌握数组的添加、删除、更新操作
  7. 批量操作:学会使用bulkWrite进行批量写入

下一章,我们将学习查询进阶,了解更复杂的查询技巧。