如何使用C#驱动程序更新MongoDB数组中的子文档

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用C#驱动程序更新MongoDB数组中的子文档相关的知识,希望对你有一定的参考价值。

如何在MongoDB中修改文档的子数组中的子元素?我正在使用最新的MongoDB C#驱动程序(C#驱动程序版本:2.5,服务器版本:3.2.0),我从SO(重复的问题)和各种其他来源尝试了多个答案的解决方案。大多数解决方案被证明是过时的。

以下是我的示例文档:

[{ 
"_id" : "69c4f77d-05ef-431d-ae17-c076c6173e04",     
"title" : "Root Page", 
"createdAt" : ISODate("2017-12-21T12:28:00.680+0000"), 
"modifiedAt" : ISODate("2017-12-26T07:18:11.165+0000"), 
"pages" : [
    {
        "_id" : "f449dc0b-3d1b-4a59-b622-6a42ce10b147", 
        "title" : "Test page 1", 
        "slug" : "test-page-1", 
        "createdAt" : ISODate("2017-12-21T12:28:00.680+0000"), 
        "modifiedAt" : ISODate("2017-12-21T12:28:00.680+0000")
    }, 
    {
        "_id" : "3d1497d7-f74c-4d88-b15c-bf2f9c736374", 
        "title" : "Test page 2", 
        "slug" : "test-page-2", 
        "createdAt" : ISODate("2017-12-25T11:27:55.006+0000"), 
        "modifiedAt" : ISODate("2017-12-25T11:27:55.006+0000")
    }, 
    {
        "_id" : "6e827e2a-5a25-4343-b646-885816bb8cc4", 
        "title" : "Test page 3", 
        "slug" : "test-page-3", 
        "createdAt" : ISODate("2017-12-25T11:31:16.516+0000"), 
        "modifiedAt" : ISODate("2017-12-25T11:31:16.516+0000")
    }]
},
...,
...
]

文档结构:根文档包含一个子文档数组,其中包含元数据和页面列表(数组)

在子文档中 - 我正在尝试更新页面数组中子页面对象的标题和slug,但到目前为止还没有运行最新的驱动程序。

我正在使用以下过滤器并更新查询:

        var filter = Builders<SubDocument>.Filter.And(Builders<SubDocument>.Filter.Eq("_id", "xxx"), Builders<SubDocument>.Filter.ElemMatch(x => x.Pages, p => p.Id == "xxx"));

        var update = Builders<SubDocument>.Update
                        .Set("pages.$.title", "changed title")
                        .Set("pages.$.slug", "changed-title")
                        .Set("pages.$.modifiedAt", DateTime.UtcNow);

        _mongoDb.Documents.UpdateOne(filter, update);

现在 - 这个查询给我一个错误:

无效的BSON字段名称页面。$ .title

我最近几个小时提到的引用是说使用“$”运算符,但它在最新的驱动程序中引发错误。

[编辑1]这是我的模特:

public class SubDocument
{
    public SubDocument()
    {
        Id = Guid.NewGuid().ToString();
        CreatedAt = DateTime.UtcNow;
        ModifiedAt = DateTime.UtcNow;
        Pages = new List<Page>();
    }

    [BsonId]
    public string Id { get; set; }        

    [BsonElement("title")]
    public string Title { get; set; }        

    [BsonElement("createdAt")]
    public DateTime CreatedAt { get; set; }

    [BsonElement("modifiedAt")]
    public DateTime ModifiedAt { get; set; }

    [BsonElement("pages")]
    public List<Page> Pages { get; set; }


}

public class Page
{
    [BsonId]
    public string Id { get; set; }

    [BsonElement("title")]
    public string Title { get; set; }

    [BsonElement("slug")]
    public string Slug { get; set; }

    [BsonElement("createdAt")]
    public DateTime CreatedAt { get; set; }

    [BsonElement("modifiedAt")]
    public DateTime ModifiedAt { get; set; }

}

任何人?提前致谢!

答案

您的代码实际上适用于mongodb v3.4和驱动程序v2.5。

正如Evk在评论中所建议的,这里是代码的强类型版本,它是重构友好的。

var filter = Builders<SubDocument>.Filter
    .And(
        Builders<SubDocument>.Filter.Eq(d => d.Id, "69c4f77d-05ef-431d-ae17-c076c6173e04"), 
        Builders<SubDocument>.Filter.ElemMatch(x => x.Pages, p => p.Id == "3d1497d7-f74c-4d88-b15c-bf2f9c736374"));

var update = Builders<SubDocument>.Update
                .Set(c => c.Pages[-1].Title, "changed title")
                .Set(c => c.Pages[-1].Slug, "changed slug")
                .Set(c => c.Pages[-1].ModifiedAt, DateTime.UtcNow);

subDocumentsCollection.UpdateOne(filter, update);

一种不同的方法可能会将您的子文档实体视为一个整体(面向文档的数据库鼓励这种方法),因此总是完全更新实体并避免细粒度更新(在R-DBMS的情况下更可能)。在这种情况下,您可能会写这样的东西。

var document = subDocumentsCollection
    .Find(d => d.Id == "69c4f77d-05ef-431d-ae17-c076c6173e04")
    .Single();

var page = document.Pages.Single(p => p.Id == "3d1497d7-f74c-4d88-b15c-bf2f9c736374");

page.Title = "changed title";
page.Slug = "changed slug";
page.ModifiedAt = DateTime.UtcNow;

subDocumentsCollection.ReplaceOne(d => d.Id == document.Id, document);

第二种方法有以下PRO和CON。

PROs

  • 更新发生在域层(可能通过适当的方法),从而允许强制执行所有域规则并防止脏数据进入数据库;因此它更加面向对象。
  • 由于代码是面向域的,因此它更简单,并且不依赖于数据库内部。

CONs

  • 这种方法需要对数据库进行两次点击(一次用于读取,另一次用于更新);
  • 如果您的实体很大,网络必须传输的数据超过严格要求。

考虑到过早优化是编程中所有邪恶(或至少大部分)的根源(here),一般来说我会选择第二种方法,只有在性能要求非常严格或低的情况下才能回到第一种方法网络带宽。

以上是关于如何使用C#驱动程序更新MongoDB数组中的子文档的主要内容,如果未能解决你的问题,请参考以下文章

Mongodb C#驱动程序在数组为空时更新所有子数组元素失败

如何使用 Java 对 MongoDB 中的文档进行批量更新?

如何使用 MongoDB 在数组中的对象中更新数组中的对象中的文档?

如何使用mongodb中的对象更新数组?

MongoDB。如何更新数组内的json属性

如何根据另一个字段更新 mongodb 中的数组 dict 元素