第四章:CRUD 操作详解

最后更新: 2024-01-01 作者: MongoDB Team
页面目录

第四章:CRUD 操作详解

掌握 MongoDB 的增删改查核心操作

4.1 Insert 操作

insertOne - 插入单文档

// 基本插入
db.users.insertOne({
  username: "alice",
  email: "alice@example.com",
  age: 25
})

// 插入后获取返回结果
db.users.insertOne({
  username: "bob",
  email: "bob@example.com"
}).then(result => {
  print("插入的文档 ID:", result.insertedId)
})

// 显式指定 _id
db.users.insertOne({
  _id: "user_001",
  username: "charlie",
  email: "charlie@example.com"
})

insertMany - 批量插入

// 批量插入多个文档
db.users.insertMany([
  { username: "dave", email: "dave@example.com", age: 30 },
  { username: "eve", email: "eve@example.com", age: 28 },
  { username: "frank", email: "frank@example.com", age: 35 }
])

// 批量插入并设置有序/无序
db.users.insertMany(
  [
    { username: "user1", email: "user1@example.com" },
    { username: "user2", email: "user2@example.com" }
  ],
  { ordered: false }  // 无序插入,一个失败继续插入其他
)

// 有序插入(默认),遇到错误停止
db.users.insertMany(
  [
    { username: "user1" },
    { _id: "duplicate_id", username: "user2" }  // 会导致插入失败
  ],
  { ordered: true }
)

insert / insertMany(已废弃)

// 不推荐使用 insert,已废弃
db.users.insert({ username: "test" })

save 方法(已废弃)

// 不推荐使用 save,已废弃
// 建议使用 insertOne 或 replaceOne
db.users.save({ _id: ObjectId("..."), username: "updated" })

4.2 Query 操作

find - 查询文档

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

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

// 限制返回字段(投影)
db.users.find(
  {},  // 查询条件
  { username: 1, email: 1, _id: 0 }  // 只返回 username 和 email
)

// 排除字段
db.users.find(
  {},
  { password: 0, __v: 0 }  // 排除 password 和 __v
)

// 嵌套字段投影
db.users.find(
  {},
  { "profile.firstName": 1, "profile.lastName": 1 }
)

findOne - 查询单文档

// 查询单个文档
db.users.findOne({ username: "alice" })

// 查询并排序
db.users.findOne(
  { status: "active" },
  {},
  { sort: { created_at: -1 } }
)

// 查询第一个文档
db.users.findOne()

查询条件操作符

操作符 说明 示例
$eq 等于 { age: { $eq: 25 } }
$ne 不等于 { age: { $ne: 25 } }
$gt 大于 { age: { $gt: 18 } }
$gte 大于等于 { age: { $gte: 18 } }
$lt 小于 { age: { $lt: 65 } }
$lte 小于等于 { age: { $lte: 65 } }
$in 在数组中 { status: { $in: ["active", "pending"] } }
$nin 不在数组中 { status: { $nin: ["deleted"] } }
// 比较操作符示例
db.users.find({ age: { $gte: 18, $lte: 65 } })

db.users.find({ 
  status: { $in: ["active", "pending"] }
})

db.users.find({ 
  username: { $nin: ["admin", "root"] }
})

// 使用 $eq 的完整形式
db.users.find({ username: { $eq: "alice" } })

逻辑操作符

操作符 说明 示例
$and 逻辑与 { $and: [{ a: 1 }, { b: 2 }] }
$or 逻辑或 { $or: [{ a: 1 }, { b: 2 }] }
$not 逻辑非 { age: { $not: { $gte: 18 } } }
$nor 逻辑或非 { $nor: [{ a: 1 }, { b: 2 }] }
// AND 操作
db.users.find({
  $and: [
    { age: { $gte: 18 } },
    { status: "active" }
  ]
})

// OR 操作
db.users.find({
  $or: [
    { username: "alice" },
    { email: "alice@example.com" }
  ]
})

// 组合条件
db.users.find({
  $and: [
    { $or: [{ age: { $lte: 18 } }, { age: { $gte: 65 } }] },
    { status: "active" }
  ]
})

