mongoDB 对数组进行更新插入 [英] mongoDB upsert on array

查看:36
本文介绍了mongoDB 对数组进行更新插入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的数据库模型看起来像这样

my db model looks like this

"clientId":"123456"
"devices" : 
[{
      "deviceId" : "123",
      "deviceType" : "ios",
      "notification" : true,
  }
  {
      "deviceId" : "321",
      "deviceType" : "android",
      "notification" : true,

  }
 ]

我传递给我的数据库方法的 c# 模型具有 ClientId、DeviceId、DeviceType、notification.请注意,它没有设备数组

The c# model I pass to my DB method has ClientId,DeviceId,DeviceType,notification. Note that it doesn't have an array of devices

我试图实现的行为

  1. (working) 如果ClientId 还没有在数据库中,用传递的值在db 中创建一个新记录,(devices 数组将有一个元素)

  1. (working) If ClientId is not yet in the database, create a new record in db with the passed value,(devices array will have one element)

如果已经在数据库中找到了ClientId,则按照以下规则更新记录:

If ClientId is already found in the database, update the the record according to the following rules:

2.1 如果 devices 数组不包含 DeviceId,则使用传递的值向数组中添加一个新元素

2.1 If devices array doesn't contain DeviceId add a new element to the array with the passed values

2.2 如果 devices 数组已经包含 DeviceId 更新元素的 deviceType 和通知.

2.2 If devices array already contains DeviceId update the element's deviceType and notification.

我正在使用 C#.任何帮助表示赞赏

I'm using c#. any help appreciated

示例:给定数据库的上述状态,通过传递 clientId: 123456, deviceId 321 deviceType "kindle", notification "false" db 将更改为

example: given the above state of database, by passing clientId: 123456, deviceId 321 deviceType "kindle", notification "false" the db will change to

"clientId":"123456"
"devices" : 
[{
      "deviceId" : "123",
      "deviceType" : "ios",
      "notification" : true,
  }
  {
      "deviceId" : "321",
      "deviceType" : "kindle",
      "notification" : false,

  }
 ]

推荐答案

这并不像您想象的那么简单,实际上很有趣的是您将对此的分析分为三个部分.因为,你猜怎么着?这正是您必须做的.让我们考虑以下步骤:

This is not really as simple as you might think, and actually it is interesting that you have broken your analysis of this into three parts. Because, guess what? That is exactly what you must do. Let's consider the steps:

db.collection.update(
    { 
        "clientId":"123456"
    },
    {
        "$setOnInsert": {
            "clientId": "123456",
            "devices": [{
                "deviceId": "321",
                "deviceType" : "kindle",
                "notification" : false
            }]
        }
    },
    { "upsert": true }
)

所以您要做的是插入一个新文档,其中clientId"当前不存在.这可以作为更新插入"来完成,以避免可能的唯一键冲突,甚至在没有唯一"约束的情况下,这样的更新插入"性质确保您只在找不到新"文档时创建它.还有 $setOnInsert 在这里,因为您不想想对此时找到"的文档做任何事情.

So what you want to do is insert a new document where the "clientId" currently does not exist. This can be done as an "upsert" to avoid possible unique key clashes and even where there is no "unique" constraint, then the "upsert" nature of this ensures you only create the "new" document when it was not found. Also there is $setOnInsert here because you do not want to do anything to a document that is "found" at this point.

注意这里没有尝试匹配数组中的元素.这是因为您可能不想仅仅因为现有文档没有那个"数组元素就创建"一个新文档.这让我们进入下一步.

Note here that there is no attempt to match the element in the array. This is because you likely do not want to "create" a new document just because an existing one did not have "that" array element. Which brings us to the next step.

db.collection.update(
    { 
        "clientId":"123456",
        "devices": { "$elemMatch": { "deviceId" : "321" } }
    },
    {
        "$set": {
            "devices.$.deviceType" : "kindle",
            "devices.$.notification" : false
        }
    }
)

现在,您要实际尝试匹配"文档,clientId"确实包含数组中的一个元素,该元素也与您要查找的deviceId"匹配.因此,通过指定要匹配的条件,您可以使用位置 $ 运算符来将字段设置在匹配"位置.

Now here you want to actually try and "match" the document for the "clientId" that does contain an element in the array that also matches the "deviceId" you are looking for. So by specifying a condition to match, you get the use of the positional $ operator in order to set the fields in the "matching" position.

如上所述,这要么匹配one,要么没有,所以要么更新完成,要么没有.这样就进入了我们级联的最后一部分:

As above, this was either going to match one thing or nothing so either the update was done or it wasn't. So that moves to our final part of the cascade here:

db.collection.update(
    { 
        "clientId":"123456"
    },
    {
        "$addToset": { "devices": {
            "deviceId" : "321",
            "deviceType" : "kindle",
            "notification" : false
        }}
    }
)

所以这是重要最后阶段.原因是,如果上述操作中的任何一个确实创建"或更新"了现有文档,那么此处使用 $addToSet 可以确定strong> 您不是将另一个文档推送"到具有相同deviceId"但其他不同值的数组.如果这些阶段中的一个有效,那么这将看到该元素的所有值都已经存在,并且不会再添加另一个.

So this is importantly the last stage. The reason being that if either of the preceding operations did "create" or "update" the existing document, then the use of $addToSet here makes sure you are not "pushing" another document to the array with the same "deviceId" but other different values. If one of those stages worked, then this would see all the values of that element to already exist, and would not then add another one.

如果您尝试以不同的顺序执行此操作,则在您提供的情况下,数组中将有两个 文档具有相同的deviceId",但deviceType"和的值不同通知".所以这就是它最后出现的原因.

If you tried to do that in a different order, in the case you present you would have two documents in the array with the same "deviceId", but differing values for "deviceType" and "notification". So that is why it comes last.

很遗憾,没有简单的方法将这些组合为一个操作.运算符根本不存在,因此这可以在单个语句中完成,因此您必须执行三个更新操作以执行您想要的操作.同样如上所述,应用这些更新的顺序很重要,以便您获得所需的结果.

So unfortunately, there is no simple way to combine these as one operation. The operators simply do not exist so that this could be done in a single statement and therefore you must perform three update operations in order to do what you want. Also as stated, the order of application for those updates is important so that you get the desired result.

虽然这在当前的生产"版本中尚不存在,但即将发布的版本(撰写本文时为 2.6 及更高版本)确实可以"batch" 这些请求使用新语法更新:

While this does not exist yet in current "production" releases, the upcoming release ( 2.6 and upwards as of writing) does have a way to to "batch" these requests with an new syntax to update:

db.runCommand(
    "update": "collection",
    "updates": [
        { 
            "q": { "clientId":"123456" },
            "u": {
                "$setOnInsert": {
                    "clientId": "123456",
                    "devices": [{
                    "deviceId": "321",
                    "deviceType" : "kindle",
                    "notification" : false
                }]
            },
            "upsert": true
        },
        {
            "q": { 
                 "clientId":"123456",
                 "devices": { "$elemMatch": { "deviceId" : "321" } }
            },
            "u": {
                "$set": {
                    "devices.$.deviceType" : "kindle",
                    "devices.$.notification" : false
                 }
            }
        },
        {
            "q": { "clientId":"123456" },
            "u": {
                "$addToset": { "devices": {
                    "deviceId" : "321",
                    "deviceType" : "kindle",
                    "notification" : false
                }}
            }
        }
    ]
)

所以虽然那是仍然基本上三个操作,但至少你可以通过网络发送它们一次

So while that is still essentially three operations, at least you get to send them over the wire just once

这篇关于mongoDB 对数组进行更新插入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