第二章 – 更新

最后更新于:2022-04-01 00:54:55

在第一章,我们介绍了 CRUD 的四分之三(create, read, update 和 delete) 操作。这章,我们来专门来讨论我们跳过的那个操作: `update`。 `Update` 有些独特的行为,这是为什么我们把它独立成章。 ## [](https://github.com/geminiyellow/the-little-mongodb-book/blob/master/zh-cn/mongodb.markdown#update-覆盖还是-set)Update: 覆盖还是 $set 最简单的情况, `update` 有两个参数: 选择器 (`where`) 和需要更新字段的内容。假设 Roooooodles 长胖了,你会希望我们这样操作: ~~~ db.unicorns.update({name: 'Roooooodles'}, {weight: 590}) ~~~ (如果你已经把 `unicorns` 集合玩坏了,它已经不是原来的数据了的话,再执行一次 `remove` 删除所有数据,然后重新插入第一章中所有的代码。) 现在,如果你查一下被更新了的记录: ~~~ db.unicorns.find({name: 'Roooooodles'}) ~~~ 你会发现 `update` 的第一个惊喜,没找到任何文档。因为我们指定的第二个参数没有使用任何的更新选项,因此,它**replace** 了原始文档。也就是说, `update` 先根据 `name` 找到一个文档,然后用新文档(第二个参数)覆盖替换了整个文档。这和 SQL 的 `update` 命令的完全不一样。在某些情况下,这非常理想,可以用于某些完全动态更新上。但是,如果你只希望改变一个或者几个字段的值的时候,你应该用 MongoDB 的 `$set` 操作。继续,让我们来更新重置这个丢失的数据: ~~~ db.unicorns.update({weight: 590}, {$set: { name: 'Roooooodles', dob: new Date(1979, 7, 18, 18, 44), loves: ['apple'], gender: 'm', vampires: 99}}) ~~~ 这里不会覆盖新字段 `weight` 因为我们没有指定它。现在让我们来执行: ~~~ db.unicorns.find({name: 'Roooooodles'}) ~~~ 我们拿到了期待的结果。因此,在最开始的时候,我们正确的更新 weight 的方式应该是: ~~~ db.unicorns.update({name: 'Roooooodles'}, {$set: {weight: 590}}) ~~~ ## [](https://github.com/geminiyellow/the-little-mongodb-book/blob/master/zh-cn/mongodb.markdown#update-操作符)Update 操作符 除了 `$set`,我们还可以用其他的更新操作符做些有意思的事情。所有的更新操作都是对字段起作用 - 所以你不用担心整个文档被删掉。比如,`$inc` 可以用来给一个字段增加一个正/负值。假设说 Pilot 获得了非法的两个 vampire kills 点,我们可以这样修正它: ~~~ db.unicorns.update({name: 'Pilot'}, {$inc: {vampires: -2}}) ~~~ 假设 Aurora 忽然长牙了,我们可以给她的 `loves` 字段加一个值,通过 `$push` 操作: ~~~ db.unicorns.update({name: 'Aurora'}, {$push: {loves: 'sugar'}}) ~~~ MongoDB 手册的 [Update Operators](http://docs.mongodb.org/manual/reference/operator/update/#update-operators) 这章,可以查到更多可用的更新操作符的信息。 ## [](https://github.com/geminiyellow/the-little-mongodb-book/blob/master/zh-cn/mongodb.markdown#upserts)Upserts 用 `update` 还有一个最大的惊喜,就是它完全支持 `upserts`。所谓 `upsert` 更新,即在文档中找到匹配值时更新它,无匹配时向文档插入新值,你可以这样理解。要使用 upsert 我们需要向 update 写入第三个参数 `{upsert:true}`。 一个最常见的例子是网站点击计数器。如果我们想保存一个实时点击总数,我们得先看看是否在页面上已经有点击记录,然后基于此再决定执行更新或者插入操作。如果省略 upsert 选项(或者设为 false),执行下面的操作不会带来任何变化: ~~~ db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}); db.hits.find(); ~~~ 但是,如果我们加上 upsert 选项,结果会大不同: ~~~ db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}, {upsert:true}); db.hits.find(); ~~~ 由于没有找到字段 `page` 值为 `unicorns`的文档,一个新的文档被生成插入。当我们第二次执行这句命令的时候,这个既存的文档将会被更新,且 `hits` 会被增加到 2。 ~~~ db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}, {upsert:true}); db.hits.find(); ~~~ ## [](https://github.com/geminiyellow/the-little-mongodb-book/blob/master/zh-cn/mongodb.markdown#批量-updates)批量 Updates 关于 `update` 的最后一个惊喜,默认的,它只更新单个文档。到目前为止,我们的所有例子,看起来都挺符合逻辑的。但是,如果你执行一些像这样的操作的时候: ~~~ db.unicorns.update({}, {$set: {vaccinated: true }}); db.unicorns.find({vaccinated: true}); ~~~ 你肯定会希望,你所有的宝贝独角兽都被接种疫苗了。为了达到这个目的, `multi` 选项需要设为 true: ~~~ db.unicorns.update({}, {$set: {vaccinated: true }}, {multi:true}); db.unicorns.find({vaccinated: true}); ~~~ ## [](https://github.com/geminiyellow/the-little-mongodb-book/blob/master/zh-cn/mongodb.markdown#小结-1)小结 本章中我们介绍了集合的基本 CRUD 操作。我们详细讲解了 `update` 及它的三个有趣的行为。 首先,如果你传 MongoDB 一个文档但是不带更新操作, MongoDB 的 `update` 会默认替换现有文档。因此,你通常要用到 `$set` 操作 (或者其他各种可用的用于修改文档的操作)。 其次, `update` 支持 `upsert` 操作,当你不知道文档是否存在的时候,非常有用。 最后,默认情况下, `update` 只更新第一个匹配文档,因此当你希望更新所有匹配文档时,你要用 `multi` 。
';