初识MongoDB(四) MongoDB文档更新操作

文档替换

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

{
    "_id": ObjectId("618ddb3d059720a0baf1ecb8"),
    "name": "三国演义",
    "authorName": "罗贯中",
    "authorGender": "男",
    "authorAge":99
}

这是一本书,有书名和作者信息。但是作者是一个独立的实体,应该将它提取出来,变成下面这样:

{
    "_id": ObjectId("618ddb3d059720a0baf1ecb8"),
    "name": "三国演义",
    "author": {
        "name": "罗贯中",
        "gender": "男",
        "age": 99
    }
}

可以采用如下操作:

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

{ "_id" : ObjectId("618e002f059720a0baf1ecb9"), "x" : 1 }
{ "_id" : ObjectId("618e0030059720a0baf1ecba"), "x" : 1 }
{ "_id" : ObjectId("618e0031059720a0baf1ecbb"), "x" : 1 }
{ "_id" : ObjectId("618e0036059720a0baf1ecbc"), "x" : 2 }

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

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

但发现执行结果确是这样:

{ "_id" : ObjectId("618e002f059720a0baf1ecb9"), "x" : 99 }
{ "_id" : ObjectId("618e0030059720a0baf1ecba"), "x" : 1 }
{ "_id" : ObjectId("618e0031059720a0baf1ecbb"), "x" : 1 }
{ "_id" : ObjectId("618e0036059720a0baf1ecbc"), "x" : 2 }

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

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

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

使用修改器

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

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

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

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

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

{ "_id" : ObjectId("618e0511059720a0baf1ecbe"), "x" : 99 }

如下图:

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

$set修改器

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

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

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

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

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

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

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

修改结果如下:

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

 $inc修改器

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

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

执行结果如下:

{
    "_id":ObjectId("618ddb3d059720a0baf1ecb8"),
    "name":"三国演义",
    "author":{
        "name":"明代罗贯中",
        "gender":"男",
        "age":99
    }
}

假如想给罗贯中增加一岁,执行如下命令:

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

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

{
    "_id":ObjectId("618ddb3d059720a0baf1ecb8"),
    "name":"三国演义",
    "author":{
        "name":"明代罗贯中",
        "gender":"男",
        "age":100
    }
}

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

数组修改器

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

$push 

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

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

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

{
    "_id":ObjectId("618ddb3d059720a0baf1ecb8"),
    "name":"三国演义",
    "author":{
        "name":"明代罗贯中",
        "gender":"男",
        "age":100
    },
    "comments":[
        "好书呀"
    ]
}

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

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

结果如下:

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

 $each

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

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

结果如下:

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

$slice

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

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

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

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

$sort

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

{
    "_id":ObjectId("618e14f7059720a0baf1ecc0"),
    "class":"一年级一班"
}

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

db.books_1.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("618e14f7059720a0baf1ecc0"),
    "class":"一年级一班",
    "students":[
        {
            "name":"张一百",
            "score":100
        },
        {
            "name":"张一百",
            "score":100
        },
        {
            "name":"张九九",
            "score":99
        },
        {
            "name":"张九九",
            "score":99
        },
        {
            "name":"张九八",
            "score":98
        }
    ]
}

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

 $addToSet

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

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

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

db.books_1.update({name:"三国演义"},{$addToSet:{comments:{$each:["嗯,好书","777","888"]}}})

发现只插入成功了"777"和"888"。

$pop

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

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

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

$pull

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

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

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

$

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

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

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

db.books_1.update({name:"三国演义",comments:"333"},{$set:{"comments.$":"333-1"}})

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

save

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

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

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值