mongoDB对数组进行upsert

本文关键字:upsert 数组 mongoDB | 更新日期: 2023-09-27 17:53:30

我的数据库模型是这样的

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

我传递给DB方法的c#模型有ClientId,DeviceId,DeviceType,notification。注意,它没有设备数组

我想达到的行为

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

  2. 如果数据库中已经存在ClientId,则按照以下规则更新该记录:

    如果devices数组中不包含DeviceId,则使用传入值 向数组中添加一个新元素。
  3. 如果devices数组中已经包含DeviceId,则更新元素的deviceType和notification。

我正在使用c#。感谢任何帮助

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

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

mongoDB对数组进行upsert

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

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

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

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

注意这里没有尝试匹配数组中的元素。这是因为您可能不希望仅仅因为现有文档没有"那个"数组元素就"创建"一个新文档。这就引出了下一步。

2。更新文档中存在的内容

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

现在在这里,您想要实际尝试和"匹配"文档的"clientId"在数组中包含一个元素,也匹配您正在寻找的"deviceId"。因此,通过指定要匹配的条件,您可以使用位置$操作符来将字段设置在"匹配"位置。

如上所述,这要么匹配一个内容,要么不匹配任何内容,所以要么更新完成,要么不更新。这就移动到了级联的最后一部分:

3。添加不存在的数组元素

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

所以这是重要的是最后一个阶段。原因是,如果前面的操作中的任何一个"创建"或"更新"现有的文档,那么使用$addToSet在这里使确定你不是"推"另一个文档到数组具有相同的"deviceId",但其他不同的值。如果其中一个阶段工作,那么这将看到该元素的所有值已经存在,然后不会添加另一个。

如果您尝试以不同的顺序执行,在您呈现的情况下,您将在数组中拥有两个文档,它们具有相同的"deviceId",但"deviceType"answers"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
                }}
            }
        }
    ]
)

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

您正在寻找一个典型的位置操作符案例:

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

你需要更新一个数组中匹配的元素,$为你完成这个工作,所以你可以找到一个文档,在一个数组中有一个特定的元素,并"记住"与位置操作符$匹配的更新它。