共计 7707 个字符,预计需要花费 20 分钟才能阅读完成。
1. 前言
在上一篇文章中,我们介绍了 MongoDB。现在,我们来看下如何在 MongoDB 中进行常规的 CRUD 操作。毕竟,作为一个存储系统,它的基本功能就是对数据进行增删改查操作。
MongoDB 中的增删改查操作,不同于我们熟悉的关系数据库中的操作。在关系数据库中,比如 MySQL,我们通常使用 SQL 语句对数据库进行增(INSERT)删(DELETE)改(UPDATE)查(SELECT)。MongoDB 在对数据进行操作过程中,使用的是 Document 进行数据操作。在对数据库进行操作的时候,使用 Document 来表示需要查询的条件和需要更新的数据,功能类似于关系数据库中的 SQL 语句。接下来,我们来看下在 MongoDB 中是如何通过 Document 来进行 CRUD 操作的。
注意:下面介绍的 CRUD 操作,是在 MongoDB 的 mongo shell 操作的。mongo shell 是一个 JavaScript 交互环境,是 MongoDB 提供的一个客户端。不同的语言有自己对应的驱动和对应的操作 API,但是原理是类似的。相信通过在 mongo shell 中进行 CRUD 操作的介绍,大家也可以举一反三,在不同的语言中操作 MongoDB。
2. INSERT 操作
MongoDB 中的新增操作,把一个新的 Document 插入到一个 Collection 中。
如果该 Collection 不存在,则新增一个新的 Collection。这个和关系数据库有很大的区别,在关系数据库中,我们需要定义数据库的 schema 和表结构,这是 NoSQL(MongoDB 是 NoSQL 的一种)数据库和关系数据库很大的区别。在 MongoDB 中,我们可以在一个 Collection 中包含多个不同结构的 Document(不推荐这样做,一个 Collection 最好具有相同格式的 Document,便于维护和使用)。
关于 ”_id” 字段,当我们新增一个 Document 到 Collection 中的时候,MongoDB 需要每一个新增的 Document 中有一个 ”_id” 字段,MongoDB 把这个字段作为主键,所以要求这个字段在 Collection 中是唯一的。如果新增的 Document 中没有包含 ”_id” 字段,那么 MongoDB 的客户端会在该 Document 中新增一个值为 ObjectId 类型的 ”_id” 字段;如果 MongoDB 服务在新增 Document 的时候发现 Document 中没有 ”_id” 字段,那么 mongod 会新增一个值为 ObjectId 类型的 ”_id” 字段到该 Document 中。
MongoDB 对单个 Document 的写操作是原子的。
MongoDB 提供了如下的方式来进行新增操作:
- db.collection.insert()
- db.collection.insertOne() 3.2 版本新增
- db.collection.insertMany() 3.2 版本新增
2.1 db.collection.insert()
insert 操作可以新增单个 Document,也可以新增多个 Document。如果新增单个 Document,则把需要新增的 Document 作为参数传递给 insert(),如果新增多个 Document,则将多个 Document 的数组作为参数传递个 insert()函数。
比如我们需要在 ”post” 这个 collection 上新增一个 Document 来表示我们的博客中新增了一篇文章,我们可以这么做:
现在,我们给 post 这个 Collection 新增了一个 Document,表示在 Blog 中新增了一篇文章。我们可以看下现在 post 这个 Collection 中是不是有我们新增的 Document。
这里我们使用 findOne()来查询,我们可以看到,我们由于没有在 Document 中包含 ”_id” 字段,所以 MongoDB 自动为我们新增了一个 ”_id” 字段。
insert()函数可以一次新增多个 Document,只要将一个 Document 的数组传递给 insert()就可以了,如:
2.2 db.collection.insertOne()
insertOne()函数是在 3.2 版本中新增的,它用来添加单个 Document。例子如下:
2.3 db.collection.insertMany()
insertMany()函数是 insert 的批量增加的版本,支持一次新增多个 Document,它也是 3.2 版本中新增的函数:
好了,我们简单介绍了下 insert 操作的三个函数,下面,我们已经在 MongoDB 的数据库里新增了几个 Document 了。接下来,是时候开始学习查找操作来查看这些已经存储在 MongoDB 的记录了。
3. QUERY 操作
MongoDB 提供了 db.collection.find()函数来执行查询操作,函数将返回一个游标(cursor),用于遍历查询到的 Documents。find()函数接受两个参数,一个是过滤条件,还有一个是投影。
db.collection.find(<query filter>, <projection>)
- <query filter> 用于查找满足过滤条件的 Document
- <projection>(投影)用于指定被找到的 Document 中需要返回哪些字段,用于限制网络中传输的数据的大小。投影的概念和关系数据库中的投影的概念基本是一致的。
现在,我们可以查询下刚才我们新增的所有的 Documents,通过 find(),我们来看下如何查询:
如果我们没有传递任何参数,或者传递一个 ”{}” 给 find()函数,那会 find()返回 Collection 中所有的 Document。现在,可以看到我们刚才新增的所有的 Documents。
接下来,我们通过指定条件,查询标题为 ”Third Post” 的 Document,我们可以这样做:
发现了么,我们的查询条件其实就是以 Document 方式构造的。在 MongoDB 中,我们可以通过构造不同的 Document 来定义不同的查询条件。
除了使用具体的字段来过滤,我们还可以使用查询操作符来做更加灵活的操作。我们现在需要查找出标题是 ”Third Post” 或 ”Fifth Post” 这两篇 Post 中的任何一篇,我们可以使用 $in 操作符来查询:
除了使用 $in,MongoDB 还提供了很多有用的操作符来帮助构造过滤条件,如 $lt, $gt, $and, $or 等。
3.1 使用子 Document 中值作为过滤条件
现在,我们假设有一篇文章,在 Document 中的存储如下:
现在,我们需要匹配内部 Document 中的条件,比如我们需要查找 author 中 name 是 Duke 的记录,我们可以这样构造我们的筛选条件:
db.post.findOne(
{"author":
{"name": "Duke",
"email": "740313507@qq.com"
}
})
查询结果就是这样的
我们观察这个过滤条件,发现和上面的过滤条件的结构一样,就是 {“field”:”value”} 的格式,只是不同的是,这次的 ”value” 不再是一个普通的值了,而是一个 Document。通过这种方式来过滤,具有一些局限性,我们如果要查找 author 中 name 是 Duke 的记录,我们必须完整指定 author 的值,也就是需要完整的 Document。如果需要只过滤 name,而不关系 email 的值,我们可以这样构造过滤条件:
db.post.findOne({"author.name": "Duke"})
查询到和上面一样的结果
但是,这次我们使用了 ”author.name” 的方式来指定 field。这种类似于成员引用的 ” 点符号 ” 结构的查询,可以用来对内嵌的 Document 中的指定的 field 进行过滤。后面你将会看到,对于数组类型的值,也可以通过类似的方式来构造过滤条件。
3.2 使用数组中的元素作为过滤条件
接下来我们看下如何使用数组中的值来构造过滤条件。最简单的,也是最容易想到的,就是把整个数组作为过滤条件,类似于上面的把整个子 Document 作为过滤条件一样,我们可以这样构造
db.post.findOne({"comments": ["comment one", "comment two"]})
查询的结果如下:
但是,这种方式没法指定数组中的某个值作为过滤条件。如果要使用数组中的某个值作为过滤条件,我们可以这么构造过滤条件:
db.post.find({"comments": "comment two"})
查询到的结果如下:
这里最后使用了 ”pretty()” 方法来格式化输出,输出格式化的数据,便于观察,仅此而已,没有别的用途。我们可以看到,这种方式构造的过滤条件,使用了数组中的一个元素来筛选记录,只要数组中包含了这个元素(不管这个元素的下标),那么就会过滤出这条记录,有点像集合中的 in 操作。这种方式可以匹配数组中的一个元素。
接下来,我们更近一步,我们需要匹配数组的某一个下标位置的元素,那么我们需要使用上面一开始提到的,使用类似匹配子 Document 的那种成员引用(点符号)方式来构造过滤条件:
db.post.find({"comments.0": "comment two"})
查询到的结果如下:
这里,我们使用 {“comments.0”: “comment two”} 的方式指定匹配的条件是:数组 ”comments” 下标为 0 的位置的值为 ”comment two”。这样,就可以过滤掉之前下标为 1 的位置为 ”comment two” 的记录了。可以看出,在使用数组下标构造过滤条件的时候,下标是从 0 开始的。
MongoDB 提供了丰富的操作符来支持构造灵活的过滤条件,这里就先介绍这么点。由于篇幅关系,这里就不再展开了,下次独立写篇文章重点介绍下 MongoDB 中的查询操作。接下来,我们该看下如何在 MongoDB 中进行更新操作。
4. UPDATE 操作
MongoDB 也支持基本的更新操作,它提供了 4 个用于更新的方法:
- db.collection.updateOne() 3.2 版本新增
- db.collection.updateMany() 3.2 版本新增
- db.collection.update()
- db.collection.replaceOne() 3.2 版本新增
这些 Update 方法支持三个参数:
- 用于筛选记录的过滤条件,过滤出需要被更新的记录,过滤条件和 query 中使用的过滤条件类似。
- 一个新的 Document,用于更新部分值或者替换除了 ”_id” 之外的一整个 Document。
- 一个以 Document 格式组织的一组更新选项。
MongoDB 对于单个 Document 的更新操作是原子的。MongoDB 在更新时对于 ”_id” 主键的处理原则是,不管是更新还是替换 Document,都不能更改被更新的 Document 的 ”_id” 主键,如果在替换的时候包含了不同的 ”_id”,那么替换会失败,如:
上面的例子中,我们修改了原先的 ”_id” 值为 1,然后进行替换更新,发现更新失败,提示 ”_id” 值不能被更改。
当我们更新的时候,新的 Document 的大小超过了原先旧的 Document 的大小的时候,更新操作会重新申请一块更大的空间来存放这个新的 Document。
接下来,我们来看下这些更新 API 的用法。
4.1 db.collection.updateOne()
updateOne()函数是在 3.2 版本中新增的一个 API,用于更新一条匹配到的 Document。现在我们需要更新我们的 Post 集合中的标题为 ”First Post” 的 Document,我们想增加一些评论,我们可以这么构造我们的更新表达式:
db.post.updateOne({"title": "First Post"}, {"$set": {"comments": ["comment one"]}})
现在,我们的 ”First Post” 这篇文章就有了一条评论了。这里我们使用 ”$set” 操作符来设置一个新的字段,MongoDB 的更新操作提供了一些有用的操作符来帮助构造更新语句。updateOne()函数会更新第一个被匹配到的 Document。如果要更新多个 Document,我们可以用 updateMany()函数来支持。
4.2 db.collection.updateMany()
updateMany()函数可以对匹配到的所有的 Document 进行更新操作。比如我们想更新所有的文章,让每篇文章都有一个浏览数的字段,表示别浏览的次数。我们可以这样做:
db.post.updateMany({}, {"$set": {"view": 0}})
我们可以看到,通过 updateMany()函数,我们更新了所有的 Document,使得每个 Document 都包含了一个 ”view” 字段,初始值为 0。
4.3 替换 Document
上面提到了两个 API,都是对 Document 中的某个字段进行更新。它们除了可以对 Document 中的单个字段进行更新外,还可以对匹配到的整个 Document 进行替换(除了 ”_id” 属性外,可以替换任何属性)。只要把更新参数改成一个普通的 Document(Document 的结构中不包含操作符),就可以对匹配到的 Document 替换成参数中的 Document。进行 Document 替换更新的时候,需要注意:原先的 Document 中的 ”_id” 属性是不能被更改的,所以新的用于替换的 Document 不能包含 ”_id” 属性,如果包含了 ”_id” 属性,那么这个 ”_id” 属性必须是和被更新的 Document 的 ”_id” 属性是相同的。
除了上面的两个 API 可以用于替换 Document,MongoDB 在 3.2 版本中新增了一个 replaceOne()函数来进行替换操作。现在我们用 replaceOne()来替换一个 Document,我们把 ”title” 是 ”First Post” 的 Document 替换成新的 Document:
db.post.replaceOne({"title": "First Post"}, {"title": "New Post", "content": "new content", "create_time": new Date()})
现在,我们已经把 ”title” 为 ”First Post” 的 Document 替换为了新的 ”title” 为 ”New Post” 的 Document 了。
最后,我们来看下上面三个 API 的集大成者,就是 update()函数,它基本包含了上面提到的三个 API 的所有功能,默认情况下,update()函数会更新匹配到的第一个 Document,如果设置了 ”multi” 选项,那么就可以更新匹配到的所有的 Document。
下面我们使用 update()来更新匹配到的第一个 Document:
db.post.update({"title": "New Post"}, {"$set": {"view": 0}})
好了,更新操作介绍到这里,接下来就是最后一个删除操作了。
5. DELETE 操作
到这里,我想大家都已经了解了 MongoDB 中的 ” 增 ”,” 改 ”,” 查 ” 的功能了,接下来我们来看下 ” 删 ” 这个功能。MongoDB 提供了三个用于删除操作的 API,分别是:
- db.collection.deleteOne()
- db.collection.deleteMany() 3.2 版本新增
- db.collection.remove() 3.2 版本新增
这三个 API 都支持一个过滤条件参数,用于匹配到满足条件的 Document,然后进行删除操作。
从三个 API 的字面意思我们可以看出,deleteOne()会删除匹配到的所有的 Document 中的第一个,而 deleteMany()和 remove()会删除所有匹配到的 Document。
假设我们需要删除 ”title” 为 ”New Post” 的 Document,我们可以用 deleteOne()来操作
db.post.deleteOne({"title": "New Post"})
当删除了 ”title” 为 ”New Post” 的 Document 以后,我们再次去查询的时候,发现这个 Document 确实已经被删除了。deleteOne()用于删除单个 Document,如果我们需要删除所有满足过滤条件的 Document 的话,我们可以用 deleteMany()或者 remove()来实现。
现在我们想删除所有浏览数为 0 的文章,那么我们可以用 deleteMany()或者 remove 来实现:
db.post.remove({"view": 0})
由于我们存储在集合中的所有 Document 的 view 值都是 0,所以上面的操作相当于我们清空了我们的 Collection。
6. 总结
好了,到这里,我们已经简单介绍了 MongoDB 中相关的 CRUD 操作了。相信大家对 MongoDB 的基本操作有了一些简单的认识了。
差不多就先写到这里了,由于文章的主题就是跟大家介绍 MongoDB 的 CRUD 操作,所以上面很多的细节部分没有展开,感兴趣的同学可以去翻阅 MongoDB 的文档来了解,有机会,下次单独拎出来好好总结下。毕竟,我们学东西,得先有个大概的了解以后,才可以有针对的深入,一味的追求细节,往往会迷失方向。
更多 MongoDB 相关教程见以下内容:
CentOS 编译安装 MongoDB 与 mongoDB 的 php 扩展 http://www.linuxidc.com/Linux/2012-02/53833.htm
CentOS 6 使用 yum 安装 MongoDB 及服务器端配置 http://www.linuxidc.com/Linux/2012-08/68196.htm
Ubuntu 13.04 下安装 MongoDB2.4.3 http://www.linuxidc.com/Linux/2013-05/84227.htm
MongoDB 入门必读(概念与实战并重) http://www.linuxidc.com/Linux/2013-07/87105.htm
Ubunu 14.04 下 MongoDB 的安装指南 http://www.linuxidc.com/Linux/2014-08/105364.htm
《MongoDB 权威指南》(MongoDB: The Definitive Guide)英文文字版[PDF] http://www.linuxidc.com/Linux/2012-07/66735.htm
Nagios 监控 MongoDB 分片集群服务实战 http://www.linuxidc.com/Linux/2014-10/107826.htm
基于 CentOS 6.5 操作系统搭建 MongoDB 服务 http://www.linuxidc.com/Linux/2014-11/108900.htm
MongoDB 的详细介绍:请点这里
MongoDB 的下载地址:请点这里
本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-08/134153.htm