如何用另一个数组更新 MongoDB 集合中的数组但只更新更改的值?

how to update an array inside a MongoDB collection with another array but update only changed values?

具有以下名为 eshops 的集合:

{
"_id" : ObjectId("53e87e239ae974e6a0a81004"),
"name" : "www.example.com",
"products" : [
    {
        "name" : "apple", //lets say the name key here is primary key of products
        "status" : 0
    },
    {
        "name" : "banana",
        "status" : 0
    },
    {
        "name" : "iphone",
        "status" : 0
    }
]
}

并拥有这个数组

var products = [
{name: "apple", status: 1}
{name: "notebook", status: 0}
]

如果我想要以下结果,更新查询应该是什么样的?

{
"_id" : ObjectId("53e87e239ae974e6a0a81004"),
"name" : "www.example.com",
"products" : [
    {
        "name" : "apple",
        "status" : 1
    },
    {
        "name" : "banana",
        "status" : 0
    },
    {
        "name" : "iphone",
        "status" : 0
    },
    {
        "name" : "notebook",
        "status" : 0
    }
]
}

完整的解释在最后,请继续阅读。

那不可能 "atomically" 不能在单个操作中完成,你将得到的最好的是 "Bulk" 操作,这是最好的方法。

var products = [
    {name: "apple", status: 1}
    {name: "notebook", status: 0}
];

var bulk = db.collection.initializeOrderedBulkOp();

products.forEach(function(product) {

    // Try to update
    bulk.find({ 
        "_id" : ObjectId("53e87e239ae974e6a0a81004"),
        "products.name": product.name
    })
    .updateOne({
        "$set": { "products.$.status": product.status }
    });

    // Try to "push"
    bulk.find({ 
        "_id" : ObjectId("53e87e239ae974e6a0a81004"),
        "products.name": { "$ne": product.name }
    })
    .updateOne({
        "$push": { "products": product }
    });

});
bulk.execute();

另一种方法是通过 .findOne() 或类似操作检索文档,然后在客户端代码中更改数组内容,然后 .save() 返回更改后的内容。

那是您不想要的,因为无法保证文档自从被读入内存后就没有 "changed"。如果其他成员被添加到数组中,那么这种操作将 "overwrite" 他们。

因此循环使用多个更新的项目。至少 "Bulk" 操作立即将这些全部发送到服务器,而无需等待来自各个写入的响应。

但是正如你所指出的。如果值仍然相同怎么办?为此,您需要查看 .execute():

上 "Bulk" 操作的 "WriteResult" 响应
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })

尽管总共发送了四 (4) 个操作,但这里有两 (2) 个操作。如果数组包含更多项,则说 "iphone" 没有变化:

var products = [
    {name: "apple", status: 1}
    {name: "notebook", status: 0},
    {name: "iphone", status: 0 }
];

那么响应将是这样的:

WriteResult({ "nMatched" : 3, "nUpserted" : 0, "nModified" : 2 })

因为 "Bulk" API 足够聪明,可以看到匹配 "iphone" 上 "status" 的值与已经存在的值没有区别(假设没有else 在 ) 之间更改了它,并且不将此报告为修改。

所以让服务器来完成工作,因为您可以编写的所有智能代码都已经存在。