zoukankan      html  css  js  c++  java
  • 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" : "joe@example.com", "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" : "joe@gmail.com"}})
    将"$addToSet"和"$each"组合起来,可以添加多个不同的值,而用"$ne"和"$push"组合就不能实现。例如:想一次添加多个邮件地址,就可以使用这些修改器:
    > db.users.update({"_id" : ObjectId("4b2d75476cc613d5ee930164")},
    {"$addToSet" :
    "emails" : {"$each" : ["joe@php.net", "joe@example.com", "joe@python.org"]}})
    "$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
    布尔类型,表示返回的是更新前的还是更新后的文档。默认是更新前的文档。

  • 相关阅读:
    样式
    样式表的类别、选择器和优先级
    随记
    框架
    表单元素
    HTLM内容容器标签和常用标签
    HTML5的意义、改变以及全局属性
    11月21日html基础
    感想 目标和展望
    C++结构体实例和类实例的初始化
  • 原文地址:https://www.cnblogs.com/answernotfound/p/mongodbnote2.html
Copyright © 2011-2022 走看看