iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 5
0
Big Data

30天之你好MongoDB系列 第 5

30-5之新手村CRUD---更新

本篇將要來說明MongoDB中更新文檔的方法,並且也同時會說明更新修改器的功能,它能幫助我們進行更有效率的更新。

  • 基本更新方法Update
  • 更新修改器 ($set$inc)。
  • 更新修改器效能比較。

~ 基本更新方法Update ~


Update函數主要的功用就如同字面所說,更新~,而使用方法如下,query就是指你要先尋找更新的目標條件,update就是你要更新的值。而另外三個參考請考下列。

  • upsert : 這個參數如果是true,代表如果沒有找到該更新的對像,則新增,反之則否,默認是false
  • multi : 如果是false,則代表你query出多筆,他就只會更新第一筆,反之則都更新,默認是false( !注意multi只能在有修改器時才能用 )。
  • writeConcern : 拋出異常的級別。
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次。如果n10則更新結果要如下。

以下為程式碼,程式碼會放在此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這方法執行時會lockDB,所以上面的程式碼,第二個updateUseInc要先執行一次確定updateUseSet完畢後才開始計時。

P.S +u^5之我感冒囉…

~ 參考資料 ~



上一篇
30-4之新手村CRUD---新增之Bulk與新增效能測試
下一篇
30-6之新手村CRUD---更新之陣列欄位攻略
系列文
30天之你好MongoDB30

尚未有邦友留言

立即登入留言