同时更新MongoDB中嵌入文档的数组元素

时间:2015-07-26 23:35:35

标签: mongodb mongodb-query

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

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

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

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

重要的是要注意:

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

1 个答案:

答案 0 :(得分:2)

这里的过程非常简单,只是在你想要“找到或创建”数组中元素的地方有所不同。

首先,假设每个键的元素已经存在,那么简单的情况是查询元素并使用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服务器处理。