集合与增删改查
🎯 引言
上一篇我们已经用 Docker 跑起了 mongo-demo 容器,并用 mongosh 连上了 MongoDB。学完这篇文章,你能理解数据库和集合的概念,并完成数据的插入、查询、修改、删除四种基本操作,也就是大家常说的 CRUD。这四种操作是日常使用 MongoDB 的核心,后面的课程都建立在它们之上。
本文所有操作都在 mongosh 中执行。如果还没连上,请先运行:
docker exec -it mongo-demo mongosh -u root -p 123456 --authenticationDatabase admin
🧱 数据库与集合
一个 MongoDB 服务里可以放很多个数据库(Database),每个数据库相互独立。可以把它理解成 Excel:一个数据库就是一个 Excel 文件,里面的每张工作表就是一个集合(Collection),而集合里的一条条数据叫文档(Document)。
切换到本课程的示例数据库 blog_demo:
use blog_demo
blog_demo 不存在,use 也能执行成功。这是 MongoDB 的特点:数据库和集合都不需要提前创建,插入第一条数据时才会真正生成。你可以把 use blog_demo 理解成「声明接下来要操作它」,等真正写入数据那一刻,MongoDB 才会把它建出来。集合也一样,无需提前创建。比如往 user 集合插入第一条数据时,这个集合就会自动出现。如果确实想手动建一个集合(比如配置一些集合级的选项),也可以用:
db.createCollection('user')
use blog_demo 只对当前连接生效。如果你关掉 mongosh 重新连接,需要再执行一次,否则 mongosh 不知道你要操作哪个数据库。这里的 db 是 mongosh 提供的一个变量,代表「当前数据库」,后面的增删改查都会用到它:db.user 就表示当前数据库下的 user 集合。
✨ 插入数据 insertOne
集合是空的,我们先往里放数据。插入一条数据用 insertOne,传一个对象进去:
db.user.insertOne({ username: '张三', age: 25, city: '北京' })
执行成功后会返回类似这样的结果:
{
acknowledged: true,
insertedId: ObjectId('66f1a2b3c4d5e6f7a8b9c0d1')
}
acknowledged: true表示写入已被确认。insertedId是这条文档的_id,由 MongoDB 自动生成,全局唯一,相当于每条数据的身份证号,你不用自己准备。
ObjectId('...') 里的长串字符是你机器上生成的随机值,和本文示例不同是正常的。以后可以根据 _id 精确定位某一条文档。一次插入多条数据用 insertMany,传一个数组:
db.user.insertMany([
{ username: '李四', age: 30, city: '上海' },
{ username: '王五', age: 22, city: '广州' },
{ username: '赵六', age: 28, city: '深圳' },
])
返回结果里会有一个 insertedIds 对象,列出每条新文档的 _id。
💡 查询数据 find
查询用 find,这是日常使用频率很高的操作。不带条件时查出集合里的全部数据:
db.user.find()
执行后能看到刚才插入的四条文档,每条都带着自动生成的 _id。find 默认返回全部字段,不需要像其他数据库那样指定列。
只想查一条时,用 findOne 并写上条件:
db.user.findOne({ username: '张三' })
第一个参数是查询条件,写成一个对象:{ username: '张三' } 表示「username 字段等于张三」。find 也可以带同样的条件,区别是 find 返回所有匹配的文档,findOne 只返回匹配到的第一条:
db.user.find({ city: '上海' })
本文先会用「字段等于某个值」这种简单条件,更丰富的条件写法(大于、小于、多条件组合等)是下一篇的主角。
⚡ 修改数据 updateOne
已有数据写错了怎么办?用 updateOne 来改。比如张三搬到了杭州:
db.user.updateOne({ username: '张三' }, { $set: { city: '杭州' } })
它接收两个参数:
- 第一个参数是条件,和
find的条件写法一样,定位要改哪些文档。 - 第二个参数是修改内容,
$set表示「把指定字段改成指定值」,没有写在$set里的字段保持原样。
执行后返回的结果里有几个值得关注的数字:
{
acknowledged: true,
matchedCount: 1,
modifiedCount: 1,
// ...其他字段省略
}
matchedCount:条件匹配到了几条文档。modifiedCount:实际被改动的有几条。如果新值和旧值一样,匹配到了但不会重复修改,这个数字就是 0。
updateOne 只改匹配到的第一条,想把符合条件的文档全部改掉,用 updateMany:
db.user.updateMany({ city: '广州' }, { $set: { city: '珠海' } })
$set! 如果直接写 db.user.updateOne({ username: '张三' }, { city: '杭州' }),MongoDB 会把它理解成「用 { city: '杭州' } 这个文档整体替换掉原来的文档」,结果是 username 和 age 字段全部丢失,只剩一个 city。这是新手很容易踩的坑:想改一个字段,却顺手把其他字段弄没了。🪤 删除数据 deleteOne
删除数据用 deleteOne 和 deleteMany。比如把赵六这条数据删掉:
db.user.deleteOne({ username: '赵六' })
返回结果里的 deletedCount 告诉你实际删掉了几条。deleteOne 只删匹配到的第一条,deleteMany 会删掉所有匹配的文档:
db.user.deleteMany({ city: '珠海' })
删除是不可逆的,执行前建议先用同样条件的 find 查一遍,确认要删的就是这些数据:
db.user.find({ city: '珠海' })
确认结果没问题,再执行对应的 deleteMany。
db.user.deleteMany({}) 会把集合里的所有文档删光。请养成习惯:执行删除前先用同样的条件 find 一遍,确认匹配到的就是要处理的数据。🧾 小节总结
- 用
use blog_demo切换数据库,数据库和集合在插入第一条数据时才真正创建。 insertOne插入一条文档,insertMany一次插入多条;_id由 MongoDB 自动生成,通过insertedId可以拿到。find()查全部,findOne({ username: '张三' })查一条;查询条件写成对象,find默认返回全部字段。updateOne和updateMany修改数据,第二个参数用$set指定要改的字段;结果里的matchedCount和modifiedCount告诉你匹配了几条、改了几条。deleteOne和deleteMany删除数据;不带条件的批量操作会作用于整个集合,删除前先用同样条件find一遍确认。
❓ 知识问答
Q1:插入数据时必须提前定义好字段吗?
不用。MongoDB 的集合没有固定的字段结构,插入什么字段就存什么字段。不过实际项目里同一个集合的文档还是建议保持一致的字段,代码维护起来更轻松。
Q2:_id 可以自己指定吗?
可以,插入时自己传一个 _id 字段即可,但必须保证唯一,重复会报错。一般直接让 MongoDB 自动生成就好。
Q3:find 查出来数据太多,屏幕看不完怎么办?
mongosh 会分页显示,输入 it(iterate 的缩写)并回车可以翻下一页。实际开发中通常会配合条件和数量限制来查询,后面的篇章会讲到。
Q4:updateOne 时条件匹配到了多条文档,会发生什么?
只会修改匹配到的第一条,其余不动。想全部修改就改用 updateMany。拿不准匹配了多少条时,先用同样的条件 find 一下。
Q5:修改之后怎么确认改成功了?
除了看返回结果里的 modifiedCount,再用 find 把对应文档查出来看一眼即可。养成「改完查一下」的习惯,能及早发现条件写错的问题。
🧪 小练习
在 user 集合中完成下面两步操作:
- 插入一条你自己的数据(用户名随意,年龄和城市填真实的)。
// 请在这里编写代码
- 把上一步插入的这条数据的城市改成「成都」,然后把它查询出来确认。
// 请在这里编写代码
🎉 恭喜你已经掌握集合与增删改查技能啦!下一篇我们学习条件查询,看看如何用更丰富的条件(比较、多条件组合等)从一堆文档里精确找出你想要的那部分。
