(4)详解MongoDB的创建、更新、删除文档

1. 插入

 

1.1 命令:insert

1.2 举例:

> db.blog.insert({"author":"tian","title":"my first mongodb blog"})

查询结果:

> db.blog.find()
{ "_id" : ObjectId("500bb4b44daafbf976598437"), "author" : "tian", "title" : "my
first mongodb blog" }

1.3 说明:

1)当我们要插入的集合(这里是blog)不存在时,mongodb会在第一次插入式自动创建一个;

2)插入的每一条文档,除了我们制定的键(这里有2个键,author和title),还会自动增加一个_id键,相当于关系型数据库的主键,如果我们没有指定的话。

该键对于一个集合必须是唯一的,它可以使任意类型,默认是ObjectId对象。

由于mongodb一开始设计就是用来作为分布式数据库的,因此没有采用自增长的方式来创建_id键,因为在不同的服务器上同步自增长主键费时又费力。

关于ObjectId,更多可以参考:http://www.mongodb.org/display/DOCS/Object+IDs

当然我们也可以自己指定:

> db.blog.insert({"_id":2012,"author":"tian","title":"my 2nd mongodb blog"})
> db.blog.find()
{ "_id" : ObjectId("500bb4b44daafbf976598437"), "author" : "tian", "title" : "my
 first mongodb blog" }
{ "_id" : 2012, "author" : "tian", "title" : "my 2nd mongodb blog" }
1.4 插入原理:
当我们将数据插入到mongodb数据库时,数据会被转换成BSON的形式(BSON是mongodb存储数据的形式,类似JSON的,是轻量的二进制格式,
能将mongodb的所有文档表示为字节字符串,数据库能理解BSON,存在磁盘上的文档也是BSON格式,更多请参考: http://www.bsonspec.org/),
然后存入数据库。
在这个阶段,mongodb只检查2件事:
1)是否包含_id键;
2)文档是否超过16M,注意,这里的大小是转成BSON格式以后的大小,可以通过Object.bsonsize(your-object)查看大小;(以mongodb 1.8为准)
只要这两点满足,就会将文档原本的存入到数据库
这样做有好处也有副作用,副作用就是可以插入无效的数据,好处就是可以使数据库更安全,远离注入式攻击,因为插入不执行代码。
 

 

 

2. 删除
 

2.1 命令:remove

 

2.2 举例:

> db.blog.remove({"title":"my first mongodb blog"})
> db.blog.remove()
2.3 说明:
1) 在上面的2个例子中,第一个例子的remove接受一个参数,用于限定要删除的文档,第二个例子没有指定参数,注意,这种操作是很危险的,因为它将删除整个集合里的文档,但是集合以及索引会被保留,第二个例子等价于db.blog.remove({})
 

2) 删除是永久性的,不能撤销,也不能恢复

 

3) 删除文档的速度相当快,如果要删除整个集合里的文档,可以采用db.drop_collection(your-collection),然后重建集合和索引,该方法速度非常快,唯一的缺点是整个集合都被删除了,包括索引

 

4) 根据_id键来删除文档的效率是最高的

 

5) 考虑一种比较极端,或者高并发可能发生的情况,当你要删除一个集合里的文档时,刚好有另一个进程在update其中的文档,在这种情况下,正被update的文档是不会被删除的,如果这并不是你想要的,可以通过制定参数$atomic参数为true来删除所有满足条件的文档,如:db.blog.remove({"author":"tian",$atomic:true}),当然,这样做也是有副作用的,就是当我们执行remove操作的时候,将阻止其他操作。

 

3. 更新

 

3.1 命令:update

 

3.2 举例:

 
> var mypost = db.blog.findOne({"_id":ObjectId("500bc4304daafbf976598439")})
> mypost.author="tian.chen"
tian.chen
> mypost
{
        "_id" : ObjectId("500bc4304daafbf976598439"),
        "author" : "tian.chen",
        "title" : "my first mongodb blog"
}
> db.blog.update({"_id":ObjectId("500bc4304daafbf976598439")},mypost)
> db.blog.findOne({"_id":ObjectId("500bc4304daafbf976598439")})
{
        "_id" : ObjectId("500bc4304daafbf976598439"),
        "author" : "tian.chen",
        "title" : "my first mongodb blog"
}
3.3 说明:
1) 我们首先通过findOne来获取一个文档,并赋值给mypost,然后,修改mypost的author键,最后再通过db.blog.update更新文档
2)  更新时匹配多个文档,更新的时候,由于第二个参数的存在就会产生重复的_id键,就会报错。怎么说呢,举个例子
> db.blog.find()
{ "_id" : ObjectId("500bc4304daafbf976598439"), "author" : "tian.chen", "title"
: "my first mongodb blog", "age" : 28 }
{ "_id" : 2012, "author" : "tian.chen", "title" : "my 2nd mongodb blog", "age" :
 6 }
