当前位置:首页 > 问答 > 正文

MongoDB中文档和文件怎么存取,边学边记的那些操作细节分享

(引用来源:MongoDB官方中文文档、个人实践笔记)

说到MongoDB存东西,主要就分两大块:文档和文件,文档就是那种像JSON一样结构灵活的数据,比如一个用户信息,里面有名字、年龄、地址啥的,这是MongoDB最拿手的,文件呢,指的是大文件,比如一张高清图片、一个视频、一个很大的压缩包,这个MongoDB也有办法存,用的是另一个叫GridFS的机制,我边学边记,把一些觉得容易迷糊的操作细节分享一下。

第一部分:文档的存取,那些琐碎但重要的细节

文档是存在集合里的,集合就像传统数据库里的表,存文档用insertOne或者insertMany,这里有个小细节,如果你不指定_id字段,MongoDB会自动帮你生成一个ObjectId作为主键,但如果你自己指定了_id,比如用数字或者字符串,那就要保证它在集合里是唯一的,不然插入会失败,我一开始就犯过这个错,自己设了个_id: 1,结果插第二条同样_id的数据时就报错了。

查文档最常用的是find,条件查询的时候,字段名要加引号,虽然有时候不加可能也行,但养成好习惯避免出错,比如找名字是“张三”的:db.users.find({"name": "张三"}),还有个细节是查询返回的字段控制,用第二个参数,{字段名: 1}表示要显示,{字段名: 0}表示不显示。_id字段默认是显示的,如果不想看,必须显式地写上{"_id": 0},我刚开始老是忘记设置这个,结果满屏都是_id,看着乱。

更新文档用updateOneupdateMany,这里有个大坑,就是$set操作符,如果你想修改某个字段的值,必须$set,比如把张三的年龄改成30:db.users.updateOne({"name": "张三"}, {$set: {"age": 30}}),如果我直接写成db.users.updateOne({"name": "张三"}, {"age": 30}),那就不是更新年龄了,而是会把整条文档替换成只剩下age: 30这一个字段,其他字段全没了!这个错误我早期犯过好几次,损失了数据,教训深刻,除非你是想替换整个文档(用replaceOne更明确),否则更新部分字段一定要用$set

MongoDB中文档和文件怎么存取,边学边记的那些操作细节分享

删除文档用deleteOnedeleteMany,这个操作要特别小心,尤其是deleteMany,如果不加条件,就会删掉整个集合的所有文档!相当于清空表,所以执行删除前,最好先用find带上同样的条件查一下,确认是不是你要删的那些数据,这是血的教训换来的习惯。

第二部分:大文件的存取,用GridFS

当文件大小超过16MB(这是MongoDB单个文档的大小限制)时,就不能用普通文档来存了,得用GridFS,GridFS在背后其实是把大文件分割成多个小的文档(称为chunk)存在两个集合里:fs.files存文件的元信息(比如文件名、大小、上传时间),fs.chunks的分块数据。

存大文件,我通常用命令行工具mongofiles,或者在程序里用驱动提供的方法,用mongofiles -d mydb put my_large_video.mp4命令,它就会把my_large_video.mp4这个文件存到数据库mydb的GridFS中,这个过程是自动的,你不用操心怎么分块。

MongoDB中文档和文件怎么存取,边学边记的那些操作细节分享

取文件也一样,用mongofiles -d mydb get my_large_video.mp4,它会自动根据fs.filesfs.chunks里的数据把文件还原回来。

这里有个操作细节是查看GridFS里存了哪些文件,你不能直接去fs.files集合里用普通的find看,因为文件内容不在那儿,但你可以用mongofiles -d mydb list命令来列出所有文件名,或者在MongoDB的shell里,用db.fs.files.find()来查看文件的元数据信息,这样能看到文件名、大小、MD5校验和等,我记得有一次我忘了文件名,就是通过查fs.files集合找到的。

还有一个细节是,GridFS默认的块大小是255KB,对于非常大的文件,这个分块机制有利于分段读取,比如在线看视频时可以跳着看,而不用加载整个文件,这个块大小通常不需要我们改,知道有这么回事就行。

总结一下

存文档,核心是记牢集合、插入、查询、更新(别忘了$set)、删除这几个基本操作,注意细节别踩坑,存大文件,就想到GridFS,它像一个大文件的“分卷存储”系统,通过命令行工具或编程驱动来操作很方便,省去了自己管理分块的麻烦,这些东西光看理论容易忘,多动手试几次,特别是那个更新不用$set的坑,自己踩一次就永远记住了。