mongodb的存儲結構是靈活可變的,但是,並不意味着我們就肆意地使用不規則的文檔結構。不規則的文檔結構對於開發和后期的維護都是一個災難。所以,還是要有一個約定的格式。
但是,由於前期設計的不周詳和其他種種原因,數據庫文檔結構在開發過程的中修改總是難以避免的,應該盡量減少這種修改。但是,到了必須改的時候還是得改:
1 { 2 "_id" : ObjectId("54a1f775e4b03dad3af55c3c"), 3 "myId" : "54a0b115e4b00712935204ba", 4 "name" : "action", 5 "key" : "m_0", 6 "index" : 0, 7 "createTime" : ISODate("2014-12-30T00:53:09.483Z"), 8 "subMs" : [ 9 { 10 "_id" : null, 11 "rm" : { 12 "mt" : "TEXT", 13 "content" : "撒東西" 14 }, 15 "name" : "地說道", 16 "key" : "menu_0_0", 17 "type" : "CLICK" 18 } 19 ] 20 } 21 { 22 "_id" : ObjectId("54b87996e4b04b29b92a71b1"), 23 "myId" : "54b5e8cce4b045d4121f5d63", 24 "rm" : { 25 "msgType" : "URL", 26 "url" : "http://www.abc.com" 27 }, 28 "name" : "usercenter", 29 "key" : "user-center", 30 "type" : "VIEW", 31 "index" : 0, 32 "createTime" : ISODate("2015-01-16T02:38:14.643Z") 33 }] 34 35 }
有很多這樣類型的文檔。 這個文檔中的subMs字段中內嵌有若干個文檔。要做的事情就是把subMs中的若干個文檔脫離出來成為一個獨立的文檔,並且使新拆出來的文檔保留原父文檔的id。
腳本如下:
1 var mList = db.m.find(); 2 var mLength = mList.length; 3 var mArray = new Array(); 4 // 這一步比較關鍵,因為find 方法會返回一個游標, 5 // 如果不先關閉,或者將此游標用完,接下來在繼續往 6 // mongodb中插入數據會導致該游標發生混亂。 7 // 所以此處未往mongodb中修改前先把游標用完。 8 while(mList.hasNext()) { 9 mArray.push(mList.next()); 10 } 11 for(var i = 0; i < mLength; i ++) { 12 var mItem = mArray.pop(); 13 var sMList = mItem.subMs; 14 mItem.subMs = new Array(); 15 if(sMList == 0 || sMList.slength == 0) { 16 continue; 17 } 18 var sMLength = sMList.slength; 19 for(var j = 0; j < sMLength; j ++) { 20 var sMItem = sMList.pop(); 21 sMItem.pid = mItem._id.str; 22 sMItem.index = j; 23 sMItem.subMs = new Array(); 24 sMItem._id = undefined; 25 db.m.insert(sMItem); 26 } 27 db.m.save(mItem); 28 }
第一次寫起來還是有點吃力,
第一,是由於js的非常不熟悉。翻了好幾次w3school才看出了點東西。
第二,是對mongodb查詢游標的理解。
着重說說,mongodb 的游標:
游標:在mongodb查詢中,返回一個可迭代的對象,這個對象就叫做游標。這個對象保存着所有的查詢結果集。
游標的行為:
1. 在mongo shell 中,游標默認顯示前20個結果集,敲入 it 翻頁。顯示下一個 20個。
關閉mongodb 的游標:游標在mongodb中如果沒有被迭代到最后,那么它在10分鍾后自動關閉。或者是被迭代到最后。
修改mongodb游標的自動過期時間方法為:
var myCursor = db.m.find().addOption(DBQuery.Option.noTimeout);
// DBQuery 這個對象中也有挺多東西,有空翻來看看
由於游標在生存周期中不是隔離的,游標存活期間在對一個文檔的寫操作可能會時該文檔的返回次數超過一次。當這個文檔被改變之后,這就是我在寫上面腳本的時候一開始缺少了先把游標用完,就對原文檔修改,造成錯誤。
所以,我上面的寫法存在問題,當數據量很大的時候,把游標中所有的東西加載到內存后會把內存撐爆的。。。
解決辦法:使用游標快照: (P707)
mongodb的游標會對一個文檔返回超過一次,在一些特殊的情況下,那么這個時候就可以使用快照方法 snapshot().
snapshot()貫穿在_id字段上的索引,保證查詢返回的每個文檔出現的_id值不超過一次。(遵循_id值)
快照方法不保證數據返回時會返回單一時刻。 也不保證對插入和刪除操作的隔離。
警告:
1.不能再分片集合中使用快照方法
2.不能在快照方法的同時使用 sort方法或者hint()方法。
作為一個選擇,如果在集合中存在有一個或多個字段是永遠不會改變的,那么可以在該字段上創建唯一索引,來得到和使用庫依照方法類似的集合。查詢時使用hint方法強制查詢時使用這個唯一索引。
第三,mongo shell, 有個好東西, 當敲一個命令的時候,不敲后邊跟上的括號,就能顯示該命令底下將會執行的js方法。
比如以下命令:
> db.m.find 回車后會顯示對應的方法。
另外,有個挺好用的mongodb客戶端:robomongo 值得推薦。
以上參考了mongodb的官方文檔。寫的很給力的一個文檔。非常值得一看,居家備用查詢也挺好。