如何用另一个数组更新 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 在 ) 之间更改了它,并且不将此报告为修改。
所以让服务器来完成工作,因为您可以编写的所有智能代码都已经存在。
具有以下名为 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()
:
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 在 ) 之间更改了它,并且不将此报告为修改。
所以让服务器来完成工作,因为您可以编写的所有智能代码都已经存在。