【问题标题】:How to create auto-increment ID only on upsert?如何仅在 upsert 上创建自动增量 ID?
【发布时间】: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


【解决方案1】:

环顾四周,我发现了这个:https://stackoverflow.com/a/43695770/10026557

我测试了一下,好像可以用:

db.test.update(
  {"value": "bca"},
  {$setOnInsert: {"_id": db.test.find().count() + 1, "value": "bca"}},
  {upsert: true}
)

额外的好处是不必依赖另一个集合和/或 javascript 逻辑。

【讨论】:

  • 对于此解决方案,我相信如果由于“读取后写入(_id 值)(db.test.find() .count() + 1)" 问题。
  • 我已经放弃了这个,并且稍微放宽了要求。我设计了一个“无 javascript”、更高性能、可扩展、兼容 bulkop 的解决方案,没有并发问题(我希望如此)。我已经在原帖中更新了修改后的解决方案。感谢您的帮助。
猜你喜欢
  • 2023-03-15
  • 2013-07-27
  • 2013-03-30
  • 2022-09-29
  • 1970-01-01
  • 2016-08-09
  • 2022-08-03
  • 2012-12-23
  • 2010-11-05
相关资源
最近更新 更多