Mongodb 如何同时插入记录和数组元素?

Mongodb 如何同时插入记录和数组元素?,mongodb,mongodb-.net-driver,event-sourcing,Mongodb,Mongodb .net Driver,Event Sourcing,这意味着要作为双上插操作读取,先上插文档,然后上插数组元素 所以MongoDB对我来说是一个非规范化的存储(我们是事件源代码),我试图处理的事情之一就是它的并发性。问题是: 事件的顺序可能不正确,因此对数据库的每次更新都需要进行升级 我不仅需要能够向上插入父文档,还需要能够向上插入该文档的数组属性中的元素 例如: 如果文档不存在,请创建它。此流中的所有事件都具有文档ID,但仅包含部分信息,具体取决于事件 如果文档确实存在,则更新它。这是最简单的部分。update命令只是作为UpdateOn

这意味着要作为双上插操作读取,先上插文档,然后上插数组元素

所以MongoDB对我来说是一个非规范化的存储(我们是事件源代码),我试图处理的事情之一就是它的并发性。问题是:

  • 事件的顺序可能不正确,因此对数据库的每次更新都需要进行升级
  • 我不仅需要能够向上插入父文档,还需要能够向上插入该文档的数组属性中的元素
例如:

  • 如果文档不存在,请创建它。此流中的所有事件都具有文档ID,但仅包含部分信息,具体取决于事件
  • 如果文档确实存在,则更新它。这是最简单的部分。update命令只是作为UpdateOneAsync和upsert编写的
  • 如果事件实际上是要更新列表,则需要升级该列表元素。因此,如果文档不存在,则需要创建该文档,并且列表项将被追加(导致插入);如果文档确实存在,那么我们需要找到该元素并将其更新为upsert,因此如果该元素存在,则将对其进行更新,否则将插入该元素
如果可能的话,让它作为单个原子操作来执行是理想的,但是如果它只能分多个步骤来完成,那就这样吧。由于2.x驱动程序的巨大变化,我在网上看到了一些混合的例子。不确定除了UpdateOneAsync之外我在寻找什么。目前正在使用2.4.x。请举例说明。短暂性脑缺血发作

注:
重申这是一个关于MongoDB C#driver 2.4.x的问题需要一些修改,但我明白了

var notificationData = new NotificationData
{
    ReferenceId = e.ReferenceId,
    NotificationId = e.NotificationId,
    DeliveredDateUtc = e.SentDate.DateTime
};

var matchDocument = Builders<SurveyData>.Filter.Eq(s => s.SurveyId, e.EntityId);

// first upsert the document to make sure that you have a collection to write to
var surveyUpsert = new UpdateOneModel<SurveyData>(
    matchDocument,
    Builders<SurveyData>.Update
        .SetOnInsert(f => f.SurveyId, e.EntityId)
        .SetOnInsert(f => f.Notifications, new List<NotificationData>())){ IsUpsert = true};

// then push a new element if none of the existing elements match
var noMatchReferenceId = Builders<SurveyData>.Filter
    .Not(Builders<SurveyData>.Filter.ElemMatch(s => s.Notifications, n => n.ReferenceId.Equals(e.ReferenceId)));

var insertNewNotification = new UpdateOneModel<SurveyData>(
    matchDocument & noMatchReferenceId,
    Builders<SurveyData>.Update
        .Push(s => s.Notifications, notificationData));

// then update the element that does match the reference ID (if any)
var matchReferenceId = Builders<SurveyData>.Filter
    .ElemMatch(s => s.Notifications, Builders<NotificationData>.Filter.Eq(n => n.ReferenceId, notificationData.ReferenceId));
var updateExistingNotification = new UpdateOneModel<SurveyData>(
    matchDocument & matchReferenceId,
    Builders<SurveyData>.Update 
        // apparently the mongo C# driver will convert any negative index into an index symbol ('$')
        .Set(s => s.Notifications[-1].NotificationId, e.NotificationId)
        .Set(s => s.Notifications[-1].DeliveredDateUtc, notificationData.DeliveredDateUtc));

// execute these as a batch and in order
var result = await _surveyRepository.DatabaseCollection
    .BulkWriteAsync(
        new []{ surveyUpsert, insertNewNotification, updateExistingNotification }, 
        new BulkWriteOptions { IsOrdered = true })
    .ConfigureAwait(false);
