【发布时间】:2018-12-13 13:51:06
【问题描述】:
我想为新插入文档的“_id”字段赋值。 ID 字段必须从 0 开始,不能跳过数字。只有在没有包含重复内容的文档时才能进行插入。
目前这就是我所拥有的。
db.system.js.save({
_id: "nextId",
value: function (x) {
return db.counters.findAndModify({
query:{_id:x},
update:{$inc:{value:1}},
new:true
}).value;
}
})
db.loadServerScripts() // make the nextId available directly or via $where clause
db.counters.insert({_id: "_id", value: 0})
db.test.update(
{"value": "abc"},
{$setOnInsert: {"_id": nextId("_id"), "value": "abc"}},
{upsert: true}
)
如果没有包含重复“值”字段的文档,一切正常。当存在重复的“值”字段时,即使在 $setOnInsert 标记内,仍会调用 nextId("_id") javascript 函数。因此,此处观察到的不正确行为将是一些“_id”数字将被消耗/浪费,但不能分配给有效的新文档。正确/预期的行为行为是仅在实际插入新文档时才调用 nextId() 的情况。
问题:是否有解决方法可以在 MongoDB 中实现正确/预期的行为?
MongoDB 版本:v3.4.10(本地安装)和 v3.4.15(mlab MongoDB 托管)
注意:我已经放弃了这个问题,并决定放宽“不能跳过数字”的要求。修改后的要求是在全局范围内使用尽可能少的 Id,同时确保使用的 Id 具有尽可能低的值。在这个放松的需求变化之后,我设计了一个更好的、可扩展的解决方案。
1) 为每个客户保留适量的 id。结果是:
db.counters.findAndModify({
query: {_id:"_id"},
update: {$inc: {"_id_beg": 10, "_id_end": 10}},
new: true
})
2) 对于每个客户端的插入,使用以下
db.test.findAndModify({
query: {"value": <input value>},
update: {$setOnInsert: {"_id": <use lowest reserved id>, "value": <input value>"}
upsert: true,
new: true
})
3) 对于每个客户端的插入响应,findAndModify 的返回值包含 nextId。如果 nextId 与最低保留 id 相同,则表示已使用。如果不一样,说明另一个客户端插入了相同的值,我们可以使用保留的 id 稍后插入
有一些特定于案例的优化,例如 _id 上的唯一索引、来自崩溃客户端的未使用值等,以允许崩溃恢复
【问题讨论】:
-
这很奇怪,根据 $setOnInsert 上的 MongoDB 文档,它不应该发生 (docs.mongodb.com/manual/reference/operator/update/setOnInsert)。问题:当查询匹配现有文档并更新时(浪费了
nextId编号),_id是否更新为新值? -
其他问题:您使用的是哪个版本的 MongoDB?我无法在我的 shell 中重现您的代码(我收到
nextId is not defined错误)。根据文档,js函数应该只在mapReduce或$where上下文docs.mongodb.com/manual/tutorial/…)中可用 -
@cornacchia $setOnInsert 确实有效,但没有按预期工作。执行了 javascript,但不使用 javascript 的返回值。因此,当查询匹配现有文档时,现有文档不会更新,_id 字段保持不变。因为javascript仍然在$setOnInsert内部执行,新生成的_id被浪费了。
-
@cornacchia 作为对您的第二个问题的回应,我相信脚本未加载。您可以通过执行 db.loadServerScripts() 来修复“未定义”问题。我已经用这个澄清更新了问题。 docs.mongodb.com/v3.2/reference/method/db.loadServerScripts
-
感谢您的澄清,我现在可以重现该问题。这确实是非常奇怪的行为,我在文档中找不到任何关于它的信息。
标签: mongodb mongodb-query