mongoDB阵列上的upsert [英] mongoDB upsert on array

查看:83
本文介绍了mongoDB阵列上的upsert的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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. (正在工作)如果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",通知"false",数据库将更改为

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".这可以作为"upsert"来完成,以避免可能发生的唯一键冲突,即使在没有"unique"约束的情况下,也可以通过"upsert"性质确保仅在找不到"new"文档时创建它.此外,这里还有$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.

请注意,没有没有尝试匹配数组中的元素.这是因为您可能不希望仅由于现有文档没有"that"数组元素而创建"新文档.这将使我们进入下一步.

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.

如上所述,这要么匹配一个,要么不匹配 ,因此更新已完成或未完成.这样便移到了级联的最后一部分:

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可以使确定您不是将具有相同"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.

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

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及更高版本)确实可以使用这些请求,并带有

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阵列上的upsert的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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