简体   繁体   English

如何在MongoDB C#驱动程序中将元素不存在的情况下更新/插入到文档的子数组中

[英]How to update/ insert if not exist an element into a sub array of a document in MongoDB C# driver

I have a document which contains an array of document (more or less complex), I would like to create 1 query which will update the element of the array based on a filter, and if no element match insert the element into the array. 我有一个包含一个文档数组(或多或少复杂)的文档,我想创建1个查询,该查询将基于过滤器更新该数组的元素,如果没有匹配的元素,则将该元素插入到数组中。 I tried couple of thing but nothing worked. 我尝试了几件事,但没有任何效果。 I do not want to do 2 requests to avoid concurrency issue. 我不想做2个请求以避免并发问题。

Below is my document model, which model a driver with the car he owns 以下是我的文档模型,该模型使用自己拥有的汽车为驾驶员建模

public string Driver{ get; set; }
public Cars[] OwnedCars{ get; set; }

Let us suppose that I have a model like this. 让我们假设我有一个这样的模型。

The car can be defined as below: 汽车可以定义如下:

Car {
    color: string;
    plateNumber: string
    insuranceNumber: string,
    options: object
        .
        .
        .
}

The thing is that I can change the color of the car, a buy a new car. 问题是我可以改变汽车的颜色,买一辆新车。

I would like one request which enables to add or update a document based on the plate number for the user. 我想要一个能够根据用户的车牌号添加或更新文档的请求。

I tried several thing: 我尝试了几件事:

I created a filter which looks like this: driverId, car and plateNumber are input 我创建了一个过滤器,如下所示:输入了driverId,car和plateNumber

var filter = Builders < CarModel > .Filter.And(
    Builders < ViewConfigCollStorageModel > .Filter.Eq(x = > x.Driver, driverId),
    Builders < ViewConfigCollStorageModel > .Filter.ElemMatch(x = > x.ownedCars, x = > x.insuranceNumber == plateNumber));

the update could look like this: 更新可能如下所示:

var update = Builders<CarModel>.Update.Set(x => x.ownedCars[-1], car);
var res = await ViewConfigCollection.RawCollection.UpdateOneAsync(filter, update, option);

in the option we can put 在选项中我们可以放

var option = new UpdateOptions() { IsUpsert = true};

The set works well if the car is found, but return an error due to the positional operator if the car does not exist. 如果找到汽车,该集合将很好地工作,但是如果汽车不存在,则由于位置运算符而返回错误。

I tried AddToSet operator but if a property does not match like the color it insert a new object whereas I would like to update the existing one. 我尝试了AddToSet运算符,但如果某个属性与颜色不匹配,它将插入一个新对象,而我想更新现有对象。

If you have any idea please do not hesitate. 如果您有任何想法,请不要犹豫。 Thanks Hak 谢谢哈克

you could do it with a bulkwrite command such as this: 您可以使用如下的bulkwrite命令来做到这一点:

db.StorageModel.bulkWrite([
    {
        updateOne: {
            filter: {
                "DriverID": "321",
                "OwnedCars": {
                    "$not": {
                        "$elemMatch": {
                            "PlateNumber": "ABC123"
                        }
                    }
                }
            },
            update: {
                "$push": {
                    "OwnedCars": {
                        "PlateNumber": "ABC123",
                        "Color": "White"
                    }
                }
            }
        }
    },
    {
        updateOne: {
            filter: {
                "DriverID": "321",
                "OwnedCars": {
                    "$elemMatch": {
                        "PlateNumber": "ABC123"
                    }
                }
            },
            update: {
                "$set": {
                    "OwnedCars.$": {
                        "PlateNumber": "ABC123",
                        "Color": "White"
                    }
                }
            }
        }
    }
])

