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

Sin*_*tic 2 mongodb event-sourcing mongodb-.net-driver

这意味着将被读取为双重更新插入操作,首先更新插入文档,然后更新插入数组元素。

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

  • 事件可能会无序出现,因此对数据库的每次更新都需要进行更新插入。
  • 我不仅需要能够更新插入父文档,还需要能够更新插入该文档的数组属性中的元素。

例如:

  • 如果该文档不存在,请创建它。该流中的所有事件都有文档的 ID,但只有部分信息取决于事件。
  • 如果该文档确实存在,则更新它。这是简单的部分。更新命令仅写为 UpdateOneAsync 和 upsert。
  • 如果事件实际上是要更新列表,则需要更新插入该列表元素。因此,如果文档不存在,则需要创建它,并且列表项将被更新插入(导致插入);如果文档确实存在,那么我们需要找到该元素并将其更新为更新插入,因此如果该元素存在,则更新它,否则插入它。

如果可能的话,让它作为单个原子操作执行将是理想的,但如果它只能通过多个步骤完成,那就这样吧。由于 2.x 驱动程序发生了巨大变化,我在网上得到了许多混合示例。不确定除了 UpdateOneAsync 之外我还在寻找什么。目前使用 2.4.x。解释的例子将不胜感激。TIA

注意:重申这是关于 MongoDB C# 驱动程序 2.4.x 的问题

Sin*_*tic 6

做了一些修补,但我明白了。

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);
Run Code Online (Sandbox Code Playgroud)

被链接为骗子的帖子绝对有帮助,但这不是答案。有一些事情需要被发现。

  • 链接示例中的“第二条语句”无法正常工作,至少在按字面翻译时是这样。为了让它工作,我必须匹配元素,然后通过将其包装在 Not() 过滤器中来反转逻辑。

  • 为了在匹配中使用“此索引”,您必须在数组上使用负索引。事实证明,在呈现查询时,C# 驱动程序会将任何负索引转换为“$”字符。

  • 为了确保它们按顺序运行,您必须包含IsOrdered设置为 true 的批量写入选项。