• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    MongoDB中文档的更新操作示例详解

    前言

    在MongoDB中,更新单个doc的操作是原子性的。默认情况下,如果一个update操作更新多个doc,那么对每个doc的更新是原子性的,但是对整个update 操作而言,不是原子性的,可能存在前面的doc更新成功,而后面的doc更新失败的情况。由于更新单个doc的操作是原子性的,如果两个更新同时发生,那么一个更新操作会阻塞另外一个,doc的最终结果值是由时间靠后的更新操作决定的。

    我们在前面的文章中提到过文档的基本的增删改查操作,MongoDB中提供的增删改查的语法非常丰富,不清楚的朋友们可以参考这篇文章:https://www.jb51.net/article/87697.htm

    那么下面的本文我们主要来看看更新都有哪些好玩的语法。

    文档替换

    假设我的集合中现在存了如下一段数据:

    {
     "_id" : ObjectId("59f005402844ff254a1b68f6"),
     "name" : "三国演义",
     "authorName" : "罗贯中",
     "authorGender" : "男",
     "authorAge" : 99.0
    }

    这是一本书,有书名和作者信息,但是作者是一个独立的实体,所以我想将之提取出来,变成下面这样:

    {
     "_id" : ObjectId("59f005402844ff254a1b68f6"),
     "name" : "三国演义",
     "author" : {
     "name" : "罗贯中",
     "gender" : "男",
     "age" : 99.0
     }
    }

    我可以采用如下操作:

    另外一个问题是更新时,MongoDB只会匹配第一个更新的文档,假设我的MongoDB中有如下数据:

    { "_id" : ObjectId("59f00d4a2844ff254a1b68f7"), "x" : 1 }
    { "_id" : ObjectId("59f00d4a2844ff254a1b68f8"), "x" : 1 }
    { "_id" : ObjectId("59f00d4a2844ff254a1b68f9"), "x" : 1 }
    { "_id" : ObjectId("59f00d4a2844ff254a1b68fa"), "x" : 2 }

    我想把所有x为1的数据改为99,我们很容易想到如下命令:

    db.sang_collect.update({x:1},{x:99})

    但我们发现执行结果却是这样:

    { "_id" : ObjectId("59f00d4a2844ff254a1b68f7"), "x" : 99 }
    { "_id" : ObjectId("59f00d4a2844ff254a1b68f8"), "x" : 1 }
    { "_id" : ObjectId("59f00d4a2844ff254a1b68f9"), "x" : 1 }
    { "_id" : ObjectId("59f00d4a2844ff254a1b68fa"), "x" : 2 }

    即只有第一条匹配的结果被更新了,其他的都没有变化。这是MongoDB的更新规则,即只更新第一条匹配结果。如果我们想将所有x为1的更新为x为99,可以采用如下命令:

    db.sang_collect.update({x:1},{$set:{x:99}},false,true)

    首先我们将要修改的数据赋值给$set,$set是一个修改器,我们将在下文详细讲解,然后后面多了两个参数,第一个false表示如果不存在update记录,是否将我们要更新的文档作为一个新文档插入,true表示插入,false表示不插入,默认为false,第二个true表示是否更新全部查到的文档,false表示只更新第一条记录,true表示更新所有查到的文档。

    使用修改器

    很多时候我们修改文档,只是要修改文章的某一部分,而不是全部,但是现在我面临这样一个问题,假设我有如下一个文档:

    {x:1,y:2,z:3}

    我现在想把这个文档中x的值改为99,我可能使用如下操作:

    db.sang_collect.update({x:1},{x:99})

    但是更新结果却变成了这样:

    { "_id" : ObjectId("59f02dce95769f660c09955b"), "x" : 99 }

    如下图:

    MongoDB帮我把整个文档更新了!要解决这个问题,我们可以使用修改器。

    $set修改器

    $set可以用来修改一个字段的值,如果这个字段不存在,则创建它。如下:

    如果该字段不存在,则创建,如下:

    也可以利用$unset删除一个字段,如下:

    $set也可以用来修改内嵌文档,还以刚才的书为例,如下:

    {
     "_id" : ObjectId("59f042cfcafd355da9486008"),
     "name" : "三国演义",
     "author" : {
     "name" : "罗贯中",
     "gender" : "男"
     }
    }

    想要修改作者的名字,操作如下:

    db.sang_collect.update({name:"三国演义"},{$set:{"author.name":"明代罗贯中"}})

    修改结果如下:

    {
     "_id" : ObjectId("59f042cfcafd355da9486008"),
     "name" : "三国演义",
     "author" : {
     "name" : "明代罗贯中",
     "gender" : "男"
     }
    }

    $inc修改器

    $inc用来增加已有键的值,如果该键不存在就新创建一个。比如我想给上文的罗贯中增加一个年龄为99,方式如下:

    db.sang_collect.update({name:"三国演义"},{$inc:{"author.age":99}})

    执行结果如下:

    {
     "_id" : ObjectId("59f042cfcafd355da9486008"),
     "name" : "三国演义",
     "author" : {
     "name" : "明代罗贯中",
     "gender" : "男",
     "age" : 99.0
     }
    }

    加入我想给罗贯中增加1岁,执行如下命令:

    db.sang_collect.update({name:"三国演义"},{$inc:{"author.age":1}})

    这是会在现有值上加1,结果如下:

    {
     "_id" : ObjectId("59f042cfcafd355da9486008"),
     "name" : "三国演义",
     "author" : {
     "name" : "明代罗贯中",
     "gender" : "男",
     "age" : 100.0
     }
    }

    注意$inc只能用来操作数字,不能用来操作null、布尔等。

    数组修改器

    数组修改器有好几种,我们分别来看。

    $push可以向已有数组末尾追加元素,要是不存在就创建一个数组,还是以我们的上面的book为例,假设book有一个字段为comments,是一个数组,表示对这个book的评论,我们可以使用如下命令添加一条评论:

    db.sang_collect.update({name:"三国演义"},{$push:{comments:"好书666"}})

    此时不存在comments字段,系统会自动帮我们创建该字段,结果如下:

    {
     "_id" : ObjectId("59f042cfcafd355da9486008"),
     "name" : "三国演义",
     "author" : {
     "name" : "明代罗贯中",
     "gender" : "男",
     "age" : 100.0
     },
     "comments" : [ 
     "好书666"
     ]
    }

    此时我们可以追加评论,如下:

    db.sang_collect.update({name:"三国演义"},{$push:{comments:"好书666啦啦啦啦"}})

    结果如下:

    {
     "_id" : ObjectId("59f042cfcafd355da9486008"),
     "name" : "三国演义",
     "author" : {
     "name" : "明代罗贯中",
     "gender" : "男",
     "age" : 100.0
     },
     "comments" : [ 
     "好书666", 
     "好书666啦啦啦啦"
     ]
    }

    如果想一次添加3条评论,可以结合$each一起来使用,如下:

    db.sang_collect.update({name:"三国演义"},{$push:{comments:{$each:["111","222","333"]}}})

    结果如下:

    {
     "_id" : ObjectId("59f042cfcafd355da9486008"),
     "name" : "三国演义",
     "author" : {
     "name" : "明代罗贯中",
     "gender" : "男",
     "age" : 100.0
     },
     "comments" : [ 
     "好书666", 
     "好书666啦啦啦啦", 
     "111", 
     "222", 
     "333"
     ]
    }

    我们可以使用$slice来固定数组的长度,假设我固定数组的长度为5,如果数组中的元素不足5个,则全部保留,如果数组中的元素超过5个,则只会保留最新的5个,如下:

    db.sang_collect.update({name:"三国演义"},{$push:{comments:{$each:["444","555"],$slice:-5}}})

    注意:$slice的值为负数,运行结果如下:

    {
     "_id" : ObjectId("59f042cfcafd355da9486008"),
     "name" : "三国演义",
     "author" : {
     "name" : "明代罗贯中",
     "gender" : "男",
     "age" : 100.0
     },
     "comments" : [ 
     "111", 
     "222", 
     "333", 
     "444", 
     "555"
     ]
    }

    我们还可以在清理之前使用$sort对数据先进行排序,然后再清理比如我有一个class文档,如下:

    {
     "_id" : ObjectId("59f07f3649fc5c9c2412a662"),
     "class" : "三年级二班"
    }

    现在向这个文档中插入student,每个student有姓名和成绩,然后按照成绩降序排列,只要前5条数据,如下:

    db.sang_collect.update({class:"三年级二班"},{$push:{students:{$each:[{name:"张一百",score:100},{name:"张九九",score:99},{name:"张九八",score:98}],$slice:5,$sort:{score:-1}}}})

    $sort的取值为-1和1,-1表示降序,1表示升序。

    上面的命令执行两次之后(即插入两次),结果如下:

    {
     "_id" : ObjectId("59f07f3649fc5c9c2412a662"),
     "class" : "三年级二班",
     "students" : [ 
     {
      "name" : "张一百",
      "score" : 100.0
     }, 
     {
      "name" : "张一百",
      "score" : 100.0
     }, 
     {
      "name" : "张九九",
      "score" : 99.0
     }, 
     {
      "name" : "张九九",
      "score" : 99.0
     }, 
     {
      "name" : "张九八",
      "score" : 98.0
     }
     ]
    }

    $slice和$sort不能只和$push一起使用,还要加上$each。

    $addToSet

    我们可以在插入的时候使用$addToSet,表示要插入的值如果存在则不插入,否则插入,如下:

    db.sang_collect.update({name:"三国演义"},{$addToSet:{comments:"好书"}})

    上面的命令执行多次之后,发现只成功插入了一条数据。也可以将$addToSet和$each结合起来使用,如下:

    db.sang_collect.update({name:"三国演义"},{$addToSet:{comments:{$each:["111","222","333"]}}})

    $pop

    $pop可以用来删除数组中的数据,如下:

    db.sang_collect.update({name:"三国演义"},{$pop:{comments:1}})

    1表示从comments数组的末尾删除一条数据,-1表示从comments数组的开头删除一条数据。

    $pull

    使用$pull我们可以按条件删除数组中的某个元素,如下:

    db.sang_collect.update({name:"三国演义"},{$pull:{comments:"444"}})

    表示删除数组中值为444的数据。

    $

    既然是数组,我们当然可以通过下标来访问,如下一行操作表示将下标为0的(第一个comments)comments修改为999:

    db.sang_collect.update({name:"三国演义"},{$set:{"comments.0":"999"}})

    可是有的时候我并不知道我要修改的数据处于数组中的什么位置,这个时候可以使用$符号来解决:

    db.sang_collect.update({comments:"333"},{$set:{"comments.$":"333-1"}})

    查询条件查出来333的下标,$符号就代码这个下标,然后通过$符号就能将之修改。

    save

    save是shell中的一个函数,接收一个参数,这个参数就是文档,如果文档中有_id参数save会执行更新操作,否则执行插入操作,使用save操作我们可以方便的完成一些更新操作。

    类似于如下命令则表示一个插入操作(因为没有_id):

    db.sang_collect.save({x:111})

    好了,MongoDB的更新操作我们就先介绍这么多,有问题欢迎留言讨论。

    参考资料:

    1.《MongoDB权威指南第2版》

    总结

    以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

    您可能感兴趣的文章:
    • 分布式文档存储数据库之MongoDB访问控制的操作方法
    • 分布式文档存储数据库之MongoDB备份与恢复的实践详解
    • 分布式文档存储数据库之MongoDB分片集群的问题
    • SpringDataMongoDB多文档事务的实现
    • mongodb如何对文档内数组进行过滤的方法步骤
    • MongoDB数据库文档操作方法(必看篇)
    • mongodb 数据类型(null/字符串/数字/日期/内嵌文档/数组等)
    • PHP库 查询Mongodb中的文档ID的方法
    • MongoDB如何更新多级文档的数据
    上一篇:Java操作mongodb的模糊查询和精确查询
    下一篇:MongoDB用Mongoose得到的对象不能增加属性完美解决方法(两种)
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    MongoDB中文档的更新操作示例详解 MongoDB,中,文档,的,更新,