mongoDB upsert在数组上

Jur*_*rik 3 c# arrays mongodb mongodb-query

我的db模型看起来像这样

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

  }
 ]
Run Code Online (Sandbox Code Playgroud)

我传递给我的DB方法的c#模型有ClientId,DeviceId,DeviceType和通知.请注意,它没有设备阵列

我正在努力实现的行为

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

  2. 如果已在数据库中找到ClientId,请根据以下规则更新记录:

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

    2.2如果设备阵列已包含DeviceId,则更新元素的deviceType和通知.

我正在使用c#.任何帮助赞赏

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

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

  }
 ]
Run Code Online (Sandbox Code Playgroud)

Nei*_*unn 6

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

1.如果文档不存在,请插入文档

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

所以你想要做的是插入一个新文档,其中"clientId"目前不存在.这可以作为"upsert"来完成,以避免可能的唯一键冲突,即使没有"唯一"约束,那么这种"upsert"性质可确保您只在未找到时创建"新"文档.也有是$setOnInsert在这里,因为你希望做任何事情,那就是"发现"在这一点上的文件.

请注意,没有尝试匹配数组中的元素.这是因为您可能不想仅仅因为现有文档没有"那个"数组元素而"创建"新文档.这将我们带到下一步.

2.更新文档所在的内容

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

现在这里要真正地尝试了"的clientId",即"相匹配"的文件包含数组也匹配"的DeviceID"你正在寻找的元素.因此,通过指定要匹配的条件,可以使用位置$运算符来设置"匹配"位置的字段.

如上所述,这要么是匹配件事,要么是一无所有,所以更新已经完成或者没有.所以这里移动到我们的级联的最后部分:

3.将数组元素添加到不存在的位置

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

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

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

结论

不幸的是,没有简单的方法将这些组合为一个操作.操作符根本不存在,因此可以在单个语句中完成,因此您必须执行三个更新操作才能执行所需操作.同样如上所述,这些更新的申请顺序非常重要,这样您才能获得所需的结果.

虽然目前的"生产"版本中尚不存在这种情况,但即将发布的版本(编写时为2.6及更高版本)确实有一种方法可以通过更新的新语法"批处理"这些请求:

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
                }}
            }
        }
    ]
)
Run Code Online (Sandbox Code Playgroud)

因此,虽然这仍然是三个操作,但至少你只需通过电线发送一次