如何在 Mongo 中实现 SELECT FOR UPDATE 给定这个查询?

问题描述 投票:0回答:1

假设我有一个名为“user_history”的集合。它包括一个名为“历史”的数组。 Collection 中还有一个“auto_increment”字段来模拟 MySQL 的字段。数组“history”中的每个元素都有一个名为“sequence”的字段,每次追加时都会将其设置为当前“auto_increment”。

我必须选择一个带有“userId”字段的文档,该字段是唯一的键。如果存在,我将在现有文档的“history”数组中添加一个新元素,并将“sequence”设置为当前“auto_increment”并将其增加 1。如果不存在,我将在“history”中插入一个元素的新文档' 数组的 'sequence' 0 并将 'auto_increment' 初始化为 1。这一切都必须以 ACID 方式运行。如果是 MySQL,我只会使用 SELECT FOR UPDATE。

这是“user_history”集合的示例

[
  {
    "user_id": 1,
    "history": [
      {
        "sequence": 0,
        "foo": "bar"
      }
    ],
    "auto_increment": 1,
  }
]

是否只能通过MongoDB支持的功能,比如findAndModify来实现这个功能?

或者我忍不住用应用程序逻辑来处理它,例如使用乐观锁定和重试,或者完全不同的解决方案(例如分布式锁)?

mongodb transactions spring-data-mongodb
1个回答
0
投票

您可以使用聚合更新更新插入

db.collection.updateOne(
    {user_id:1},
    [
        { $replaceRoot: { 
            newRoot: { $mergeObjects: [ 
                { "user_id": 1, "history": [], "auto_increment": 0 }, 
                "$$ROOT"  
            ] } 
        } },
        { $set: { 
            history: { $concatArrays: [
                "$history", 
                [{
                    "sequence": {$add: ["$auto_increment", 1]},
                    "foo": "bar"
                }]
            ] }, 
            auto_increment: {$add: ["$auto_increment", 1]}
        } }
   ],{upsert: true})

{user_id:1}
是您的“选择更新”,
[...]
是更新管道。

$replaceRoot
阶段实现“upsert”行为 - 如果没有匹配的文档,
$$ROOT
将为空,并且“$mergeObject”中的第一个元素将用于替换文档。否则
$$ROOT
将覆盖第一个对象(假设所有字段都在那里),即文档将被自身替换。

$set
阶段追加
history
数组并递增
auto_increment

作为旁注,

auto_increment
字段的名称可能有点误导,因为它没有任何“自动” - 您需要手动增加它。另一方面,BSON 保证数组中项目的顺序,并且根据
sequence
的预期用途,您可能可以仅依赖于
history
数组中项目的索引。

© www.soinside.com 2019 - 2024. All rights reserved.