• MongoDB 学习二


    这章我们学习数据操作。

    Inserting and Saving Documents

    上一章我们已经简单介绍了数据插入,如:

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

    那么,假如你碰到需要插入多条documents的时候情况怎么办呢?

    只要传入数组就行

    db.foo.insert([{"_id":0},{"_id":1},{"_id":2}])

    插入验证

    MongoDB在插入数据的时候仅做最少的检查。它检查document的基础结构并且"_id"不存在时帮你自动添加。

    一种数据结构的检查是所有的document都必须小于16M。

    Removing Documents

    假如我们数据库中有数据,让我们来删除它:

    db.foo.remove()

    这是我们会删除foo中所有的documents。实际上它并不会删除foo这个collection。

    remove这个方法支持一个查询document的参数,当我们传入这个参数时,只有匹配这个查询参数的document才会被删除。

    当我们执行remove时,它就永久被删除,没有任何方法可以恢复。

    Remove Speed

    删除文档是一个相当快的操作,但是你想要清除整个collection,最快的是drop方法

    Updating Documents

    当document存储在数据库中时,我们可以使用update方法更改它。

    update有两个参数:一个用来定位我们需要更新的document,另一个是我们需要修改的document。

    更新document是原子性的:假如有两个update动作发生在同一时间,哪个操作先到达服务器端将会先接收,第二个操作会后接收。

    Using Modifiers

    通常我们只要更新document中的某些部分。你可以使用update modifiers来更新特殊字段。 Update Modifiers是用于指定复杂更新操作的特殊key值,例如altering,adding,或者removing keys。

    假设我们要分析网站访问量,我们可以使用update modifiers来做这个自动增量,例如原来的数据:

    {
       "_id":ObjectId("..."),
       "url": "www.example.com",
       "pageviews":1
    }

    每次访问页面,我们可以根据它的url查找并且使用"$inc" modifier 来增加pageviews的值

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

    Getting started with the "$set" modifier

     "$set"可以设置字段值。假如字段不存在,它会被创建。

     举个例子,假设你有个用户简况存在数据库中:

    {
       "_id":ObjectId("4XXXX"),
       "name":"joe",
       "age":30,
       "sex":"male",
       "location":"Wisconsin"
    }

    这是最基本的信息,假如用户想要存储他最喜欢的书,你可以使用"$set":

    db.users.update({"_id":ObjectId("4XXXX")},{"$set":{"favorite book":"War and Peace"}})

    "$set"甚至可以更改它的类型,假如用户实际上喜欢多本书,我们可以将favorite book这个值存入数组:

    db.users.update({"_id":ObjectId("4XXXX")},{"$set":{"favorite book":["War and Peace","Cat's Cradle"]}})

    当用户发现他实际上不喜欢阅读时,他也可以使用"$unset"来移除key

    db.users.update({"_id":ObjectId("4XXXX")},{"$unset":{"favorite book":1}})

    Array modifers

     "$push"可以添加数据到类型为Array的字段。假如字段不存在,它会被创建。

    例如原有数据:

    {
        "_id":ObjectId("4XXX"),
        "title":"A blog post"
    }

    添加cmments的数组字段:

    db.blog.update({"title":"A blog post"},{"$push":{"comments":{"name":"xc","content":"nice post"}}})

    再来看下数据,这时变为:

    {
        "_id":ObjectId("4XXX"),
        "title":"A blog post",
        "comments":[
              {
                   "name":"xc",
                   "content":"nice post"
               }
         ]
    }

     这只是简单的对数组进行push,可是我们要插入多条数组数据怎么做呢?答案是使用"$each":

    db.blog.update({"title":"A blog post"},{"$push":{"comments":{"$each":[{"name":"xc","content":"nice post"},{"name":"cc","content":"test"}]}}})

    Using arrays as sets

    如果你只想添加数组中没有的元素,可以使用"$ne":

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

    你也可以通过"$addToSet"完成这个工作:

    db.blog.update({"title":"A blog post"},{"$addToSet":{"comments":{"name":"xc","content":"nice post"}}})

    Removing elements

    移除数组最后一个元素,用

    {"$pop":{"key":1}}

    移除数组第一个元素,用

    {"$pop":{"key":-1}}

    根据条件匹配删除数据,用"$pull",例:

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

    Positional array modifications

    当我们在一个数组中有多个值并且想要修改它们时,数组操作变的很机智。

    我们可以基于数组的索引来选取它们。

     {
     "_id" : ObjectId("55ef9fdcb01a89febec546be"),
    "comments": [ {"comment":"x","author":"John","votes":0},
    {"comment":"y","author":"Claire","votes":3},
    {"comment":"z","author":"Alice","votes":-1} ] }

    我们想要使第一条comments的votes加1,我们可以这样做(comments.0.votes代表第一条comments的votes字段):

    db.foo.update({},{"$inc":{"comments.0.votes":1}})

    在许多情况下,我们不知数组的索引来查找并修改数组。结果MongoDB出现了一个定位操作,"$",可以指出数组中匹配的元素并进行跟新。

    例如,我们有个用户交John,并且我们想把它的名字变为Jim,我们就可以这样写:

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

    Upserts

    upsert是更新的一种特殊类型(其实就是update的第三个参数)。假如我们通过更新匹配条件没有找到document,它会结合条件创建并更新document。假如找到了,它则正常更新。

    举个例子:

    //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})
    }

    我们用upsert改下

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

    upsert如果不加或者是false。如果查不到匹配的document时则什么都不会发生。

    在upsert的基础上,有些时间我们只需要在创建document的时候对某个字段赋值,并不想更新它的值,可以用"$setOnInsert":

     db.users.update({},{"$setOnInsert":{"createdAt":new Date()}},true)

    我们可以看到数据为

    { "_id" : ObjectId("55efc4a3de70c83a19f2d9b9"), "createdAt" : ISODate("2015-09-09T05:34:07.791Z") }

    这时候继续在打命令

     db.users.update({},{"$setOnInsert":{"createdAt":new Date()}},true)

    我们发现createAt字段的值还是没有变化。

    Updating Multiple Documents

    update方法默认的只会根据匹配条件更新第一条数据。假如有更多的匹配出的document,则不会更新。

    如果需要全部更新,你只要指定第四个参数为true就可以了。

    例如我们想为所有生日在某一天的用户添加一个gift字段,可以这样写:

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

    Returning Updated Documents

    我们执行update操作时,不会返回更新的数据,这时,你需要findAndModify命令。并且它是原子性的。

    假设我们有些订单需要跑,数据结构为

    {
        "id":ObjectId(),
        "status":state,
        "priority":N
    }

    "status"可以是"READY","RUNNING"或者"DONE"。我们有个任务,需要根据"READY"的优先级顺序,跑process方法,并且更新其状态为"DONE"。

    我们需要根据排序优先级查找出document准备跑porcess方法,状态标记为"RUNNING",当我们跑完方法时,更新状态为"DONE",看上去像是这样:

    var cursor = db.processes.find({"status":"READY"});
    ps = cursor.sort({"priority":-1}).limit(1).next();
    db.processes.update({"_id":ps._id},{"$set":{"status":"RUNNING"}});
    do_something(ps);
    db.processes.update({"_id":ps._id},{"$set":{"status":"DONE"}});

    这种算法并不好。假设我们有两个线程在跑。假如线程一和线程二同时检索到同一条数据,那么将跑同样的process。

    我们可以做状态检查来避免这种情况,但是这将会变的复杂:

    var cursor = db.processes.find({"status":"READY"});
    cursor.sort({"priority":-1}).limit(1);
    while((ps=cursor.next())!=null){
        ps.update({"_id":ps._id,"status":"READY"},{"$set":{"status":"RUNNING"}});
        var lastOp = db.runCommand({getlasterror:1});
        if(lastOp.n == 1){
            do_something(ps);
            db.processes.update({"_id":ps._id},{"$set":{"status":"DONE"}});
            break;
        }
        cursor = db.process.find({"status":"READY"});
        cursor.sort({"priority":-1}).limit(1);
    }

    另外,一个线程可能会做完所有的工作,而另外一个线程一直处于等待失败的状态中。

    类似这种情况,"findAndModify"完美解决。"findAndModify"能够在一个操作中同时返回并更新数据。

    ps = db.runCommand(
          {
             "findAndModify":"processes",
             "query":{"status":"READY"},
             "sort":{"priority":-1},
             "update":{"$set":{"status":"RUNNING"}}
          }
    )

    返回值:

    {
         "ok":1,
         "value":{
                "_id":ObjectId("4XXXX"),
                "priority":1,
                "status":"READY"
         }
    }

    这里返回的状态是READY,为更新前的状态数据。假如你这时查找数据库,会发现状态已经更新了。

    "findAndModify"除了有"udpate"这个key,也有"remove"这个key。 想必这个不用解释了吧。

    ps = db.runCommand(
          {
             "findAndModify":"processes",
             "query":{"status":"READY"},
             "sort":{"priority":-1},
             "remove":true}
          }
    )

    这章介绍了MongoDB的创建、删除、修改操作,下章将介绍查询操作,敬请期待...

  • 相关阅读:
    【读书笔记】程序员的自我修养总结(三)
    【DSP开发】利用CCS5.4开发基于DSP6455的JPEG2000图像解压缩过程
    【DSP开发】利用CCS5.4开发基于DSP6455的JPEG2000图像解压缩过程
    【读书笔记】程序员的自我修养总结(二)
    【读书笔记】程序员的自我修养总结(二)
    【读书笔记】程序员的自我修养总结(一)
    【读书笔记】程序员的自我修养总结(一)
    CMake生成VS2010工程相对路径和绝对路径问题说明
    CMake生成VS2010工程相对路径和绝对路径问题说明
    关于lib和dll
  • 原文地址:https://www.cnblogs.com/showtime813/p/4789038.html
Copyright © 2020-2023  润新知