here's the c# code that generated the above mongodb command. 这是生成上面的mongodb命令的C#代码。 it uses MongoDB.Entities for brevity. 为了简洁起见,它使用MongoDB.Entities。

 using MongoDB.Entities; using System.Linq; namespace StackOverflow { public class Program { public class StorageModel : Entity { public string DriverID { get; set; } public Car[] OwnedCars { get; set; } } public class Car { public string PlateNumber { get; set; } public string Color { get; set; } } private static void Main(string[] args) { new DB("test"); (new StorageModel { DriverID = "321", OwnedCars = new[] { new Car { PlateNumber = "ABC123", Color = "Red"} } }).Save(); var updatedCar = new Car { PlateNumber = "ABC123", Color = "White" }; var bulk = DB.Update<StorageModel>(); bulk.Match(s => s.DriverID == "321" && !s.OwnedCars.Any(c => c.PlateNumber == updatedCar.PlateNumber)) .Modify(b => b.Push(s => s.OwnedCars, updatedCar)) .AddToQueue(); bulk.Match(s => s.DriverID == "321" && s.OwnedCars.Any(c => c.PlateNumber == updatedCar.PlateNumber)) .Modify(b => b.Set(s => s.OwnedCars[-1], updatedCar)) .AddToQueue(); bulk.Execute(); } } } 

Hi Thank you for your answer, indeed we have 1 request now. 您好,谢谢您的回答,确实我们现在有1个请求。 However both the requests are executed. 但是,两个请求都被执行。 The update is always call even if the the insert occurs before. 即使插入发生在之前,也总是调用更新。

The solution with 2 requests looks like this: 具有2个请求的解决方案如下所示:

 var res = await CarCollection.UpdateOneAsync(filterTryInsert, updateTryInsert);
           if(res.UpsertedId == null && res.MatchedCount == 0 && res.ModifiedCount ==0)
             {
              var resUp = await 
              CarCollection.UpdateOneAsync(filterUpdate,updateUp);
              return resUp.ModifiedCount > 0;
             }

Here if the car does not exist for the user it is directly inserted, upsertedId won't be null so the if block won't be reached. 在这里,如果用户不存在汽车,则无法直接插入汽车,upsertedId不会为null,因此不会到达if块。

The issue with this is a concurrency issue, if the same user tries to call an update in one browser and a delete in another one we could end up doing this: 与此相关的问题是并发问题,如果同一用户尝试在一个浏览器中调用一个更新,而在另一个浏览器中调用一个删除,我们最终可能会这样做:

Push => Delete => Update the update in this case will lead to an error because the element does not exist. 推送=>删除=>更新这种情况下的更新将导致错误,因为该元素不存在。

The one request solution is safer because it let the DB managing the concurrency avoiding those kind of issues. 单请求解决方案比较安全,因为它可以让数据库管理并发避免此类问题。

The C# code of the Ryan solution looks like this: Ryan解决方案的C#代码如下所示:

        var bulkOps = new List<WriteModel<CarModel>>() { modelPush, modelUpdate };

        var res = await CarCollection.BulkWriteAsync(bulkOps);
        return res.ModifiedCount > 0;

Now the question is could we with the bulk write avoid executing both requests all the time? 现在的问题是,批量写入是否可以始终避免执行两个请求?

Thanks in Advance: 提前致谢:

Hak 陈克勤

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用MongoDB和C#驱动程序更新子文档集合 - How to update a sub document collection using MongoDB and C# driver 如何使用C#驱动程序更新MongoDB数组中的子文档 - How to update child document in MongoDB Array using C# Driver C#Mongodb驱动程序-如何在位置0的数组中插入元素 - C# Mongodb Driver - How to insert an element into an array at position 0 MongoDB + C#驱动程序+查询元素数组,其中每个数组元素包含要查询的子文档 - MongoDB + C# driver + query array of elements where each array element contains sub-document to query on Mongodb C#驱动程序更新数组元素 - Mongodb C# driver update array element 使用 C# 驱动程序更新/删除 mongodb 中的子文档 - Update/Delete a sub document in mongodb using C# driver C#mongodb驱动程序-子数组中的更新不起作用 - C# mongodb driver - update in sub array not working 如何使用c#驱动程序删除mongodb文档中的嵌套数组元素 - How can I delete nested array element in a mongodb document with the c# driver 如何在C#Mongodb强类型驱动程序中删除嵌套文档数组内的元素 - How to delete an element inside array of a nested document in C# Mongodb strongly typed driver 如何使用 Mongodb c# 驱动程序更新文档的数组字段的特定索引值? - How to update document's array field's specific indexed value using Mongodb c# driver?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM