跳到主要内容

CRUD 操作

CRUD 是 Create(创建)、Read(读取)、Update(更新)、Delete(删除)的缩写,是数据库操作的基础。掌握 CRUD 操作是使用 MongoDB 的第一步,也是最重要的一步。

CRUD 操作概述

什么是 CRUD?

CRUD 代表了应用程序对数据库的四种基本操作:

操作MongoDB 方法说明
CreateinsertOne, insertMany创建新文档
Readfind, findOne查询文档
UpdateupdateOne, updateMany, replaceOne更新文档
DeletedeleteOne, deleteMany删除文档

MongoDB CRUD 的特点

原子性保证:在 MongoDB 中,所有写操作在单文档级别都是原子的。这意味着如果一个更新操作修改了文档中的多个字段,要么所有字段都更新成功,要么都不更新,不会出现部分更新的情况。

// 单文档操作的原子性示例
db.accounts.updateOne(
{ _id: 'account_1' },
{
$inc: { balance: -100 }, // 扣款
$push: { transactions: { // 记录交易
type: 'withdrawal',
amount: 100,
date: new Date()
}}
}
)
// 以上两个操作要么都成功,要么都不执行

自动集合创建:当向不存在的集合插入文档时,MongoDB 会自动创建该集合。

// products 集合不存在,插入后自动创建
db.products.insertOne({ name: 'iPhone', price: 999 })

插入文档(Create)

MongoDB 提供了多种插入文档的方法。

insertOne - 插入单个文档

// 语法
db.collection.insertOne(<document>)

// 示例
db.users.insertOne({
username: "zhangsan",
email: "[email protected]",
age: 28,
createdAt: new Date()
})

返回结果

{
acknowledged: true,
insertedId: ObjectId("...")
}

insertMany - 插入多个文档

// 语法
db.collection.insertMany([<document1>, <document2>, ...])

// 示例
db.products.insertMany([
{ name: "iPhone 15", price: 999, category: "electronics" },
{ name: "MacBook Pro", price: 1999, category: "electronics" },
{ name: "AirPods Pro", price: 249, category: "audio" }
])

返回结果

{
acknowledged: true,
insertedIds: {
'0': ObjectId("..."),
'1': ObjectId("..."),
'2': ObjectId("...")
}
}

insert - 通用插入方法

// 可以插入单个或多个文档
db.users.insert({ username: "test" })
db.users.insert([{ a: 1 }, { a: 2 }])

注意事项

  1. 原子性:单文档操作是原子的
  2. _id 字段:如果未指定,MongoDB 自动生成 ObjectId
  3. 集合创建:如果集合不存在,会自动创建
// 显式指定 _id
db.users.insertOne({
_id: "user001",
username: "zhangsan",
email: "[email protected]"
})

// 插入嵌套文档
db.orders.insertOne({
orderId: "ORD001",
customer: {
name: "张三",
address: {
city: "Beijing",
street: "Main St 123"
}
},
items: [
{ product: "iPhone", quantity: 1 },
{ product: "Case", quantity: 2 }
],
total: 1030.00,
status: "pending"
})

查询文档(Read)

find - 查询文档

// 语法
db.collection.find(<query>, <projection>)

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

// 带条件查询
db.users.find({ age: 28 })

// 只返回特定字段
db.users.find({}, { username: 1, email: 1 })

findOne - 查询单个文档

// 返回匹配的第一个文档
db.users.findOne({ username: "zhangsan" })

// 带投影
db.users.findOne({ age: 28 }, { username: 1, email: 1 })

查询条件

1. 精确匹配

// 查询 age = 28 的用户
db.users.find({ age: 28 })

// 查询嵌套字段
db.orders.find({ "customer.name": "张三" })

2. 比较操作符

操作符说明示例
$eq等于{ age: { $eq: 25 } }
$ne不等于{ age: { $ne: 25 } }
$gt大于{ age: { $gt: 25 } }
$gte大于等于{ age: { $gte: 25 } }
$lt小于{ age: { $lt: 30 } }
$lte小于等于{ age: { $lte: 30 } }
$in在数组中{ age: { $in: [25, 30] } }
$nin不在数组中{ age: { $nin: [25, 30] } }
// 查询年龄大于 25 的用户
db.users.find({ age: { $gt: 25 } })

// 查询年龄在某个范围内的用户
db.users.find({ age: { $gte: 20, $lte: 30 } })

// 查询特定状态的用户
db.orders.find({ status: { $in: ["pending", "processing"] } })

3. 逻辑操作符

操作符说明示例
$and逻辑与{ $and: [{ age: { $gt: 20 } }, { age: { $lt: 30 } }] }
$or逻辑或{ $or: [{ status: "active" }, { role: "admin" }] }
$not逻辑非{ age: { $not: { $gt: 30 } } }
$nor都不是{ $nor: [{ status: "deleted" }, { banned: true }] }
// AND: 年龄大于 20 且小于 30
db.users.find({
$and: [
{ age: { $gt: 20 } },
{ age: { $lt: 30 } }
]
})

// OR: 状态为 active 或 role 为 admin
db.users.find({
$or: [
{ status: "active" },
{ role: "admin" }
]
})

// 简化写法(MongoDB 自动使用 $and)
db.users.find({ age: { $gt: 20, $lt: 30 } })

4. 元素操作符

操作符说明示例
$exists字段是否存在{ email: { $exists: true } }
$type字段类型{ age: { $type: "number" } }
// 查找有 email 字段的用户
db.users.find({ email: { $exists: true } })

// 查找 email 字段为字符串类型
db.users.find({ email: { $type: "string" } })

5. 数组操作符

操作符说明示例
$all包含所有元素{ tags: { $all: ["a", "b"] } }
$elemMatch数组元素匹配{ scores: { $elemMatch: { $gt: 80 } } }
$size数组长度{ tags: { $size: 3 } }
// 查询 tags 包含 "admin" 和 "developer" 的用户
db.users.find({ tags: { $all: ["admin", "developer"] } })

// 查询数组中至少有一个元素大于 90 的文档
db.students.find({
scores: { $elemMatch: { $gt: 90 } }
})

// 查询 tags 数组长度为 3 的文档
db.users.find({ tags: { $size: 3 } })

投影(Projection)

投影用于指定返回哪些字段:

// 只返回 username 和 email(默认包含 _id)
db.users.find({}, { username: 1, email: 1 })

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

// 嵌套字段投影
db.orders.find({}, { "customer.name": 1, total: 1 })

// 条件投影:数组中符合条件的第一个元素
db.students.find(
{ scores: { $elemMatch: { $gt: 80 } } },
{ name: 1, "scores.$": 1 }
)

排序、分页、限制

// sort: 排序(1 升序,-1 降序)
db.users.find().sort({ age: 1 }) // 按年龄升序
db.users.find().sort({ createdAt: -1 }) // 按创建时间降序

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

// skip: 跳过指定数量
db.users.find().skip(20)

// 分页查询:第 2 页,每页 10 条
db.users.find().sort({ createdAt: -1 }).skip(10).limit(10)

// 组合使用
db.products.find({ category: "electronics" })
.sort({ price: 1 })
.limit(20)
.skip(0)

更新文档(Update)

更新操作是 CRUD 中最复杂的部分,MongoDB 提供了丰富的更新操作符来满足各种需求。

更新操作的选择

在选择更新方法时,需要考虑以下几点:

方法更新范围说明
updateOne单个文档只更新第一个匹配的文档
updateMany多个文档更新所有匹配的文档
replaceOne单个文档替换整个文档(保留 _id)

updateOne - 更新单个文档

// 语法
db.collection.updateOne(<filter>, <update>, <options>)

// 示例:将用户名改为 "zhangsan" 的用户的年龄更新为 30
db.users.updateOne(
{ username: "zhangsan" },
{ $set: { age: 30 } }
)

updateMany - 更新多个文档

// 更新所有年龄大于 30 的用户的状态
db.users.updateMany(
{ age: { $gt: 30 } },
{ $set: { status: "senior" } }
)

replaceOne - 替换文档

替换整个文档(除了 _id):

db.users.replaceOne(
{ username: "zhangsan" },
{
username: "zhangsan",
email: "[email protected]",
age: 30
}
)

更新操作符详解

MongoDB 提供了多种更新操作符,每种操作符都有特定的用途。理解这些操作符是掌握 MongoDB 更新操作的关键。

$set - 设置字段值

$set 是最常用的更新操作符,用于设置字段的值。如果字段不存在,则会创建该字段。

// 更新单个字段
db.users.updateOne(
{ _id: 1 },
{ $set: { age: 30 } }
)

// 更新嵌套字段(使用点符号)
db.orders.updateOne(
{ _id: 1 },
{ $set: { "customer.address.city": "Shanghai" } }
)

// 同时更新多个字段
db.users.updateOne(
{ _id: 1 },
{ $set: {
name: "张三",
email: "[email protected]",
"profile.bio": "软件工程师"
}}
)

重要提示$set 只更新指定的字段,不会影响文档中的其他字段。

