【问题标题】:How to update/ insert if not exist an element into a sub array of a document in MongoDB C# driver如果不存在元素,如何更新/插入MongoDB C#驱动程序中文档的子数组
【发布时间】:2019-08-26 12:17:45
【问题描述】:

我有一个包含文档数组(或多或少复杂)的文档,我想创建 1 个查询,它将基于过滤器更新数组的元素,如果没有元素匹配,则将元素插入大批。我尝试了几件事,但没有任何效果。我不想做 2 个请求以避免并发问题。

下面是我的文档模型,它用他拥有的汽车为驾驶员建模

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

假设我有一个这样的模型。

汽车可以定义如下:

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

问题是我可以改变汽车的颜色,买一辆新车。

我想要一个能够根据用户的车牌号添加或更新文档的请求。

我尝试了几件事:

我创建了一个如下所示的过滤器:输入 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));

更新可能如下所示:

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

在我们可以放的选项中

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

如果找到汽车,该集合运行良好,但如果汽车不存在,则由于位置运算符返回错误。

我尝试了 AddToSet 运算符,但如果属性与颜色不匹配,它会插入一个新对象,而我想更新现有对象。

如果您有任何想法,请不要犹豫。 谢谢 哈克

【问题讨论】:

    标签: c# mongodb insert


    【解决方案1】:

    您可以使用这样的批量写入命令来完成:

    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"
                        }
                    }
                }
            }
        }
    ])
    

    这是生成上述 mongodb 命令的 c# 代码。为简洁起见,它使用 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();
            }
    
        }
    }

    【讨论】:

    • 您好,感谢您的回答,确实我们现在有 1 个请求。然而,这两个请求都被执行了。更新总是被调用,即使插入发生在之前。
    • 是的,不要认为有办法避免这种情况。也许其他人知道方法。
    【解决方案2】:

    您好,感谢您的回答,确实我们现在有 1 个请求。然而,这两个请求都被执行了。即使插入发生在之前,也会始终调用更新。

    有 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;
                 }
    

    这里如果直接插入的用户不存在汽车,则 upsertedId 不会为空,因此不会到达 if 块。

    这是一个并发问题,如果同一用户尝试在一个浏览器中调用更新并在另一个浏览器中调用删除,我们最终可能会这样做:

    Push => Delete => 在这种情况下更新更新会导致错误,因为该元素不存在。

    单请求解决方案更安全,因为它让数据库管理并发性避免了这类问题。

    Ryan 解决方案的 C# 代码如下所示:

            var bulkOps = new List<WriteModel<CarModel>>() { modelPush, modelUpdate };
    
            var res = await CarCollection.BulkWriteAsync(bulkOps);
            return res.ModifiedCount > 0;
    

    现在的问题是我们能否通过批量写入避免一直执行这两个请求?

    提前致谢:

    哈克

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-17
      • 1970-01-01
      相关资源
      最近更新 更多