我的数据库模型是这样的
"clientId":"123456"
"devices" :
[{
"deviceId" : "123",
"deviceType" : "ios",
"notification" : true,
}
{
"deviceId" : "321",
"deviceType" : "android",
"notification" : true,
}
]
我传递给DB方法的c#模型有ClientId,DeviceId,DeviceType,notification。注意,它没有设备数组
我想达到的行为
(working)如果ClientId还没有在数据库中,用传递的值在db中创建一个新的记录,(devices数组将有一个元素)
如果数据库中已经存在ClientId,则按照以下规则更新该记录:
如果devices数组中不包含DeviceId,则使用传入值 向数组中添加一个新元素。 如果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,
}
]
这并不像您想象的那么简单,实际上您将此分析分为三个部分是很有趣的。因为,你猜怎么着?这正是必须做的。让我们考虑以下步骤:
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"one_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"
}
}
)
你需要更新一个数组中匹配的元素,$为你完成这个工作,所以你可以找到一个文档,在一个数组中有一个特定的元素,并"记住"与位置操作符$匹配的更新它。