【问题标题】:mongo change $position of an array element after additionmongo在添加后更改数组元素的$位置
【发布时间】:2018-08-16 12:43:31
【问题描述】:

使用 mongo 3.6

$position 用于 mongo $push

如何改变现有数组元素的位置。

{
"name":"a",
"arr":[
   {_id:1, val:1},
   {_id:3, val:3},
   {_id:2, val:2},
]
}

需要将第三个元素的位置改为第二个

【问题讨论】:

  • 您可以随时覆盖整个文档(如果一切都失败了)
  • eee ??这很冒险..因为其他人可能正在更新其他内容..
  • 您可以只覆盖arr。它的风险不亚于盲目地将 pos 3 的项目转移到 pos 2(当其他代码可能在几毫秒前这样做时)
  • eee??即使那样有风险..它必须更简单..因为有人可能仍在更新 arr 的其他部分
  • mongodb 读取是并行的,但写入是串行的。没有两个写请求同时处理@andNn

标签: mongodb


【解决方案1】:

问题暗示的方式不可能。当前 v3.6 中没有原子操作来重新排序嵌入文档,也没有事务。

cmets 提出真正的问题是如何安全地重新排序子文档,以考虑可能的并发写入。

您可以在应用程序级别实现乐观锁定:

const uid = process.pid + '-' + process.hrtime();

// lock the document for updates
db.collection.updateOne({_id, reordering: {$exists: false}}, {$set: {reordering: uid}})
.then(res=>{
    if (res.result.nModified !== 1) {
        throw new Error("concurent reordering or wrong _id")
    }
})
.then(()=>db.collection.findOne({_id, reordering:uid}))
.then(doc=>{
    if (!doc) {
        throw new Error("concurent write")
    }
    // reorder
    doc.arr.splice(1, 0, doc.arr.splice(2, 1)[0]);      
    Reflect.deleteProperty(doc, 'reordering');
    // commit and unlock
    return db.collection.replaceOne({_id, reordering: uid}, doc)
})
.then(res=>{
    if (res.result.nModified !== 1) {
        throw new Error("concurrent write")
    }
})

如果应用程序在 updateOnereplaceOne 之间的某个位置死机,则需要进行一些内务处理以清除残留锁。

如何捕获异常取决于您 - 您可以重试几次,或立即放弃。

【讨论】:

  • 嗨,Alex,感谢您的努力,但正在寻找稳定的东西。
  • 相当稳定——2 phase commits的简化版。你有什么顾虑?
  • 如果应用程序在获得锁后崩溃了怎么办,是否可以恢复
  • 这完全取决于你。锁是在应用程序级别实现的,因此它完全在您的控制之下——您可以为可恢复性付出多少努力。答案中的a bit of housekeeping 是由您决定如何处理孤立锁。由于这个原因,const uid = process.pid + '-' + process.hrtime(); 并不是一个随机数。
猜你喜欢
  • 2012-09-03
  • 1970-01-01
  • 1970-01-01
  • 2020-09-11
  • 2021-09-21
  • 1970-01-01
  • 1970-01-01
  • 2017-12-09
  • 2020-12-01
相关资源
最近更新 更多