MongoDB 中更新文档时的条件更新插入(插入)
Conditional upsert (insert) when updating document in MongoDB
我在 MongoDB 中有一些文档,看起来像这样:
{type: type1, version: 2, data: ...}
{type: type1, version: 3, data: ...}
{type: type2, version: 1, data: ...}
{type: type2, version: 2, data: ...}
...
我想更新数据以匹配 type 和 version 或为给定的 type 创建一个新文档 当 version 不匹配但想禁止使用新 type 创建新文档
当我这样做时:
db.getCollection('products').update({"type": "unknown_type", "version" : "99"}, {$set: {"version": 99, "data": new data}}, {"upsert": true})
它创建了一个新文档:
{type: unknown_type, version: 99, data: ...}
这正是我想要禁止的。
有没有办法在一个电话中完成这个操作?有没有办法限制某些字段的值?
对于此用例,我能看到的最佳处理方式是使用 "Bulk Operations" 以便在同一请求中同时发送 "update" 和 "insert" 命令。我们还需要在此处有一个唯一索引,以强制您实际上不会创建这两个字段的新组合。
从这些文档开始:
{ "type" : "type1", "version" : 2 }
{ "type" : "type1", "version" : 3 }
{ "type" : "type2", "version" : 1 }
{ "type" : "type2", "version" : 2 }
并在两个字段上创建唯一索引:
db.products.createIndex({ "type": 1, "version": 1 },{ "unique": true })
然后我们尝试做一些实际插入的事情,对更新和插入都使用批量操作:
db.products.bulkWrite(
[
{ "updateOne": {
"filter": { "type": "type3", "version": 1 },
"update": { "$set": { "data": {} } }
}},
{ "insertOne": {
"document": { "type": "type3", "version": 1, "data": { } }
}}
],
{ "ordered": false }
)
我们应该得到这样的回应:
{
"acknowledged" : true,
"deletedCount" : 0,
"insertedCount" : 1,
"matchedCount" : 0,
"upsertedCount" : 0,
"insertedIds" : {
"1" : ObjectId("594257b6fc2a40e470719470")
},
"upsertedIds" : {
}
}
注意这里 matchedCount
是 0
反映了 "update" 操作:
"matchedCount" : 0,
如果我用不同的数据再次做同样的事情:
db.products.bulkWrite(
[
{ "updateOne": {
"filter": { "type": "type3", "version": 1 },
"update": { "$set": { "data": { "a": 1 } } }
}},
{ "insertOne": {
"document": { "type": "type3", "version": 1, "data": { "a": 1 } }
}}
],
{ "ordered": false }
)
然后我们看到:
BulkWriteError({
"writeErrors" : [
{
"index" : 1,
"code" : 11000,
"errmsg" : "E11000 duplicate key error collection: test.products index: type_1_version_1 dup key: { : \"type3\", : 1.0 }",
"op" : {
"_id" : ObjectId("5942583bfc2a40e470719471"),
"type" : "type3",
"version" : 1,
"data" : {
"a" : 1
}
}
}
],
"writeConcernErrors" : [ ],
"nInserted" : 0,
"nUpserted" : 0,
"nMatched" : 1,
"nModified" : 1,
"nRemoved" : 0,
"upserted" : [ ]
})
这将在所有驱动程序中持续抛出错误,但我们也可以在响应的详细信息中看到:
"nMatched" : 1,
"nModified" : 1,
这意味着即使 "insert" 失败了,"update" 实际上也完成了它的工作。这里要注意的重要一点是,虽然 "errors" 可以出现在 "batch" 中,但我们可以在它们属于预测类型时处理它们,即 11000
重复键错误的代码我们预期。
所以最后的数据当然是这样的:
{ "type" : "type1", "version" : 2 }
{ "type" : "type1", "version" : 3 }
{ "type" : "type2", "version" : 1 }
{ "type" : "type2", "version" : 2 }
{ "type" : "type3", "version" : 1, "data" : { "a" : 1 } }
这是您想在这里实现的目标。
所以操作会产生异常,但是通过将 { "ordered": false }
选项标记为 "unordered" 到 .bulkWrite()
那么它至少会提交任何没有导致异常的指令错误。
在这种情况下,典型的结果是 "insert" 有效但没有更新,或者 "insert" 在 "update" 适用的情况下失败。当响应中返回失败时,您可以检查错误的 "index" 是 1
指示预期的 "insert" 失败并且错误代码是 11000
因为预计 "duplicate key".
因此可以忽略 "expected" 情况下的错误,您只需要处理已发出的批量指令中不同代码 an/or 不同位置的 "unexpected" 错误。
我在 MongoDB 中有一些文档,看起来像这样:
{type: type1, version: 2, data: ...}
{type: type1, version: 3, data: ...}
{type: type2, version: 1, data: ...}
{type: type2, version: 2, data: ...}
...
我想更新数据以匹配 type 和 version 或为给定的 type 创建一个新文档 当 version 不匹配但想禁止使用新 type 创建新文档 当我这样做时:
db.getCollection('products').update({"type": "unknown_type", "version" : "99"}, {$set: {"version": 99, "data": new data}}, {"upsert": true})
它创建了一个新文档:
{type: unknown_type, version: 99, data: ...}
这正是我想要禁止的。 有没有办法在一个电话中完成这个操作?有没有办法限制某些字段的值?
对于此用例,我能看到的最佳处理方式是使用 "Bulk Operations" 以便在同一请求中同时发送 "update" 和 "insert" 命令。我们还需要在此处有一个唯一索引,以强制您实际上不会创建这两个字段的新组合。
从这些文档开始:
{ "type" : "type1", "version" : 2 }
{ "type" : "type1", "version" : 3 }
{ "type" : "type2", "version" : 1 }
{ "type" : "type2", "version" : 2 }
并在两个字段上创建唯一索引:
db.products.createIndex({ "type": 1, "version": 1 },{ "unique": true })
然后我们尝试做一些实际插入的事情,对更新和插入都使用批量操作:
db.products.bulkWrite(
[
{ "updateOne": {
"filter": { "type": "type3", "version": 1 },
"update": { "$set": { "data": {} } }
}},
{ "insertOne": {
"document": { "type": "type3", "version": 1, "data": { } }
}}
],
{ "ordered": false }
)
我们应该得到这样的回应:
{
"acknowledged" : true,
"deletedCount" : 0,
"insertedCount" : 1,
"matchedCount" : 0,
"upsertedCount" : 0,
"insertedIds" : {
"1" : ObjectId("594257b6fc2a40e470719470")
},
"upsertedIds" : {
}
}
注意这里 matchedCount
是 0
反映了 "update" 操作:
"matchedCount" : 0,
如果我用不同的数据再次做同样的事情:
db.products.bulkWrite(
[
{ "updateOne": {
"filter": { "type": "type3", "version": 1 },
"update": { "$set": { "data": { "a": 1 } } }
}},
{ "insertOne": {
"document": { "type": "type3", "version": 1, "data": { "a": 1 } }
}}
],
{ "ordered": false }
)
然后我们看到:
BulkWriteError({
"writeErrors" : [
{
"index" : 1,
"code" : 11000,
"errmsg" : "E11000 duplicate key error collection: test.products index: type_1_version_1 dup key: { : \"type3\", : 1.0 }",
"op" : {
"_id" : ObjectId("5942583bfc2a40e470719471"),
"type" : "type3",
"version" : 1,
"data" : {
"a" : 1
}
}
}
],
"writeConcernErrors" : [ ],
"nInserted" : 0,
"nUpserted" : 0,
"nMatched" : 1,
"nModified" : 1,
"nRemoved" : 0,
"upserted" : [ ]
})
这将在所有驱动程序中持续抛出错误,但我们也可以在响应的详细信息中看到:
"nMatched" : 1,
"nModified" : 1,
这意味着即使 "insert" 失败了,"update" 实际上也完成了它的工作。这里要注意的重要一点是,虽然 "errors" 可以出现在 "batch" 中,但我们可以在它们属于预测类型时处理它们,即 11000
重复键错误的代码我们预期。
所以最后的数据当然是这样的:
{ "type" : "type1", "version" : 2 }
{ "type" : "type1", "version" : 3 }
{ "type" : "type2", "version" : 1 }
{ "type" : "type2", "version" : 2 }
{ "type" : "type3", "version" : 1, "data" : { "a" : 1 } }
这是您想在这里实现的目标。
所以操作会产生异常,但是通过将 { "ordered": false }
选项标记为 "unordered" 到 .bulkWrite()
那么它至少会提交任何没有导致异常的指令错误。
在这种情况下,典型的结果是 "insert" 有效但没有更新,或者 "insert" 在 "update" 适用的情况下失败。当响应中返回失败时,您可以检查错误的 "index" 是 1
指示预期的 "insert" 失败并且错误代码是 11000
因为预计 "duplicate key".
因此可以忽略 "expected" 情况下的错误,您只需要处理已发出的批量指令中不同代码 an/or 不同位置的 "unexpected" 错误。