MongoDB学习系列 -- 数据库、集合、文档的CURD

前面一篇章节我们已经对MongoDB的基本概念有了一个大概的了解,从今天开始,我们将进行更细粒度的学习,首先就是数据库、集合、文档的CURD操作。

为了便于操作,减少学习难度,我们这里使用javascript shell →mongo来进行代码的演示。OK,废话不多说,直接开搞。

注意:更新、删除操作一定要有筛选条件,其中,有些高版本中已经默认必须要填写筛选条件了。同时也要注意,筛选条件要具有代表性,如果你想要更新某个文档的信息,那么你就需要使用能唯一标识该文档的条件,否则会造成多余的删除或更新的失败。

一、数据库

添加

数据库的添加和切换命令是同一个:use。当数据库存在时就切换到相应的数据库,不存在时会自动创建该数据库。

show dbs
admin  0.078GB
local  0.078GB
test   0.078GB
> db.time.insert({"time":new Date()});
WriteResult({ "nInserted" : 1 })
> show dbs
admin  0.078GB
local  0.078GB
newdb  0.078GB
test   0.078GB
修改

这个就不必多说了,集合已经文档的CURD操作都是对数据库的修改。

删除

数据库删除命令是dropDatabase,db表示当前数据库,执行db.dropDatbase会物理删除当前数据库下的集合以及文档信息。

db.dropDatabase()
{ "dropped" : "newdb", "ok" : 1 }
> show dbs
admin  0.078GB
local  0.078GB
test   0.078GB

二、集合

新增

集合的新增使用creatCollection命令:db.createCollection(name, options)

参数说明:

  • name: 要创建的集合名称
  • options: 可选参数, 指定有关内存大小及索引的选项

options 可以是如下参数:

