Mongodb update操作更新二维数组有趣的现象

问题描述

有如下数据:

{ "_id" : 1, "name" : "dave123", "favorites" : [ "chocolate", "cake", "butter", "apples" ] }
{ "_id" : 2, "name" : "li", "favorites" : [ "apples", "pudding", "pie", "cake" ] }
{ "_id" : 3, "name" : "ahn", "favorites" : [ "pears", "pecans", "chocolate", "cherries" ] }
{ "_id" : 4, "name" : "ty", "favorites" : [ "ice cream" ] }

然后我刚才操作失误,原本想给数组中添加一个元素结果给添加了一个数组进去:

db.test.update({_id:2},{$push:{favorites:['cake']}})

执行后的数据如下:

{ "_id" : 1, "name" : "dave123", "favorites" : [ "chocolate", "cake", "butter", "apples" ] }
{ "_id" : 2, "name" : "li", "favorites" : [ "apples", "pudding", "pie", "cake", [ "cake" ] ] }
{ "_id" : 3, "name" : "ahn", "favorites" : [ "pears", "pecans", "chocolate", "cherries" ] }

可以看到id2的文档的favorites字段中又包含了一个数组

想法设法删除掉这个数组

我知道$pull命令可以删除掉数组中的一个元素,但是它需要一个条件,于是问题被引到了如何在数组中查询另外一个数组.

命令如下:

db.tester.find({favorites:{$in:[['cake']]}})

输出:

{ "_id" : 2, "name" : "li", "favorites" : [ "apples", "pudding", "pie", "cake", [ "cake" ] ] }

然后我尝试使用update来完成这个操作,由于集合中就这么一条特殊数据我就没有指定条件:

 db.tester.update({},{$pull:{favorites:{$in:[['cake']]}}})

输出:

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })

喜闻乐见,匹配到了东西但是没有更新东西.

继续折腾

在我的努力下试出来了这么一个命令:

db.tester.update({favorites:{$in:[['cake']]}},{$pull:{favorites:{$in:[['cake']]}}})

输出:

WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

这次这个数组中的元素被正确的删除掉了.

最终的问题

WHY?

udpate操作中查询和跟随着的修改操作有什么潜在的关系吗,还是和$pull中的条件有关系?

阅读 3.3k
2 个回答

答案

就像我猜的一样,是一个细节问题.

By default, the update() method updates a single document.

默认情况下update只会修改匹配到的第一条数据.

在我使用的语句中:

db.tester.update({},{$pull:{favorites:{$in:[['cake']]}}})

这条命令会匹配多条数据,鉴于上面提到的规则MongoDB匹配到了多个数据后实际交由$pull操作的只有第一条:

{ "_id" : 1, "name" : "dave123", "favorites" : [ "chocolate", "cake", "butter", "apples" ] }

也就是上面这条,但是$pull条件却不成立了,所以不会进行任何更新.

这也解释了为什么如下的操作可行:

db.test.update({favorites:{$in:[['cake']]}},{$pull:{favorites:{$in:[['cake']]}}})

查询只会匹配到第一条数据而这条数据则会交由$pull处理.

如何解决问题

很简单在这个情况下开启多条修改支持就可以了,命令如下:

db.tester.update({},{$pull:{favorites:{$in:[['cake']]}}},{multi:true})

这样一来查询到的四条数据都会将由后面的更新处理而$pull也会匹配到对应的内容.

我是如何找到问题的

观察我们的前两条数据:

{ "_id" : 1, "name" : "dave123", "favorites" : [ "chocolate", "cake", "butter", "apples" ] }
{ "_id" : 2, "name" : "li", "favorites" : [ "apples", "pudding", "pie", "cake" ] }

我尝试删除两者数组中都存在的cake元素:

db.test.update({},{$pull:{favorites:{$in:['cake']}}})

但是只会删除第一条,第二条数据会进行匹配但是不会删除.

rs0:PRIMARY> db.test.insert({ "_id" : 2, "name" : "li", "favorites" : [ "apples", "pudding", "pie", "cake", [ "cake" ] ] })
rs0:PRIMARY> db.test.update({},{$pull:{favorites:{$in:[['cake']]}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
rs0:PRIMARY> db.test.find()
{ "_id" : 2, "name" : "li", "favorites" : [ "apples", "pudding", "pie", "cake" ] }

看上去我并不能重现这个问题。你的版本是?

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题