$unset - 删除字段

$unset 用于删除文档中的字段,字段的值可以是任意值(通常使用空字符串 ""1)。

// 删除单个字段
db.users.updateOne(
{ _id: 1 },
{ $unset: { age: "" } }
)

// 删除多个字段
db.users.updateOne(
{ _id: 1 },
{ $unset: { age: "", tempField: "" } }
)

// 删除嵌套字段
db.orders.updateOne(
{ _id: 1 },
{ $unset: { "customer.address": "" } }
)

$inc - 数值递增/递减

// 年龄增加 1
db.users.updateOne(
{ _id: 1 },
{ $inc: { age: 1 } }
)

// 积分减少 50
db.users.updateOne(
{ _id: 1 },
{ $inc: { points: -50 } }
)

$mul - 数值相乘

// 价格翻倍
db.products.updateMany(
{ category: "electronics" },
{ $mul: { price: 2 } }
)

$rename - 重命名字段

// 将 username 重命名为 name
db.users.updateMany(
{},
{ $rename: { "username": "name" } }
)

$push - 向数组添加元素

// 添加一个元素到 tags 数组
db.users.updateOne(
{ _id: 1 },
{ $push: { tags: "vip" } }
)

// 使用 $each 添加多个元素
db.users.updateOne(
{ _id: 1 },
{ $push: { tags: { $each: ["vip", "premium"] } } }
)

// 使用 $sort 排序后添加
db.scores.updateOne(
{ _id: 1 },
{ $push: { scores: { $each: [95, 88], $sort: -1 } } }
)

$pull - 从数组删除元素

// 删除 tags 数组中的 "old" 元素
db.users.updateOne(
{ _id: 1 },
{ $pull: { tags: "old" } }
)

// 删除大于 80 的分数
db.scores.updateOne(
{ _id: 1 },
{ $pull: { scores: { $gt: 80 } } }
)

$addToSet - 添加唯一元素

// 只添加不存在的元素
db.users.updateOne(
{ _id: 1 },
{ $addToSet: { tags: "new" } }
)

upsert - 插入或更新

如果文档不存在,则插入新文档:

// 如果没有 "testuser",则创建新用户
db.users.updateOne(
{ username: "testuser" },
{ $set: { email: "[email protected]", age: 25 } },
{ upsert: true }
)

删除文档(Delete)

deleteOne - 删除单个文档

// 删除第一个匹配的文档
db.users.deleteOne({ username: "zhangsan" })

deleteMany - 删除多个文档

// 删除所有年龄大于 100 的用户
db.users.deleteMany({ age: { $gt: 100 } })

// 删除集合中的所有文档
db.users.deleteMany({})

remove - 通用删除方法

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

// 删除单个文档
db.users.remove({ username: "zhangsan" }, true)

drop - 删除整个集合

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

dropDatabase - 删除数据库

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

实战示例

完整的 CRUD 操作流程

// 1. 插入数据
db.products.insertMany([
{ name: "iPhone 15", price: 999, category: "electronics", stock: 100 },
{ name: "MacBook Pro", price: 1999, category: "electronics", stock: 50 },
{ name: "Coffee Maker", price: 89, category: "appliances", stock: 200 }
])

// 2. 查询数据
// 查询所有电子产品
db.products.find({ category: "electronics" })

// 查询价格小于 1000 的产品
db.products.find({ price: { $lt: 1000 } })

// 3. 更新数据
// iPhone 15 降价
db.products.updateOne(
{ name: "iPhone 15" },
{ $set: { price: 899, onSale: true } }
)

// 所有电子产品库存减少 10
db.products.updateMany(
{ category: "electronics" },
{ $inc: { stock: -10 } }
)

// 4. 删除数据
// 删除库存为 0 的产品
db.products.deleteMany({ stock: { $lte: 0 } })

批量操作与事务

// 批量写入(更高效)
db.products.bulkWrite([
{ insertOne: { document: { name: "Product A", price: 100 } } },
{ insertOne: { document: { name: "Product B", price: 200 } } },
{ updateOne: { filter: { name: "Product A" }, update: { $set: { price: 120 } } } },
{ deleteOne: { filter: { name: "Product C" } } }
])

小结

本章我们学习了:

  1. 插入文档:insertOne、insertMany、insert
  2. 查询文档:find、findOne,条件和投影
  3. 更新文档:updateOne、updateMany、replaceOne
  4. 删除文档:deleteOne、deleteMany、drop

这些是 MongoDB 最基础也是最重要的操作,必须熟练掌握。