[英]How to update/ insert if not exist an element into a sub array of a document in MongoDB C# driver
我有一個包含一個文檔數組(或多或少復雜)的文檔,我想創建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運算符,但如果某個屬性與顏色不匹配,它將插入一個新對象,而我想更新現有對象。
如果您有任何想法,請不要猶豫。 謝謝哈克
您可以使用如下的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"
}
}
}
}
}
])
這是生成上面的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個請求的解決方案如下所示:
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不會為null,因此不會到達if塊。
與此相關的問題是並發問題,如果同一用戶嘗試在一個瀏覽器中調用一個更新,而在另一個瀏覽器中調用一個刪除,我們最終可能會這樣做:
推送=>刪除=>更新這種情況下的更新將導致錯誤,因為該元素不存在。
單請求解決方案比較安全,因為它可以讓數據庫管理並發避免此類問題。
Ryan解決方案的C#代碼如下所示:
var bulkOps = new List<WriteModel<CarModel>>() { modelPush, modelUpdate };
var res = await CarCollection.BulkWriteAsync(bulkOps);
return res.ModifiedCount > 0;
現在的問題是,批量寫入是否可以始終避免執行兩個請求?
提前致謝:
陳克勤
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.