天天看点

MongoDB学习笔记二:创建、更新及删除文档

插入并保存文档

对目标集使用insert方法插入一个文档:

> db.foo.insert({"bar" : "baz"})

这个操作会给文档增加一个"_id"键(要是原来没有的话),然后将其保存到MongoDB中。

批量插入

如果要插入多个文档,使用批量插入会快一些。批量插入传递一个由文档构成的数组给数据库。

如果只是导入数据(例如,从数据feed或者MySQL中导入),可以使用命令行工具,如mongoimport,而不是使用批量插入。

删除文档

> db.users.remove()

上述操作会伸出users集合中所有的文档。但不会删除集合本身,原有的索引也会保留。

remove函数可以接受一个查询文档作为可选参数。给定这个参数以后,只有符合条件的文档才被删除。列入,假设要删除mailing.list集合中所有"out-put"为true的人:

> db.mailing.list.remove({"opt-out" : true})

删除数据是永久性地,不能撤销,也不能恢复。

删除速度

删除文档通常会很快,但是要清楚整个集合,直接删除(然后重建索引)hi更快。

例如,在Python中,使用如下方法插入一百万个虚拟元素:

for i in range(1000000):

collection.insert({"foo": "bar", "baz": i, "z": 10 -i })

现在把刚插入的文档都删除,并记录花费的时间。首先来看一个简单的删除(remove):

import time
from pymongo import Collection

db = Collection().foo
collection = db.bar

start = time.time()

collection.remove()
collection.find_one()

total = time.time() - start
print "%d seconds" % total      

这段脚本输出"46.08 seconds"

如果用db.drop_collection("bar")来代替remove和find_one,只花了.01秒,速度提升相当明显。

更新文档

Ⅰ使用update方法修改存入数据库以后的文档。update有两个参数,一个是查询文档,用来找出需要的文档,另一个是修改器(modifier)文档,描述对修改的文档做哪些修改。

例:

> joe = db.people.findOne({"name" : "joe", "age" : 20})

> joe.age++;

> db.people.update({"name" : "joe"}, joe)

Ⅱ使用修改器

通常文档只会有一部分要更新。利用原子的更新修改器,可以使得这种部分更新极为搞笑。更新修改器是种特殊的键,用来指定复杂的更新操作,比如调整、增加或者删除键,还可能是操作数组或者内嵌文档。

假设要在一个集合中防止网站的分析数据,每当有人访问页面的时候,就要增加计数器。可以使用更新修改器原子性地完成这个增加。每个URL对应的访问次数都以如下的方式存储在文档中:

{

"_id" : ObjectId("4b253b067525f35f94b60a31"),

"url" : "www.example.com",

"pageviews" : 52

}

每次有人访问,就通过URL找到该页面,并用"$inc"修改器增加"pageviews"的值。

> db.analytics.update({"url" : "www.example.com"}, {"$inc" : {"pageviews" : 1}})

接着,执行一个find操作,会发现"pageviews"的值增加了1.

> db.analytics.find()

{

"_id" : ObjectId("4b253b067525f35f94b60a31"),

"url" : "www.example.com",

"pageviews" : 53

}

①"$set"修改器

"$set"用来指定一个键的值。如果这个键不存在,则创建它。

例:添加喜欢的书到用户信息:

> db.users.update({"_id" : ObjectId("4b253b067525f35f94b60a31")}, {"$set" : {"favorite book" : "war and peace"}})

如果用户觉得喜欢的另一本书:

> db.users.update({"name" : "joe"}, {"$set" : {"favorite book" : "green eggs and ham"}})

用"$set"可以修改键的数据类型。例如,如果用户喜欢的是一堆书:

> db.users.update({"name" : "joe"} : {"$set" : {"favorite book" : ["cat's cradle", "fundation trilogy", "ender's game"]}})

如果用户发现自己不爱读书,可以用"$unset"将键完全删除:

> db.users.update({"name" : "joe"}, {"$unset" : {"favorite book" : 1}})

②增加和减少

> db.game.update({"game" :"pinball", "user" : "joe"}, {"$inc" : {"score" : 50}})

③数组修改器

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

例如,假设要存储博客文章,要添加一个包含一个数组的"comments"(评论)键。可以向还不存在的"comments"数组push一个评论,这个数组会被自动创建,并加入评论:

> db.blog.posts.update({"title" : "A blog post"}, {$push : {"comments" : {"name" : "joe", "email" : "[email protected]", "content" : "nice post."}}})

要是还想添加一条评论,可以接着使用"$push"。

