簡體   English   中英

MongoDB更新嵌套數組foreach

[英]MongoDB update nested array foreach

我收集了Users,每個用戶都有一個數組Ancestors,以前的開發人員做錯了DB體系結構,現在每個祖先都是string,但是必須是ObjectId 它仍然包含objectId(實際上是對象ID的十六進制,例如558470744a73274db0f0d65d )。 如何將每個祖先轉換為ObjectId? 我這樣寫:

db.getCollection('Users').find({}).forEach(function(item){
  if (item.Ancestors instanceof Array){
      var tmp = new Array()
      item.Ancestors.forEach(function(ancestor){
          if (ancestor instanceof String){
               tmp.push(ObjectId(ancestor))
             }
          })
          item.Ancestors = tmp
          db.getCollection('Users').save(item) 
  }
})

但是看起來它無法正常工作,現在有些祖先是ObjectId,有些是null 而且祖先從一開始就可以為空。 所以我把所有的if

像貓鼬一樣嘗試

var mongoose = require('mongoose');

db.getCollection('Users').find({}).forEach(function(item){
  if (item.Ancestors instanceof Array){
      var tmp = new Array()
      item.Ancestors.forEach(function(ancestor){
          if (ancestor instanceof String){
               tmp.push(mongoose.Types.ObjectId(ancestor))
             }
          })
          item.Ancestors = tmp
          db.getCollection('Users').save(item) 
  }
})

這里的解決方案概念是使用游標遍歷您的集合,並為游標中的每個文檔收集有關Ancestors數組元素的索引位置的數據。

然后,您稍后將在循環中將此數據用作更新操作參數,以正確標識要更新的元素。

假設您的集合不是那么笨拙,可以使用游標的forEach()方法實現上述直覺,就像您嘗試進行迭代並獲取所有涉及的數組元素的索引數據一樣。

下面展示了針對小型數據集的這種方法:

function isValidHexStr(id) {
    var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
    if(id == null) return false;
    if(typeof id == "string") {
        return id.length == 12 || (id.length == 24 && checkForHexRegExp.test(id));
    }
    return false;
};


db.users.find({"Ancestors.0": { "$exists": true, "$type": 2 }}).forEach(function(doc){ 
    var ancestors = doc.Ancestors,
        updateOperatorDocument = {}; 
    for (var idx = 0; idx < ancestors.length; idx++){ 
        if(isValidHexStr(ancestors[idx]))                   
            updateOperatorDocument["Ancestors."+ idx] = ObjectId(ancestors[idx]);           
    };  
    db.users.updateOne(
        { "_id": doc._id },
        { "$set": updateOperatorDocument }
    );      
});

現在,為了提高性能,尤其是在處理大型集合時,請利用Bulk() API Bulk()更新集合。 與上述操作相反,這非常有效,因為使用bulp API時,您將分批將操作發送到服務器(例如,批量大小為1000),這將為您提供更好的性能,因為您不會發送對服務器的每個請求,但每1000個請求中只有一個,因此使您的更新更高效,更快。

以下示例演示了如何使用MongoDB版本>= 2.6< 3.2可用的Bulk() API。

function isValidHexStr(id) {
    var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
    if(id == null) return false;
    if(typeof id == "string") {
        return id.length == 12 || (id.length == 24 && checkForHexRegExp.test(id));
    }
    return false;
};

var bulkUpdateOps = db.users.initializeUnOrderedBulkOp(), 
    counter = 0;

db.users.find({"Ancestors.0": { "$exists": true, "$type": 2 }}).forEach(function(doc){ 
    var ancestors = doc.Ancestors,
        updateOperatorDocument = {}; 
    for (var idx = 0; idx < ancestors.length; idx++){ 
        if(isValidHexStr(ancestors[idx]))                   
            updateOperatorDocument["Ancestors."+ idx] = ObjectId(ancestors[idx]);           
    };
    bulkUpdateOps.find({ "_id": doc._id }).update({ "$set": updateOperatorDocument })

    counter++;  // increment counter for batch limit
    if (counter % 1000 == 0) { 
        // execute the bulk update operation in batches of 1000
        bulkUpdateOps.execute(); 
        // Re-initialize the bulk update operations object
        bulkUpdateOps = db.users.initializeUnOrderedBulkOp();
    } 
})

// Clean up remaining operation in the queue
if (counter % 1000 != 0) { bulkUpdateOps.execute(); }

下一個示例適用於新的MongoDB 3.2版,此版本已棄用 Bulk() API,並使用bulkWrite()提供了一組較新的api。

它使用與上面相同的游標,但是使用相同的forEach()游標方法通過批量操作創建數組,以將每個批量寫入文檔推入數組。 由於寫命令最多只能接受1000個操作,因此您需要將操作分組以最多具有1000個操作,並在循環達到1000次迭代時重新初始化數組:

var cursor = db.users.find({"Ancestors.0": { "$exists": true, "$type": 2 }}),
    bulkUpdateOps = [];

cursor.forEach(function(doc){ 
    var ancestors = doc.Ancestors,
        updateOperatorDocument = {}; 
    for (var idx = 0; idx < ancestors.length; idx++){ 
        if(isValidHexStr(ancestors[idx]))                   
            updateOperatorDocument["Ancestors."+ idx] = ObjectId(ancestors[idx]);           
    };
    bulkUpdateOps.push({ 
        "updateOne": {
            "filter": { "_id": doc._id },
            "update": { "$set": updateOperatorDocument }
         }
    });

    if (bulkUpdateOps.length == 1000) {
        db.users.bulkWrite(bulkUpdateOps);
        bulkUpdateOps = [];
    }
});         

if (bulkUpdateOps.length > 0) { db.users.bulkWrite(bulkUpdateOps); }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM