第二章 - 更新

2018-02-24 16:17 更新

在第一章,我們介紹了 CRUD 的四分之三(create, read, update 和 delete) 操作。這章,我們來專門來討論我們跳過的那個操作:?update。?Update?有些獨特的行為,這是為什么我們把它獨立成章。

Update: 覆蓋還是 $set

最簡單的情況,?update?有兩個參數(shù): 選擇器 (where) 和需要更新字段的內(nèi)容。假設(shè) Roooooodles 長胖了,你會希望我們這樣操作:

db.unicorns.update({name: 'Roooooodles'},
    {weight: 590})

(如果你已經(jīng)把?unicorns?集合玩壞了,它已經(jīng)不是原來的數(shù)據(jù)了的話,再執(zhí)行一次?remove?刪除所有數(shù)據(jù),然后重新插入第一章中所有的代碼。)

現(xiàn)在,如果你查一下被更新了的記錄:

db.unicorns.find({name: 'Roooooodles'})

你會發(fā)現(xiàn)?update?的第一個驚喜,沒找到任何文檔。因為我們指定的第二個參數(shù)沒有使用任何的更新選項,因此,它replace?了原始文檔。也就是說,?update?先根據(jù)?name?找到一個文檔,然后用新文檔(第二個參數(shù))覆蓋替換了整個文檔。這和 SQL 的?update?命令的完全不一樣。在某些情況下,這非常理想,可以用于某些完全動態(tài)更新上。但是,如果你只希望改變一個或者幾個字段的值的時候,你應(yīng)該用 MongoDB 的?$set?操作。繼續(xù),讓我們來更新重置這個丟失的數(shù)據(jù):

db.unicorns.update({weight: 590}, {$set: {
    name: 'Roooooodles',
    dob: new Date(1979, 7, 18, 18, 44),
    loves: ['apple'],
    gender: 'm',
    vampires: 99}})

這里不會覆蓋新字段?weight?因為我們沒有指定它?,F(xiàn)在讓我們來執(zhí)行:

db.unicorns.find({name: 'Roooooodles'})

我們拿到了期待的結(jié)果。因此,在最開始的時候,我們正確的更新 weight 的方式應(yīng)該是:

db.unicorns.update({name: 'Roooooodles'},
    {$set: {weight: 590}})

Update 操作符

除了?$set,我們還可以用其他的更新操作符做些有意思的事情。所有的更新操作都是對字段起作用 - 所以你不用擔(dān)心整個文檔被刪掉。比如,$inc?可以用來給一個字段增加一個正/負(fù)值。假設(shè)說 Pilot 獲得了非法的兩個 vampire kills 點,我們可以這樣修正它:

db.unicorns.update({name: 'Pilot'},
    {$inc: {vampires: -2}})

假設(shè) Aurora 忽然長牙了,我們可以給她的?loves?字段加一個值,通過?$push?操作:

db.unicorns.update({name: 'Aurora'},
    {$push: {loves: 'sugar'}})

MongoDB 手冊的?Update Operators?這章,可以查到更多可用的更新操作符的信息。

Upserts

用?update?還有一個最大的驚喜,就是它完全支持?upserts。所謂?upsert?更新,即在文檔中找到匹配值時更新它,無匹配時向文檔插入新值,你可以這樣理解。要使用 upsert 我們需要向 update 寫入第三個參數(shù)?{upsert:true}。

一個最常見的例子是網(wǎng)站點擊計數(shù)器。如果我們想保存一個實時點擊總數(shù),我們得先看看是否在頁面上已經(jīng)有點擊記錄,然后基于此再決定執(zhí)行更新或者插入操作。如果省略 upsert 選項(或者設(shè)為 false),執(zhí)行下面的操作不會帶來任何變化:

db.hits.update({page: 'unicorns'},
    {$inc: {hits: 1}});
db.hits.find();

但是,如果我們加上 upsert 選項,結(jié)果會大不同:

db.hits.update({page: 'unicorns'},
    {$inc: {hits: 1}}, {upsert:true});
db.hits.find();

由于沒有找到字段?page?值為?unicorns的文檔,一個新的文檔被生成插入。當(dāng)我們第二次執(zhí)行這句命令的時候,這個既存的文檔將會被更新,且?hits?會被增加到 2。

db.hits.update({page: 'unicorns'},
    {$inc: {hits: 1}}, {upsert:true});
db.hits.find();

批量 Updates

關(guān)于?update?的最后一個驚喜,默認(rèn)的,它只更新單個文檔。到目前為止,我們的所有例子,看起來都挺符合邏輯的。但是,如果你執(zhí)行一些像這樣的操作的時候:

db.unicorns.update({},
    {$set: {vaccinated: true }});
db.unicorns.find({vaccinated: true});

你肯定會希望,你所有的寶貝獨角獸都被接種疫苗了。為了達(dá)到這個目的,?multi?選項需要設(shè)為 true:

db.unicorns.update({},
    {$set: {vaccinated: true }},
    {multi:true});
db.unicorns.find({vaccinated: true});

小結(jié)

本章中我們介紹了集合的基本 CRUD 操作。我們詳細(xì)講解了?update?及它的三個有趣的行為。 首先,如果你傳 MongoDB 一個文檔但是不帶更新操作, MongoDB 的?update?會默認(rèn)替換現(xiàn)有文檔。因此,你通常要用到?$set?操作 (或者其他各種可用的用于修改文檔的操作)。 其次,?update?支持?upsert?操作,當(dāng)你不知道文檔是否存在的時候,非常有用。 最后,默認(rèn)情況下,?update?只更新第一個匹配文檔,因此當(dāng)你希望更新所有匹配文檔時,你要用?multi?。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號