MongoDB更新嵌套数组foreach

时间:2016-10-13 09:35:07

标签: arrays mongodb mongodb-query

我有用户集合,每个用户都有一个数组祖先,以前的开发人员做错了数据库架构,现在每个祖先都是 string 但必须是 ObjectId 。它仍然包含objectId(实际上是对象Id的HEX,如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

2 个答案:

答案 0 :(得分:1)

使用mongoose尝试这样,

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="sel" size = "1">
</select>

答案 1 :(得分:1)

此处的解决方案概念是使用游标遍历集合,并为游标中的每个文档收集有关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批量更新集合。 与上述操作相比,这是非常有效的,因为使用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(); }

下一个示例适用于自deprecated Bulk() API以来的新MongoDB版本3.2,并使用 {{3}提供了一套更新的api }

它使用与上面相同的游标,但使用相同的 bulkWrite() 游标方法创建具有批量操作的数组,以将每个批量写入文档推送到数组。因为写入命令可以接受不超过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); }