使用upsert更新,但仅在db中的文档的日期字段小于更新的文档时才更新

Sam*_* I. 7 mongodb mongodb-query

我试图想出这个逻辑时遇到一些问题.所以,我想做的是:

  • 批量更新一堆帖子到我的远程MongoDB实例但是
  • 如果更新,则仅lastModified在远程集合上的字段小于lastModified我要更新/插入的同一文档中的字段时更新

基本上,我想更新我的文档列表,如果它们自上次更新后已被修改.我可以想到两种蛮力的方法来做到这一点......

首先,查询我的整个集合,尝试手动删除和替换符合条件的文档,添加新文档,然后在删除远程中的所有内容后将所有内容重新插入远程集合​​.

其次,查询每个项目然后决定,如果有一个在远程,如果我想更新它或不.这似乎在处理远程集合时会非常繁琐.

如果相关,我正在使用mondodbnpm包进行数据库操作的NodeJS环境.

chr*_*dam 2

您可以使用bulkWriteAPI 根据您指定的逻辑执行更新,因为它可以更好地处理此问题。

例如,以下代码片段展示了如何进行此操作,假设您已经拥有来自 Web 服务的数据,您需要使用以下数据来更新远程集合:

mongodb.connect(mongo_url, function(err, db) {
    if(err) console.log(err);
    else {
        var mongo_remote_collection = db.collection("remote_collection_name");

        /* data is from http call to an external service or ideally
           place this within the service callback
        */
        mongoUpsert(mongo_remote_collection, data, function() {
            db.close();
        })
    }
})

function mongoUpsert(collection, data_array, cb) {      
    var ops = data_array.map(function(data) {
        return {
            "updateOne": {
                "filter": { 
                    "_id": data._id, // or any other filtering mechanism to identify a doc
                    "lastModified": { "$lt": data.lastModified }
                },
                "update": { "$set": data },
                "upsert": true
            }
        };
    });

    collection.bulkWrite(ops, function(err, r) {
        // do something with result
    });

    return cb(false);
}
Run Code Online (Sandbox Code Playgroud)

如果来自外部服务的数据很大,那么请考虑以 500 个为一组向服务器发送写入,这样可以提供更好的性能,因为您不会将每个请求发送到服务器,而是每 500 个请求发送一次。

对于批量操作,MongoDB 强加了每批 1000 个操作的默认内部限制,因此选择 500 个文档是不错的选择,因为您可以对批大小进行一定的控制,而不是让 MongoDB 强加默认值,即对于数量级较大的操作> 1000 个文档。因此,对于上述第一种方法中的情况,可以一次写入所有数组,因为数组很小,但 500 个选择适用于较大的数组。

var ops = [],
    counter = 0;

data_array.forEach(function(data) {
    ops.push({
        "updateOne": {
            "filter": { 
                "_id": data._id,
                "lastModified": { "$lt": data.lastModified }
            },
            "update": { "$set": data },
            "upsert": true
        }
    });
    counter++;

    if (counter % 500 === 0) {
        collection.bulkWrite(ops, function(err, r) {
            // do something with result
        });
        ops = [];
    }
})

if (counter % 500 != 0) {
    collection.bulkWrite(ops, function(err, r) {
        // do something with result
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 等一下,如果“本地的lastModified小于远程的lastModified”,在这种情况下本地文档将不会被插入,因为mongo会抛出重复键错误,对吗?不满足过滤条件,由于“upsert:true”,mongo 将尝试插入带有 _id 的文档,插入将失败,因为带有该 _id 的文档已经存在。 (3认同)
  • 在这种情况下,找到带有ID但不满足lastModified条件的文档(本地的lastModified小于远程的lastModified),仍然会插入到集合中,不是吗?由于它们不落在过滤器参数上 (2认同)