如何仅在upsert上创建自动增量ID?

时间:2018-07-05 07:06:49

标签: mongodb mongodb-query

我想为新插入文档的“ _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上的唯一索引,崩溃的客户端中未使用的值等,以允许崩溃恢复

1 个答案:

答案 0 :(得分:0)

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

我对其进行了测试,看起来它可以工作:

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

额外的奖励不必依靠其他集合和/或javascript逻辑。