MongoDB 中嵌入文档的数组元素的并发更新

Posted

技术标签:

【中文标题】MongoDB 中嵌入文档的数组元素的并发更新【英文标题】:Concurrent update of array elements which are embedded documents in MongoDB 【发布时间】:2015-07-26 23:35:34 【问题描述】:

我在 MongoDB 的 x 集合中有这样的文档:


    "_id" : ...
    "attrs" : [
        
            "key": "A1",
            "type" : "T1",
            "value" : "13"
        ,
        
            "key": "A2",
            "type" : "T2",
            "value" : "14"
        
    ]

上面的A1A2 元素只是示例:attrs 字段可以包含任意数量的数组元素。

我需要从访问 MongoDB 的多个独立客户端同时访问 attrs 数组。例如,考虑两个客户端,一个想将key标识的元素的value等于“A1”更改为“80”,另一个想将key标识的元素的value更改为等于到“A2”到“20”。有没有使用 MongoDB 操作的紧凑方法?

需要注意的是:

客户端不知道attr数组中每个元素的位置,只知道元素的键值需要修改。 在客户空间读取整个attrs 数组,在客户空间搜索要修改的元素,然后用新数组(其中要修改的元素已更改)更新attrs 将涉及竞争条件。李> 客户端还可以添加和删除数组中的元素。因此,在 MongoDB 中进行第一次搜索以定位要修改的元素的位置,然后使用该特定位置更新它通常不起作用,因为元素可能已被添加/删除,从而改变了先前找到的位置。李>

【问题讨论】:

某种意义上这个问题是***.com/questions/31632070/…的后续问题 【参考方案1】:

这里的过程真的很简单,只是在你想要“查找或创建”数组中的元素的位置有所不同。

首先,假设每个键的元素已经就位,那么简单的情况是查询元素并使用positional $ 运算符返回的索引进行更新:

db.collection.update(
   
       "_id": docId, 
       "attrs":  "$elemMatch":  "key": "A1", "type": "T1"  
   
    "$set":  "attrs.$.value": "20" 
)

那只会修改匹配的元素,不会影响其他元素。

在需要“查找或创建”且特定键可能不存在的第二种情况下,您可以使用“两个”更新语句。但是Bulk Operations API 允许您在对服务器的单个请求中通过单个响应执行此操作:

var bulk = db.collection.initializeOrderedBulkOp();

// Try to update where exists
bulk.find(
    "_id": docId,
    "attrs":  "$elemMatch":  "key": "A1", "type": "T2"  
).updateOne(
    "$set":  "attrs.$.value": "30" 
);

// Try to add where does noes not exist
bulk.find(
    "_id": docId,
    "attrs":  "$not":  "$elemMatch":  "key": "A1", "type": "T2"   
).updateOne(
    "$push":  "attrs":  "key": "A1", "type": "T2", "value": "30"  
);

bulk.execute();

基本逻辑是首先尝试更新以匹配具有所需值的元素,就像之前所做的那样。其他条件通过使用$not 反转匹配逻辑来测试根本找不到元素的位置。

如果未找到数组元素,则可以通过$push 添加一个新元素。

我真的应该补充一点,因为我们在这里专门寻找否定匹配,所以通过一些唯一标识符(例如 _id 键)匹配您打算更新的“文档”总是一个好主意。虽然可以使用“多次”更新,但您需要小心自己在做什么。

因此,在运行“查找或创建”过程的情况下,不匹配的元素会被正确添加到数组中,而不会干扰其他元素,预期匹配的先前更新也以相同的方式应用:


    "_id" : ObjectId("55b570f339db998cde23369d"),
    "attrs" : [
            
                    "key" : "A1",
                    "type" : "T1",
                    "value" : "20"
            ,
            
                    "key" : "A2",
                    "type" : "T2",
                    "value" : "14"
            ,
            
                    "key" : "A1",
                    "type" : "T2",
                    "value" : "30"
            
    ]

这是一个简单的模式,当然这里的批量操作消除了向服务器发送和接收多个请求所涉及的任何开销。所有这些都可以愉快地工作,而不会干扰可能存在或不存在的其他元素。

除此之外,将数据保存在数组中还有其他好处,以便标准运算符支持的轻松查询和分析,而无需恢复到 javascript 服务器处理以遍历元素。

【讨论】:

非常感谢您的回答!我需要一些时间来深入分析,但看起来不错。

以上是关于MongoDB 中嵌入文档的数组元素的并发更新的主要内容,如果未能解决你的问题,请参考以下文章

更新 MongoDB 中精确元素数组中的字段

如何根据另一个字段更新 mongodb 中的数组 dict 元素

如何在 mongoDB 的嵌入式文档中提取特定元素

过滤 MongoDB 中的嵌入式数组

获取数组中每个索引的子文档元素计数并更新子文档键 - 数组中的子文档(IN MONGODB)

如何匹配 MongoDB 中嵌入式数组或文档中的字符串?