codecamp

第二章 - 更新

在第一章,我们介绍了 CRUD 的四分之三(create, read, update 和 delete) 操作。这章,我们来专门来讨论我们跳过的那个操作: update。 Update 有些独特的行为,这是为什么我们把它独立成章。

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

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 这章,可以查到更多可用的更新操作符的信息。

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();

批量 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});

小结

本章中我们介绍了集合的基本 CRUD 操作。我们详细讲解了 update 及它的三个有趣的行为。 首先,如果你传 MongoDB 一个文档但是不带更新操作, MongoDB 的 update 会默认替换现有文档。因此,你通常要用到 $set 操作 (或者其他各种可用的用于修改文档的操作)。 其次, update 支持 upsert 操作,当你不知道文档是否存在的时候,非常有用。 最后,默认情况下, update 只更新第一个匹配文档,因此当你希望更新所有匹配文档时,你要用 multi 。

第一章 - 基础知识
第三章 - 掌握查询
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }