注意:不是常见的upsert案例。
Present Document example:
{
_id : 123
array:[
{
id : a
col1 : val1
col2 : val2
},
{
id : b
col1 : val1
col2 : val2
}
]
}
Update Document Sample:
{
id : a
col1 : val1
col2 : val2
col3 : val3
}
我无法弄清楚如何做到这一点。 我使用更新查询:
{
_id : 123
array.id : a
}
这应该照顾我认为的前3个点(更新选项有upsert:true)
如何确保仅附加其他元素。由于文档不完全相同,Upsert会复制。由于我想索引这些id,因此操作会因重复而失败。
答案 0 :(得分:1)
这里的基本情况是“upserts”和“添加到数组”在组合中作为单个语句不能很好地混合。您可以使用$addToSet
进行非常“基本”的处理,但这通常不适合这种情况,特别是当您想要“更新”相关数组的内容时。
所以这总是分解为“多个”更新语句,以便获得最终所需的数据状态。 “多个”更新的最佳案例处理是使用Bulk Operations API,因为所有操作都可以在“单个”请求中以“单个”响应发送。
这使得处理方面的效率更高,因为在尝试下一个操作之前,您不需要等待来自服务器的每个操作的响应。它只需要仔细制作操作,以便在添加项目时它们之间不会发生冲突。
假设您“实际上”收到了这样的更新数据包:
{
"_id" : 123,
"id" : "a",
"col1" : "val1",
"col2" : "val2",
"col3" : "val3"
}
然后你的操作编码如下:
var bulk = db.collection.initializeOrderBulkOp();
// Try to set fields in matched array where found
bulk.find({ "_id": 123, "array.id": "a" }).updateOne({
"$set": {
"array.$.col1": "val1",
"array.$.col2": "val2",
"array.$.col3": "val3"
}
});
// Try to "push" fields to new array where array not found ( no upsert )
bulk.find({ "_id": 123, "array.id": { "$ne": "a" } }).updateOne({
"$push": {
"array": {
"id": "a",
"col1": "val1",
"col2": "val2",
"col3": "val3"
}
}
});
// Try to "upsert" where the basic document is not found.
// Only modify on "insert" via $setOnInsert
bulk.find({ "_id": 123 }).upsert().updateOne({
"$setOnInsert": {
"array": [
{
"id": "a",
"col1": "val1",
"col2": "val2",
"col3": "val3"
}
]
}
});
// Actually send and execute the operations
bulk.execute();
这通过实现以下逻辑来解决问题:
查找当前数组并相应地更新。这些是"positional"更新地址第3点和第5点。
如果匹配的文档中不存在该数组,则“推送”新的数组条目。所以$ne
意味着数组不存在。请注意,这是不 upsert尝试,故意这样做。地址分别为第2点和第4点。
如果文档根本不存在,则添加带有全新数组的新文档。地址1,实际上也是2。
这是多次更新,但它仍然只是“一个”请求和对数据库服务器的响应,所以这是一件好事。 MongoDB甚至不会尝试“替换”已经存在的具有相同值的值,因此不必担心这一点,而不是真的很重要。
我还建议您重新构建输入数据包,以更好地反映您想要做的事情:
{
"_id" : 123,
"array": [{
"id" : "a",
"col1" : "val1",
"col2" : "val2",
"col3" : "val3"
}]
}
因为以编程方式使用它来构造执行更新所需的语句要容易得多,因为结构已经存在以供引用。
如果您尝试将“upsert”与这些操作“混合”,那么所需的测试不可避免地会导致创建新文档,而您并不打算这样做。因此,您需要在要执行的更新类型中进行多项操作。
正是出于这个原因,为什么“upsert”执行“last”,并始终使用$setOnInsert
修饰符,以便任何“匹配”文档实际上都不会被修改,除非结果实际上是“upsert” “,创建一个全新的文档。同样,在单个语句中将其他更新修饰符与此操作混合是不明智的。
这是“Bulk API”存在的一个很好的理由,因此像这样的“链式”逻辑可以放入单个服务器请求中,而不是多个请求。