var notificationData=新的notificationData
{
ReferenceId=e.ReferenceId,
NotificationId=e.NotificationId,
DeliveredDateUtc=e.SentDate.DateTime
};
var matchDocument=Builders.Filter.Eq(s=>s.SurveyId,e.EntityId);
//首先向上插入文档,以确保您有一个要写入的集合
var surveyUpsert=新的更新模型(
匹配文件,
建设者。更新
.SetOnInsert(f=>f.SurveyId,e.EntityId)
.SetOnInsert(f=>f.Notifications,new List()){isupert=true};
//如果现有元素都不匹配,则推送新元素
var noMatchReferenceId=Builders.Filter
.Not(Builders.Filter.ElemMatch(s=>s.Notifications,n=>n.ReferenceId.Equals(e.ReferenceId));
var insertNewNotification=newupdateemodel(
matchDocument和noMatchReferenceId,
建设者。更新
.Push(s=>s.通知、通知数据);
//然后更新与引用ID匹配的元素(如果有)
var matchReferenceId=Builders.Filter
.ElemMatch(s=>s.Notifications,Builders.Filter.Eq(n=>n.ReferenceId,notificationData.ReferenceId));
var updateExistingNotification=new UpdateOneModel(
匹配文档和匹配引用ID,
建设者。更新
//显然,mongo C#驱动程序会将任何负索引转换为索引符号(“$”)
.Set(s=>s.Notifications[-1].NotificationId,e.NotificationId)
.Set(s=>s.Notifications[-1].deliveredateutc,notificationData.deliveredateutc));
//以批处理和顺序执行这些操作
var result=await_surveyRepository.DatabaseCollection
.BulkWriteAsync(
新[]{surveyUpsert,insertNewNotification,updateExistingNotification},
新的BulkWriteOptions{IsOrdered=true})
.配置等待(错误);
这篇文章被链接为一个傻瓜,这绝对是有帮助的,但这不是答案。有一些事情需要被发现

  • 链接示例中的“第二个语句”不起作用 正确,至少当逐字翻译时。为了让它发挥作用,我必须在 元素,然后通过将其包装在Not()过滤器中来反转逻辑

  • 为了在比赛中使用“此索引”,您必须使用 数组上的负索引。事实证明,C#driver会 执行查询时,将任何负索引转换为“$”字符 渲染

  • 为了确保它们按顺序运行,必须包括大容量写入
    IsOrdered
    设置为true的选项


    • 做了一些修补,但我成功了

      var notificationData = new NotificationData
      {
          ReferenceId = e.ReferenceId,
          NotificationId = e.NotificationId,
          DeliveredDateUtc = e.SentDate.DateTime
      };
      
      var matchDocument = Builders<SurveyData>.Filter.Eq(s => s.SurveyId, e.EntityId);
      
      // first upsert the document to make sure that you have a collection to write to
      var surveyUpsert = new UpdateOneModel<SurveyData>(
          matchDocument,
          Builders<SurveyData>.Update
              .SetOnInsert(f => f.SurveyId, e.EntityId)
              .SetOnInsert(f => f.Notifications, new List<NotificationData>())){ IsUpsert = true};
      
      // then push a new element if none of the existing elements match
      var noMatchReferenceId = Builders<SurveyData>.Filter
          .Not(Builders<SurveyData>.Filter.ElemMatch(s => s.Notifications, n => n.ReferenceId.Equals(e.ReferenceId)));
      
      var insertNewNotification = new UpdateOneModel<SurveyData>(
          matchDocument & noMatchReferenceId,
          Builders<SurveyData>.Update
              .Push(s => s.Notifications, notificationData));
      
      // then update the element that does match the reference ID (if any)
      var matchReferenceId = Builders<SurveyData>.Filter
          .ElemMatch(s => s.Notifications, Builders<NotificationData>.Filter.Eq(n => n.ReferenceId, notificationData.ReferenceId));
      var updateExistingNotification = new UpdateOneModel<SurveyData>(
          matchDocument & matchReferenceId,
          Builders<SurveyData>.Update 
              // apparently the mongo C# driver will convert any negative index into an index symbol ('$')
              .Set(s => s.Notifications[-1].NotificationId, e.NotificationId)
              .Set(s => s.Notifications[-1].DeliveredDateUtc, notificationData.DeliveredDateUtc));
      
      // execute these as a batch and in order
      var result = await _surveyRepository.DatabaseCollection
          .BulkWriteAsync(
              new []{ surveyUpsert, insertNewNotification, updateExistingNotification }, 
              new BulkWriteOptions { IsOrdered = true })
          .ConfigureAwait(false);
      
      var notificationData=新的notificationData
      {
      ReferenceId=e.ReferenceId,
      NotificationId=e.NotificationId,
      DeliveredDateUtc=e.SentDate.DateTime
      };
      var matchDocument=Builders.Filter.Eq(s=>s.SurveyId,e.EntityId);
      //首先向上插入文档,以确保您有一个要写入的集合
      var surveyUpsert=新的更新模型(
      匹配文件,
      建设者。更新
      .SetOnInsert(f=>f.SurveyId,e.EntityId)
      .SetOnInsert(f=>f.Notifications,new List()){isupert=true};
      //如果现有元素都不匹配,则推送新元素
      var noMatchReferenceId=Builders.Filter
      .Not(Builders.Filter.ElemMatch(s=>s.Notifications,n=>n.ReferenceId.Equals(e.ReferenceId));
      var insertNewNotification=newupdateemodel(
      matchDocument和noMatchReferenceId,
      建设者。更新
      .Push(s=>s.通知、通知数据);
      //然后更新与引用ID匹配的元素(如果有)
      var matchReferenceId=Builders.Filter
      .ElemMatch(s=>s.Notifications,Builders.Filter.Eq(n=>n.ReferenceId,notificationData.ReferenceId));
      var updateExistingNotification=new UpdateOneModel(
      匹配文档和匹配引用ID,
      建设者。更新
      //显然,mongo C#驱动程序会将任何负索引转换为索引符号(“$”)
      .Set(s=>s.Notifications[-1].NotificationId,e.NotificationId)
      .Set(s=>s.Notifications[-1].deliveredateutc,notificationData.deliveredateutc));
      //以批处理和顺序执行这些操作
      var result=await_surveyRepository.DatabaseCollection
      .BulkWriteAsync(
      新[]{surveyUpsert,insertNewNotification,updateExist