经常会有这种情况,如果一个值不在数组里面就把它加进去。可以在查询文档中用"$ne"来实现。例如,要是坐着不再引文列表中就添加进去,可以这么做:

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

也可以用"$addToSet"完成同样的事情,并且"$addToSet"可以避免重复。

> db.users.update({"_id" : ObjectId("4b2d75476cc613d5ee930164")}, {"$addToSet : {"emails" : "[email protected]"}})

将"$addToSet"和"$each"组合起来,可以添加多个不同的值,而用"$ne"和"$push"组合就不能实现。例如:想一次添加多个邮件地址,就可以使用这些修改器:

> db.users.update({"_id" : ObjectId("4b2d75476cc613d5ee930164")},

{"$addToSet" :

"emails" : {"$each" : ["[email protected].net", "[email protected]", "[email protected]"]}})

"$pop"修改器可以从数组任何一端删除元素。{$pop : {key : 1}}从数组末尾删除一个元素,{$pop : {key : -1}}则从头部删除。

"$pull"可以基于特定条件来删除元素,而不仅仅是一句位置。例如,有一个待完成事项列表,顺序有些问题:

> db.list.insert({"todo" : ["dishes", "laundry", "dry cleaning"]})

要是想把洗衣服(alundry)放到第一位,可以从列表中先删掉:

> db.list.update({}, {"$pull" : {"todo" : "laundry"}})

"$pull"会将所有匹配的部分删掉。对数组[1, 1, 2, 1]执行pull 1,得到的结果就是只有一个元素的数组[2]。

④数组的定位修改器

若是数组有多个值,而我们只想对其中的一部分进行操作,这就需要一些技巧。有两种方法操作数组中的值:通过位置或者定位操作符("$")。

数组都是以0开头的,可以将下表直接作为键来选择元素。例如,这里有一个文档,其中包含由内嵌文档组成的数组,比如包含评论的博客文章。

> db.blog.posts.findOne()

{

"_id" : ObjectId("4b329a216cc613d5ee930192")

"content" : "..."

"comments" : [

{

"comment" : "good post"

"author" : "John"

"votes" : 0

},

{

"comment" : "i thought it was too short"

"author" : "Claire"

"votes" : 3

}

{

"comment" : "free watches"

"author" : "Alice"

"votes" : -1

}

]

}

如果想增加第一个评论的投票数量,可以这么做:

> db.blog.update({"post" : post_id}, {"$inc" : {"comments.0.votes" : 1}})

但是很多情况下,不预先查询文档就不能知道要修改数组的下标。为了克服这个困难,MongoDB提供了定位操作符"$",用来定位查询文档已经匹配的元素,并进行更新。例如,要是用户John把名字改成Jim,就可以用定位符替换评论中的名字:

> db.blog.update({"comments.author" : "John"}, {"$set" : {"comments.$.author" : "Jim"}})

定位符只更新第一个匹配的元素。

⑤修改器速度

如果修改操作涉及空间分配会减慢修改操作的速度,同事苏浙数组变长,MongoDB需要更长的时间来遍历整个数组,对每个数组的修改也会慢下来。

下面的Python程序插入一个键,并增加其值100000次(不涉及空间分配):

from pymongo import Connection

import time

db = Connection().performance_test
db.drop_collection("updates")
collection = db.upddates

collection.insert({"x" : 1})

# make sure the insert is complete before we start timing collection.find_one()

start = time.time()

for i in range(100000):
    collection.update({} : {"$inc" : {"x" : 1}})
    
# make sure the updates are complete before we stop timing
collection.find_one()

print time.time() - start      

一共运行了7.33秒。美妙有13000多次更新。

如果是push 100000次的话:

for i in range(100000):
    collection.update({}, {'$push' : {'x' : 1}})      

程序花了67.58秒,每秒更新不到1500次。

『upsert』

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

回顾之前记录网站页面访问次数的例子。要是没有upsert,就得试着查询URL,没有找到就得新建一个文档,找到的话就增加访问次数。要是把这个鞋城JavaScript程序(而不是用mongo scriptname.js来运行的一系列shell命令),会是如下这样的:

// check if we have an entry for this page

blog = db.analytics.findOne({url : "/blog"})

// if we do, add one to the number of views and save

if (blog) {

blog.pageviews++;

db.analytics.save(blog);

}

// otherwise, create a new document for this page

else {

db.analytics.save({url : "/blog", pageviews : 1})

}

这就是说如果有人访问页面,我们得去数据库打个来回,然后选择更新或者插入。要是多个进程同时运行这段代码,还得考虑对于给定URL不能查如文档的限制。

钥匙使用upsert,即可以避免竞态问题,又可以缩减代码量(update的第3个参数表示这是一个upsert):

> db.analystics.update({"url" : "/blog"}, {"$inc" : {"visits" : 1}}, true)

这行代码和之前的代码作用完全一样,但它更高效,并且是原子性的。

创建新文档会将条件文档作为基础,然后将修改器文档应用于其上。例如,要是执行一个匹配键并增加对应键值的upsert操作,会在匹配的基础上进行增加:

> db.math.remove()

> db.math.update({"count" : 25}, {"$inc" : {"count" : 3}}, true)

> db.math.findOne()

{

"_id" : ObjectId("4b3295f26cc613d5ee93018f"),

"count" : 28

}

先是remove清空了集合,里面就没有文档了。upsert创建了一个键"count"的值为25的文档,随后将这个值加3,最后得到"count"为28的文档。要是不开启upsert选项,{"count" : 25}不会匹配到任何文档,也就没有任何更改。

要是将这个upsert(条件为{count : 25})再次运行,还会创建一个新文档。这是因为没有文档匹配条件(唯一的文档的"count"的值是28)。

save Shell帮助程序

save是一个shell函数,可以在文档不存在时插入,存在时更新。他只有一个参数:文档。要是这个文档含有"_id"键,save会调用upsert。否则,会调用插入。程序员可以非常方便地使用这个函数在shell中快速修改文档。

> var x = db.foo.findOne()

> x.num = 42

> db.foo.save(x)

要是不用save,最后一行可以像下面这样写,但很啰嗦:

> db.foo.update({"_id" : x._id}, x)

更新多个文档

默认情况下,更新只能对符合条件的第一个文档执行操作。要是有多个文档符合条件,其余的文档就没有变化。要使所有匹配到的文档都得到更新,可以设置update的第4个参数为true。

多文档更新对模式迁移非常有用,还可以在对特定用户发布新功能的时候使用。例如,假设要给所有在特定日期过生日的用户一份礼物,就可以使用多文档更新,将"gift"增加到他们的账号。

> db.users.update({birthday : "10/13/1978"}, {$set : {gift : "Happy Birthday!"}}, false, true)

这样就给生日为1978年10月113日的所有用户文档添加了"gift"键。

想要知道多文档更新到底更新了多少文档,可以运行getLastError命令。键"n"的值就是要的数字。

> db.count.update({x : 1}, {$inc : {x : 1}}, false, true)

> db.runCommand({getLastError : 1})

{

"err" : null,

"updatedExisting" : true,

"n" : 5,

"ok" : true

}

这里"n"为5,说明5个文档被更新了。"updatedExisting"为true,说明是对已有的文档进行更新。

返回已更新的文档:findAndModify命令。

例:假设我们有一个集合,其中包括以一定顺序运行的进程。其中每个进程都被表示为具有如下形式的文档:

{

"_id" : ObjectId(),

"status" : stat,

"priority" : N

}

"status"是一个字符串,可以是"READY"、"RUNNING"或"DONE"。要找到状态为"READY"的具有最高优先级的任务,运行进程函数,然后更新其状态为"DONE"。将已经就绪的进程按照优先级排序,然后将优先级最高的进程的状态更新为"RUNNING"。完成了以后,就把状态改为"DONE"。

使用"findAndModify"的程序:

> ps = db.runCommand({"findAndModify" : "processes",

"query" : {"status" : "READY"},

"sort" : {"priority" : -1},

"update" : {"$set" : {"status" : "RUNNING"}}}).value

> do_something(ps)

> db.process.update({"_id" : ps._id}, {"$set" : {"status" : "DONE"}})

findAndModify既有"update"键也有"remove"键。"remove"键表示将匹配到的文档从集合里面删除。例如,现在不要更新状态了,而是直接删掉,就可以像下面这样:

> ps = db.runCommand({"findAndModify" : "processes",

"query" : {"status" : "READY"},

"sort" : {"priority" : -1},

"remove" : true}).value

> do_something(ps)

findAndModify命令中每个键对应的值如下所示。

· findAndModify

字符串,集合名

· query

查询文档,用来检索文档的条件

· sort

排序结果的条件

· update

修改器文档,对所找到的文档执行的更新。

· remove

布尔类型,表示是否删除文档。

· new

布尔类型,表示返回的是更新前的还是更新后的文档。默认是更新前的文档。

转载于:https://www.cnblogs.com/answernotfound/p/mongodbnote2.html