> var tianpost=db.blog.findOne({"author":"tian.chen","age":6})
> tianpost
{
        "_id" : 2012,
        "author" : "tian.chen",
        "title" : "my 2nd mongodb blog",
        "age" : 6
}
> tianpost.age=26
26
> db.blog.update({"author":"tian.chen"},tianpost)
cannot change _id of a document old:{ _id: ObjectId('500bc4304daafbf976598439'),
 author: "tian.chen", title: "my first mongodb blog", age: 28.0 } new:{ _id: 201
2.0, author: "tian.chen", title: "my 2nd mongodb blog", age: 26.0 }

在这个例子中,我们的post集合里包含2个文档,我们要将第二个文档的age改成26,首先通过author和age获取一个文档并赋值给tianpost,在这里,tianpost也是具有_id键的,其值为2012,然后,我们将tianpost的age改成26后,然后通过author=tian.chen查找文档,将其update为tianpost,问题就出现在这里,通过author=tian.chen查找文档时,首先找到第一个文档,其_id为 ObjectId('500bc4304daafbf976598439'),而tianpost._id=2012,我们知道_id是不能修改的,结果就报错了!

为了避免这种情况,最好确保更新总是指定唯一文档。

 

3.4 使用修改器

更新修改是种特殊的键,用来指定复杂的操作,如调整,增加或删除键,还可能操作数据或内嵌的文档。

假设有这样一个文档:

{

"_id" : 2012,

"age" : 26,

"author" : "tian.chen",

"pageviews" : 1,

"title" : "my 2nd mongodb blog"

}

其中的键pageviews表示该post被阅读的次数,每阅读一次就增加1,这时,我们可以使用$inc修改器

> db.blog.update({"_id":2012},{"$inc":{"pageviews":1}})

3.4.1 $set修改器

$set修改器用来指定一个键的值,如果这关键不存在就创建它,这对于修改来说是很方便的,因为我们通常修改的只是及个别的键的值的。

假设我们要修改下面文档的age:

{

"_id" : 2012,

"age" : 26,

"author" : "tian.chen",

"pageviews" : 1,

"title" : "my 2nd mongodb blog"

}

如果我们只是简单的用db.blog.update({"_id":2012},{"age":28}),那么,将会用{"age":28}替换掉整个文档,这个时候,$set修改器就很有用处了:

db.blog.update({"_id":2012},{"$set":{"age":28}})

当然,也可以修改多个:

db.blog.update({"_id":2012},{"$set":{"age":28,"author":"tian"}})

 

用$set可以修改键的值,如果键不存在就创建它,与之对应的,用$unset可以删除键,如:

db.blog.update({"_id":2012},{"$unset":{"pageviews":1}})

这样,就会将文档中的pageviews键删除掉

 

3.4.2 $inc修改器

正如我们在上面看到的,$inc修改器用来增加键的值.

需要注意的是$inc只能应用于整数、长整数、双精度浮点数,同时$inc的键的值必须为数字,如果要修改其他类型,应使用上面的$set

 

3.4.3 数组修改器 $push & $pop

如果指定的键存在,$push就会向已有的数据末尾加入一个元素,如果没有,则会创建一个新的数组

如:

>db.blog.update({"_id":2012},{$push:{"comments":{"name":"jake","content":"nice post"}}})

查出来的结果为:

> db.blog.find()
{ "_id" : ObjectId("500bc4304daafbf976598439"), "author" : "tian.chen", "title"
: "my first mongodb blog", "age" : 28 }
{ "_id" : 2012, "age" : 28, "author" : "tian", "comments" : [ { "name" : "jake",
"content" : "nice post" } ],
"title" : "my 2nd mongodb blog" }

 

有时候,我们可能会希望,如果一个值在数组中不存在,就添加进去,可以用如下方式来实现,即通过$ne来实现:

