本篇將要來說明MongoDB中更新文檔的方法,並且也同時會說明更新修改器的功能,它能幫助我們進行更有效率的更新。
Update。$set、$inc)。Update ~Update函數主要的功用就如同字面所說,更新~,而使用方法如下,query就是指你要先尋找更新的目標條件,update就是你要更新的值。而另外三個參考請考下列。
true,代表如果沒有找到該更新的對像,則新增,反之則否,默認是false。false,則代表你query出多筆,他就只會更新第一筆,反之則都更新,默認是false( !注意multi只能在有修改器時才能用 )。db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
下面來簡單示範一下用法。首先我們先新增三筆資料。
db.user.insert({"name":"mark","age":23});
db.user.insert({"name":"steven","age":23});
db.user.insert({"name":"jj","age":23});
然後我們將名字為mark這人的age改為18,指令如下,query為{"name":"mark"},query的詳細用法會在find那邊詳詳細細的說明。
db.user.update({"name":"mark"},{"name":"mark","age":18})
執行結果如下,不過誒……我只要更新age也,為啥要全部換掉?

$set、$inc) ~$set$set修改器主要的功用就是用來指定一個字段的值,不用像上面一樣整個替換掉。
所以如我們如果要將mark這位仁兄的age改為18只要下達下面的指令。
db.user.insert({"name":"mark","age":23});
db.user.insert({"name":"steven","age":23});
db.user.insert({"name":"jj","age":23});
db.user.update({"name":"mark"},{"$set" : { "age" : 18} })
執行結果如下,成功更新為age為18

$inc假設一下情景,假如有個投票網站、或是要存放訪客數的功能,每次更新時都是要+1,這種時後就可以用$inc來更新你的document,理論上來說速度應該會優於$set,等會兒會來測試一下。
注意$inc只能用在數值類型,否則就會提示Modifier $inc allowed for numbers only。
我們寫段程式碼來看看他的使用方法,下面範例我們先新增一筆資料,然後我們每次更新時,like都會加1,所以我們更新3次,理論上like會變為3。
db.home.insert({"id" : 1 ,"like" : 0})
db.home.update({"id" : 1},{"$inc" : {"like" : 1}})
db.home.update({"id" : 1},{"$inc" : {"like" : 1}})
db.home.update({"id" : 1},{"$inc" : {"like" : 1}})
執行結果如下,可以看到like增加到3了。

$set與$inc效能測試 ~假設一個情況,有個功能是存放訪客like數,一樣每次更新時都是要+1,我們這時來比較看看,來看看那個更新較快,測試的環境一樣使用nodejs。
本測試會新增一筆資料然後更新n次。如果n為10則更新結果要如下。

以下為程式碼,程式碼會放在此MyGithub。
程式碼簡單的說一下,首先會先用bulk insert來建立一筆資料,然後接下來在產生n個更新的promise,最後用promise all等所以promise都更新完後,再結束計時。
debugger;
var mongodb = require('mongodb');
var mongodbServer = new mongodb.Server('localhost', 27017, {
auto_reconnect: true,
poolSize: 10
});
var db = new mongodb.Db('test', mongodbServer);
var count = 10;
db.open(function() {
db.collection('home', function(err, collection) {
/*
* Update use $set 測試
*/
var bulk = collection.initializeUnorderedBulkOp();
bulk.insert({
"id": 1,
"name": "mark",
"Like": 0
});
bulk.execute(function(err, res) {
console.time("update use $set");
var funcs = [];
for (var i = 1; i < count+1; i++) {
funcs.push(updateUseSet(i));
}
Promise.all(funcs).then((res) => {
console.timeEnd("update use $set");
});
});
function updateUseSet(i) {
return new Promise((resolve,reject) => {
collection.update({
"name": "mark"
}, {
"$set": {
"Like": i
}
}, function(err, res) {
resolve(i);
});
});
}
/*
* Update use $inc 測試
*/
var bulk = collection.initializeUnorderedBulkOp();
bulk.insert({
"id": 2,
"name": "steven",
"Like": 0
});
bulk.execute(function(err, res) {
var funcs = [];
udpateUseInc().then((res) => {
console.time("update use $inc");
for (var j = 1; j < count+1;j++) {
funcs.push(updateUseInc(1));
}
Promise.all(funcs).then((res) => {
console.timeEnd("update use $inc");
});
})
});
function updateUseInc() {
return new Promise((resolve,reject) => {
collection.update({
"name": "steven"
}, {
"$inc": {
"Like": 1
}
}, function(err, res) {
resolve();
});
});
}
});
});
測試結果,$inc在更新數據時,完全贏過$set。
| 測試案例(更新次數) | $set | $inc |
|---|---|---|
| 10 | 39ms | 19ms |
| 1000 | 317ms | 257ms |
| 10000 | 4159ms | 3206ms |
| 50000 | 31893ms | 15929ms |
| 100000 | 154861ms | 129684ms |
事實上寫這篇時,一開始發現為啥$inc執行時間總是大於$set,不符合預期,一直覺得怪怪的後來查來了一下發現update這方法執行時會lock住DB,所以上面的程式碼,第二個updateUseInc要先執行一次確定updateUseSet完畢後才開始計時。
P.S +u^5之我感冒囉…