// NOR 示例
db.users.find({
  $nor: [
    { status: "banned" },
    { role: "admin" }
  ]
})

字段操作符

操作符 说明 示例
$exists 字段存在 { email: { $exists: true } }
$type 字段类型 { age: { $type: "number" } }
$regex 正则匹配 { username: { $regex: "^admin" } }
// 字段存在
db.users.find({ phone: { $exists: true } })
db.users.find({ profile: { $exists: false } })

// 字段类型
db.users.find({ age: { $type: "number" } })
db.users.find({ _id: { $type: "objectId" } })

// 正则表达式(区分大小写)
db.users.find({ username: { $regex: "^admin", $options: "i" } })

// 使用正则的简洁语法
db.users.find({ email: /@gmail\.com$/ })

数组操作符

操作符 说明 示例
$all 包含所有元素 { tags: { $all: ["a", "b"] } }
$elemMatch 数组元素匹配 { scores: { $elemMatch: { $gt: 80 } } }
$size 数组长度 { tags: { $size: 3 } }
// 查询包含特定标签的文章
db.articles.find({ tags: { $all: ["mongodb", "database"] } })

// 查询数组中至少有一个元素满足条件
db.scores.find({
  results: { 
    $elemMatch: { 
      subject: "Math",
      score: { $gte: 90 }
    } 
  }
})

// 查询标签数量为 3 的文档
db.articles.find({ tags: { $size: 3 } })

// 查询空数组
db.articles.find({ tags: { $size: 0 } })

内嵌文档查询

// 精确匹配内嵌文档(字段顺序必须一致)
db.users.find({ profile: { firstName: "Alice", lastName: "Smith" } })

// 查询嵌套字段(使用点号)
db.users.find({ "profile.address.city": "Beijing" })
db.users.find({ "profile.phone.mobile": { $exists: true } })

// 嵌套数组查询
db.orders.find({
  "items.product_id": ObjectId("...")
})

4.3 Update 操作

updateOne - 更新单文档

// 基本更新
db.users.updateOne(
  { username: "alice" },
  { $set: { email: "new_email@example.com" } }
)

// 使用 updateOne 返回结果
db.users.updateOne(
  { username: "alice" },
  { $set: { status: "inactive" } }
).then(result => {
  print("匹配文档数:", result.matchedCount)
  print("修改文档数:", result.modifiedCount)
})

updateMany - 批量更新

// 批量更新
db.users.updateMany(
  { status: "pending" },
  { $set: { status: "active", updated_at: new Date() } }
)

// 更新多个字段
db.products.updateMany(
  { category: "Electronics" },
  { 
    $set: { 
      tax_rate: 0.13,
      currency: "USD"
    }
  }
)

字段操作符

操作符 说明 示例
$set 设置字段值 { $set: { field: value } }
$unset 删除字段 { $unset: { field: 1 } }
$rename 重命名字段 { $rename: { old: new } }
$inc 递增/递减 { $inc: { count: 1 } }
$mul { $mul: { price: 1.1 } }
$setOnInsert 仅插入时设置 { $setOnInsert: { created: new Date() } }
// $set - 设置字段
db.users.updateOne(
  { _id: ObjectId("...") },
  { $set: { "profile.avatar": "https://..." } }
)

// $unset - 删除字段
db.users.updateOne(
  { _id: ObjectId("...") },
  { $unset: { temp_field: 1 } }
)

// $rename - 重命名字段
db.users.updateMany(
  {},
  { $rename: { "first_name": "firstName", "last_name": "lastName" } }
)

// $inc - 递增
db.counters.updateOne(
  { _id: "order_id" },
  { $inc: { seq: 1 } }
)

// $mul - 乘法
db.products.updateMany(
  {},
  { $mul: { price: 1.1 } }  // 所有价格涨 10%
)

数组操作符

操作符 说明 示例
$push 添加元素 { $push: { tags: "new" } }
$pop 删除元素 { $pop: { tags: 1 } }
$pull 移除匹配元素 { $pull: { tags: "old" } }
$addToSet 添加唯一元素 { $addToSet: { tags: "new" } }
$pull + $each 批量移除 { $pull: { tags: { $in: ["a", "b"] } } }
// $push - 添加元素
db.users.updateOne(
  { _id: ObjectId("...") },
  { $push: { tags: "developer" } }
)