> db.papers.insert({"title":"mongodb post","authors":["tian"]})

> db.papers.update({"authors":{"$ne":"harry"}},{$push:{"authors":"harry"}})

{ "_id" : ObjectId("500c0098886e42d4a1a1bff5"), "authors" : [ "tian", "harry" ],
"title" : "mongodb post" }

 

也可用$addToSet来实现,因为$ne并不总是可行:

> db.papers.update({"_id": ObjectId("500c0098886e42d4a1a1bff5")},
... {"$addToSet":{"authors":"jerry"}})

>{ "_id" : ObjectId("500c0098886e42d4a1a1bff5"), "authors" : [ "tian", "harry", "
jerry" ], "title" : "mongodb post" }

 

$addToSet还有一个妙处,通过与$each结合,可以插入多个值,当然,如果值已经在数组中,就不会被添加进去:

> db.papers.update({"_id": ObjectId("500c0098886e42d4a1a1bff5")},
... {"$addToSet":
... {"authors":{"$each":["tian","jerry","mike"]}}})

 

{ "_id" : ObjectId("500c0098886e42d4a1a1bff5"), "authors" : [ "tian", "harry", "
jerry", "mike" ], "title" : "mongodb post" }

 

"tian","jerry"已经存在,因此没有重复添加,"mike"不存在,被添加进来了。

 

删除数组的元素可以用$pop或$pull

$pop主要用于删除数组头部或尾部的值{$pop:{key:-1}}和{$pop:{key:1}}

> db.papers.find()
{ "_id" : ObjectId("500c0098886e42d4a1a1bff5"), "authors" : [ "tian", "harry", "
jerry", "mike"
], "title" : "mongodb post" }
> db.papers.update({"_id": ObjectId("500c0098886e42d4a1a1bff5")},
... {$pop:{"authors":1}}
... )
> db.papers.find()
{ "_id" : ObjectId("500c0098886e42d4a1a1bff5"), "authors" : [ "tian", "harry", "
jerry"
], "title" : "mongodb post" }
> db.papers.update({"_id": ObjectId("500c0098886e42d4a1a1bff5")},
... {$pop:{"authors":-1}})
> db.papers.find()
{ "_id" : ObjectId("500c0098886e42d4a1a1bff5"), "authors" : [ "harry", "jerry" ]
, "title" : "mongodb post" }

 

可以发现{$pop:{key:-1}}删除尾部,而{$pop:{key:1}}删除头部的。

 

更经常的,我们希望通过值来判断,这时候就可以用$pull,{"$pull":{key:value}}

 

3.5 upsert更新

upsert是一种特殊的更新,要是没有符合更新条件的文档,就会以这个条件和文档为基础创建一份新的文档。如果有匹配的文档,则正常更新。

假设我们有一个集合analytics,用来记录每个url的访问次数,每访问一次就给pageviews键加1,正常情况,我们需要判断当前访问的url有没有存在,如果没有,则添加,有则更新。

采用upsert,我们可以有更优雅的写法:

db.analytics.update({"url":"/blog"},{"$inc":{"pageviews":1}},true)

 

在这里,我们通过给update传递第三个参数表示upsert

 

3.6 save函数

save跟upsert有点类似,也是不存在时插入,存在时更新。不同的是save只有一个参数。看下面的例子:

> var x = db.blog.findOne({"_id":2012})
> x.age=99
99
> db.blog.save(x)
> db.blog.find({"_id":2012})
{ "_id" : 2012, "age" : 99, "author" : "tian", "comments" : [   {       "name" :
"jake",        "content" : "nice post" },      {       "name" : "tina",
"content" : "not too bad" } ], "title" : "my 2nd mongodb blog" }

 

3.7 更新多个文档

在前面的例子中,我们都是只更新一个文档,mongodb目前也是默认只更新一个文档。我们可以通过对update指定第四个参数,来更新多个文档

 

图像 1

在上面的例子中,我们对比了没传第四个参数和传第四个参数进行更新的区别,传第四个参数后,符合条件的都会更新。

更新完成后,可以通过db.runCommand({getLastError:1})来获取更新了多少文档。

 

参考:MongoDB权威指南

posted @ 2012-07-22 22:03  Xiao Tian  阅读(1075)  评论(0编辑  收藏  举报