字段 类型 描述
capped 布尔 (可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。
当该值为 true 时,必须指定 size 参数。
autoIndexId 布尔 (可选)如为 true,自动在 _id 字段创建索引。默认为 false。
size 数值 (可选)为固定集合指定一个最大值(以字节计)。
如果 capped 为 true,也需要指定该字段。
max 数值 (可选)指定固定集合中包含文档的最大数量。

在插入文档时,MongoDB 首先检查固定集合的 size 字段,然后检查 max 字段。

下面在test数据库下创建了一个最大有1000个文档的固定集合demoColl。
db.createCollection(
Display all 186 possibilities? (y or n)
> db.createCollection('demoColl',{ capped : true, autoIndexId : true, size : 
...    6142800, max : 10000 })
{ "ok" : 1 }
> show collections
article
blog
demoColl
student
system.indexes
version
ydm

另外,当你向集合中插入一些文档时,如果该集合不存在,MongoDB 会自动创建集合。

修改

集合内文档的CURD操作也即是集合的修改,下面会介绍。

删除

删除使用drop命令

删除我们上面创建的demoColl集合。

show collections
article
blog
demoColl
student
system.indexes
version
ydm
> db.demoColl.drop()
true
> show collections
article
blog
student
system.indexes
version
ydm

三、文档

添加:

集合的添加场景比较多

(1)使用insert命令添加文档

(2)使用$upsert这类操作时,如果该文档不存在,会自动创建一个对应的文档。

1、单个文档插入

db.ydm.insert( { 'name':'xiaohong', 'sex': 0, 'address':'beijing','phone':'187*****' })

2、批量插入

 db.ydm.insert(
... [
... {
... 'name':'lily',
... 'sex': 1},
... {
... 'name':'xiaoming',
... 'sex': 0,}
... ])
更新:

文档的更新有文档替换修改器两种方式

std = db.student.findOne({"name":'lily'})
{
	"_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
	"name" : "lily",
	"age" : 29,
	"sex" : 1,
	"address" : "bj"
}

现在假如我们想对上面的一个文档进行age+1操作

(1)文档替换

> std.age = std.age+1
30
> db.student.update({'name':'lily'},std)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.findOne({"name":'lily'})
{
	"_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
	"name" : "lily",
	"age" : 30,
	"sex" : 1,
	"address" : "bj"
}

OK,我们实现了age+1的操作。仔细观察一下上面的操作过程,会不会觉得有点繁琐,而且有点低效?

注意:update命令其实有四个参数:第三个参数表示upsert操作(true表示为upsert,默认为false),第四个参数为是否多文档更新(true为多文档更新,默认为false)。

upsert:upsert更新是一种特殊的更新,要是没有文档符合更新条件,就会以这个条件和更新文档为基础创建一个新的文档。如果找到了匹配的文档,则会正常更新。upsert非常方便,同一套代码既可创建文档也可更新文档。

db.student.update({"name":'wangwu'},{$inc:{'age':1}},true)
WriteResult({
	"nMatched" : 0,
	"nUpserted" : 1,
	"nModified" : 0,
	"_id" : ObjectId("5b40d2275c99dd00d4d1585b")
})
> db.student.findOne({"name":'wangwu'});
{
	"_id" : ObjectId("5b40d2275c99dd00d4d1585b"),
	"name" : "wangwu",
	"age" : 1
}

没有满足条件的文档,开启upsert之后就会插入一条相应文档。同时,更新之后有一个WriteResult的对象返回,我们看到有一个nUpserted的字段,这个字段表示有多少个upsert操作。是不是很方便?如果没有这个选项,我们估计就要先查询有没有匹配文档,有的话就更新,没有的话就插入,这样会很繁琐,而且在不同线程之间可能会产生竞态问题。upsert是原子性的,能有效避免竞态问题,而且更高效,代码更简洁。

多文档更新:使用update命令的时候默认只对match到的第一个文档执行update操作,可以通过设置update的第四个参数为true来实现对match到文档的全部update。

db.student.find();
{ "_id" : ObjectId("5b3f782598781e88f5e3462f"), "name" : "xiaoming", "sex" : 1 }
{ "_id" : ObjectId("5b401a87d3c72e6c8d190cf7"), "date" : ISODate("2018-07-07T01:42:31.010Z") }
{ "_id" : ObjectId("5b3f79a5d16c252df9d143fc"), "name" : "lily", "age" : 32, "sex" : 1, "address" : "bj" }
{ "_id" : ObjectId("5b40d1ea5c99dd00d4d1585a"), "name" : "lucy", "age" : 1 }
{ "_id" : ObjectId("5b40d2275c99dd00d4d1585b"), "name" : "wangwu", "age" : 1 }
> db.student.update({"sex":1},{$inc:{'age':1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.update({"sex":1},{$inc:{'age':1}},false,true)
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
> db.student.find();
{ "_id" : ObjectId("5b3f782598781e88f5e3462f"), "name" : "xiaoming", "sex" : 1, "age" : 2 }
{ "_id" : ObjectId("5b401a87d3c72e6c8d190cf7"), "date" : ISODate("2018-07-07T01:42:31.010Z") }
{ "_id" : ObjectId("5b3f79a5d16c252df9d143fc"), "name" : "lily", "age" : 33, "sex" : 1, "address" : "bj" }
{ "_id" : ObjectId("5b40d1ea5c99dd00d4d1585a"), "name" : "lucy", "age" : 1 }
{ "_id" : ObjectId("5b40d2275c99dd00d4d1585b"), "name" : "wangwu", "age" : 1 }

(2)修改器

常见的操作都是只更新文档中的部分内容,利用原子的更新修改器可以使这部分更新更为高效。更新修改器是一种特殊的键,用来指定复杂的更新操作,比如调整、增加或者删除键,还可能是操作数组或者内嵌文档。

常见的修改器有$inc、$set 、$unset。

$set用来更新文档中某一键的值,甚至可以修改键值的数据类型。$unset操作与$set操作相反,用来删除某一个键的值。

$inc 用来给文档中整形以及浮点类型的数据进行加/减操作,其中变化的值必须要为数字

而且,$inc因为不需要修改文档的大小,能就地修改,运行非常快。对于$set修改器,如果文档大小不发生改变时也能立即修改,否则性能也会有所下降。

db.student.update({"sex":1,'name':'lily'},{$inc:{'address':1}})
WriteResult({
	"nMatched" : 0,
	"nUpserted" : 0,
	"nModified" : 0,
	"writeError" : {
		"code" : 16837,
		"errmsg" : "Cannot apply $inc to a value of non-numeric type. {_id: ObjectId('5b3f79a5d16c252df9d143fc')} has the field 'address' of non-numeric type String"
	}
})

其中,$set以及$inc都会在键不存在时创建这个键,下面验证一下。

db.student.findOne({"sex":1})
{
	"_id" : ObjectId("5b3f782598781e88f5e3462f"),
	"name" : "yuandongming",
	"age" : 33,
	"sex" : 1
}
> db.student.findOne({"sex":1,'name':'lily'})
{
	"_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
	"name" : "lily",
	"age" : 30,
	"sex" : 1,
	"address" : "bj"
}
> db.student.update({"sex":1,'name':'lily'},{$set:{"phone":'183*****'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.findOne({"sex":1,'name':'lily'})
{
	"_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
	"name" : "lily",
	"age" : 30,
	"sex" : 1,
	"address" : "bj",
	"phone" : "183*****"
}
db.student.update({"sex":1,'name':'lily'},{$unset:{'phone':'183*****'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.findOne({"sex":1,'name':'lily'})
{
	"_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
	"name" : "lily",
	"age" : 31,
	"sex" : 1,
	"address" : "bj"
}

OK,对于上面我们使用文档替换完成的age+1操作用$inc修改器又将如何操作呢?

db.student.update({"sex":1,'name':'lily'},{$inc:{'age':1}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.student.findOne({"sex":1,'name':'lily'})
{
	"_id" : ObjectId("5b3f79a5d16c252df9d143fc"),
	"name" : "lily",
	"age" : 31,
	"sex" : 1,
	"address" : "bj"
}

OK,使用修改器是不是更简介,更高效?

除了上面列出的三个修改器之外,还有一个数组修改器,关于这部分就不在这一章节展开了。

上面我们列举的这些文档修改以及修改器修改在命令发出之后,只能通过getLastError来获取有限的信息,并不能返回已更新的文档,这个其实可以通过findAndModify命令来实现,这里就不再展开了。

查询:

关于查询的知识,请参考这篇文章 --MongoDB学习系列 -- 文档查询

删除:

删除是物理性删除,数据不可恢复。

db.article.find()
{ "_id" : ObjectId("5b3f76e398781e88f5e3462d"), "title" : "hello world", "date" : "20180707", "writer" : "zhangpeng" }
> db.article.remove({"writer" : "zhangpeng"})
WriteResult({ "nRemoved" : 1 })
> db.article.find()

注:

(1)删除文档数据时集合原有的索引并不会删除。

(2)文档插入的时候如果有多条文档,最好使用批量插入功能(减少Tcp连接数)。

(3)文档插入过程:驱动程序将数据转换成BSON的形式,接着插入到数据库中,这个过程中,数据库会进行两个校验:_id和文档大小校验,如果两个都OK的话,数据直接入库,这样也避免了各种在关系型数据库系统下常见的注入漏洞。

(4)文档的插入、更新、删除操作都是瞬间完成的,这是因为这些操作不需要等待数据库的响应。这并不异步操作,客户端在发出命令之后就不再操心数据库的响应了,可以立即去干别的事情了。当然也可以使用getlastError命令来检查是否成功执行,但是,这会对性能方面产生影响。

MongoDB学习系列 -- 基础知识了解

MongoDB学习系列 -- 常见各种限制

MongoDB学习系列 -- 数组修改

MongoDB学习系列 -- 文档查询

猜你喜欢

转载自blog.csdn.net/ydm19891101/article/details/80956037