// $push + $each - 批量添加
db.users.updateOne(
  { _id: ObjectId("...") },
  { $push: { tags: { $each: ["python", "golang"] } } }
)

// $addToSet - 添加唯一元素(不会重复)
db.users.updateOne(
  { _id: ObjectId("...") },
  { $addToSet: { skills: "mongodb" } }
)

// $pop - 删除最后一个元素 (1) 或第一个元素 (-1)
db.lists.updateOne(
  { _id: ObjectId("...") },
  { $pop: { items: 1 } }
)

// $pull - 移除匹配的元素
db.users.updateOne(
  { _id: ObjectId("...") },
  { $pull: { tags: "old_tag" } }
)

// $pull + $in - 批量移除
db.users.updateOne(
  { _id: ObjectId("...") },
  { $pull: { tags: { $in: ["temp", "unused"] } } }
)

替换文档 - replaceOne

// 替换整个文档(保留 _id)
db.users.replaceOne(
  { _id: ObjectId("...") },
  {
    username: "alice",
    email: "alice@example.com",
    status: "active",
    updated_at: new Date()
  }
)

4.4 Delete 操作

deleteOne - 删除单文档

// 删除单个文档
db.users.deleteOne({ username: "alice" })

// 删除结果
db.users.deleteOne({ _id: ObjectId("...") }).then(result => {
  print("删除文档数:", result.deletedCount)
})

deleteMany - 批量删除

// 批量删除
db.users.deleteMany({ status: "inactive", last_login: { $lt: ISODate("2023-01-01") } })

// 删除所有文档(谨慎使用)
db.temp_data.deleteMany({})

drop - 删除集合

// 删除整个集合(包括索引)
db.users.drop()

// 检查是否删除成功
db.getCollectionNames()  // users 应该不在列表中

dropDatabase - 删除数据库

// 删除当前数据库
db.dropDatabase()

// 删除指定数据库
use mydb
db.dropDatabase()

4.5 查询选项

limit 和 skip

// 限制返回数量
db.users.find().limit(10)

// 跳过前 N 条(分页)
db.users.find().skip(20).limit(10)

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

sort - 排序

// 单字段排序(1 升序,-1 降序)
db.users.find().sort({ created_at: -1 })

// 多字段排序
db.articles.find().sort({
  category: 1,
  view_count: -1,
  created_at: -1
})

// 结合其他操作
db.users.find(
  { status: "active" },
  {},
  { sort: { created_at: -1 }, limit: 10 }
)

countDocuments - 计数

// 统计满足条件的文档数
db.users.countDocuments({ status: "active" })

// 统计集合总文档数
db.users.countDocuments({})

// 使用 estimatedDocumentCount(更快,但不接受查询条件)
db.users.estimatedDocumentCount()

distinct - 去重

// 获取字段的所有不重复值
db.users.distinct("country")

// 带查询条件的去重
db.orders.distinct("status", { created_at: { $gte: ISODate("2024-01-01") } })

// 数组字段去重
db.articles.distinct("tags")

4.6 游标操作

// 遍历游标
const cursor = db.users.find({ status: "active" })
while (cursor.hasNext()) {
  printjson(cursor.next())
}

// 使用 forEach
db.users.find().forEach(user => {
  print(`User: ${user.username}`)
})

// 使用 toArray
const users = db.users.find().toArray()

// 限制和转换
const usernames = db.users.find({}, { username: 1, _id: 0 }).toArray()

💡 实践提示

  1. 使用投影减少数据传输:只查询需要的字段,减少网络开销
  2. 合理使用索引字段查询:确保查询条件包含索引字段
  3. 分页查询时使用游标分页:避免 skip 大量数据导致的性能问题
  4. 更新前先查询:确保更新的文档存在
  5. 使用 $set 避免覆盖整个文档:除非你确实想替换整个文档

📚 继续学习