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的CRUD操作的更多相关文章

  1. 【翻译】MongoDB指南/CRUD操作(四)

    [原文地址]https://docs.mongodb.com/manual/ CRUD操作(四) 1 查询方案(Query Plans) MongoDB 查询优化程序处理查询并且针对给定可利用的索引选 ...

  2. 【翻译】MongoDB指南/CRUD操作(三)

    [原文地址]https://docs.mongodb.com/manual/ CRUD操作(三) 主要内容: 原子性和事务(Atomicity and Transactions),读隔离.一致性和新近 ...

  3. 【翻译】MongoDB指南/CRUD操作(二)

    [原文地址]https://docs.mongodb.com/manual/ MongoDB CRUD操作(二) 主要内容: 更新文档,删除文档,批量写操作,SQL与MongoDB映射图,读隔离(读关 ...

  4. 【翻译】MongoDB指南/CRUD操作(一)

    [原文地址]https://docs.mongodb.com/manual/ MongoDB CRUD操作(一) 主要内容:CRUD操作简介,插入文档,查询文档. CRUD操作包括创建.读取.更新和删 ...

  5. Mongodb系列- CRUD操作介绍

    ---恢复内容开始--- 一 Create 操作 在MongoDB中,插入操作的目标是一个集合. MongoDB中的所有写入操作在单个文档的层次上都是原子的. For examples, see In ...

  6. MongoDB的CRUD操作(java Util )

    1.保存插入操作: public static synchronized String insert(DBObject record) { DBCollection col = MongoDB.get ...

  7. 实例讲解Springboot整合MongoDB进行CRUD操作的两种方式

    1 简介 Springboot是最简单的使用Spring的方式,而MongoDB是最流行的NoSQL数据库.两者在分布式.微服务架构中使用率极高,本文将用实例介绍如何在Springboot中整合Mon ...

  8. Spring Data MongoDB 一:入门篇(环境搭建、简单的CRUD操作)

    一.简介 Spring Data  MongoDB 项目提供与MongoDB文档数据库的集成.Spring Data MongoDB POJO的关键功能区域为中心的模型与MongoDB的DBColle ...

  9. Mongodb Manual阅读笔记:CH2 Mongodb CRUD 操作

    2 Mongodb CRUD 操作 Mongodb Manual阅读笔记:CH2 Mongodb CRUD 操作Mongodb Manual阅读笔记:CH3 数据模型(Data Models)Mong ...

随机推荐

  1. [ios]关于用FMDB 操作数据库 删除 tableView 后刷新

    刚了解使用fmdb,从数据库获取数据 绑定到一个可变数组classNameItems //从ClassList表取得数据 FMResultSet *classInfo=[db executeQuery ...

  2. Python之路【第七篇】python基础 之socket网络编程

    本篇文章大部分借鉴 http://www.cnblogs.com/nulige/p/6235531.html python socket  网络编程 一.服务端和客户端 BS架构 (腾讯通软件:ser ...

  3. SQL Server 2012安装图文教程

    解析SQL Server 2012安装中心 当系统打开"SQL Server安装中心",则说明我们可以开始正常的安装SQL Server 2012了. SQL Server安装中心 ...

  4. SQL编程篇 (二) 定义与流程控制

    分类: sql编程:标准的sql 编程 * 纯sql 在标准的编程中又分为 sqlserver-->T-sql oracle-->pl-sql(扩展) 变量:在使用变量之前先定义 声明变量 ...

  5. C++类型转换函数

    1.什么是类型转换函数 类型转换函数的作用是将一个类的对象转换成另一类型的数据. class testclass { private: int r; int m; public : testclass ...

  6. python中实现定时器Timer

    实现定时器最简单的办法是就是循环中间嵌time.sleep(seconds), 这里我就不赘述了 # encoding: UTF-8 import threading #Timer(定时器)是Thre ...

  7. "SQL Server does not handle comparison of NText, Text, Xml, or Image data types."

    "SQL Server does not handle comparison of NText, Text, Xml, or Image data types." sql2000 ...

  8. perl 删除过期文件

    #!/usr/bin/perl `find /bak/ >list.txt`; open LIST,"/root/list.txt"; while (<LIST> ...

  9. Linux新手扫盲(转载)

    一. Linux特点 1.免费/开源: 2.支持多线程/多用户: 3.安全性好: 4.对内存和文件管理优越. Linux最小只需4M ——> 嵌入式开发 二. 文件目录 Linux系统所有软硬件 ...

  10. C/C++ makefile自动生成工具(comake2,autotools,linux),希望能为开源做点微薄的贡献!

      序     在linux下C或C++项目开发,Makefile是必备的力气,但是发现手写很麻烦. 在百度有个comake2工具,用于自动生成Makefile工具,而在外边本想找一个同类工具,但发现 ...