Search code examples
mongodbmongodb-querymongodb-.net-driver

How do I replace an entire array of subdocuments in MongoDB?


Here is an example document from my collection:

Books
[
   id: 1,
   links:
   [
     {text: "ABC", "url": "www.abc.com"},
     {text: "XYZ", "url": "www.xyz.com"}
   ]
]

I want to replace the links array in one update operation. Here is an example of how the above document should be modified:

Books
[
   id: 1,
   links:
   [
     {text: "XYZ", "url": "www.xyz.com"},
     {text: "efg", "url": "www.efg.com"},   <== NEW COPY OF THE ARRAY
     {text: "ijk", "url": "www.ijk.com"}
   ]
]

As you can see, the links array has been replaced (old data removed, and new data added).

I am having very hard time with the Update.Set() because it says it MyLinks<> cannot be mapped to a BsonValue

I've tried many different ways of achieving this, and all of them fail, including .PushAllWrapped<WebLinkRoot>("links", myDoc.WebLinks). Everything I've tried results in the new values being appended to the array, rather than the array being replaced.

As it seems MongoDB doesn't provide a simple method to replace an array of subdocument OR a method like .ClearArray(), what is the best way for me to ensure the array is cleared before adding new elements in a single query?


Solution

  • I think you have to do something like this:

    var newArray = new BSONArray {
        new BSONDocument { { "text", "XYZ" }, { "url", "www.xyz.com" } },
        new BSONDocument { { "text", "efg" }, { "url", "www.efg.com" } },
        new BSONDocument { { "text", "ijk" }, { "url", "www.ijk.com" } }
    };
    
    var update = Update.Set( "links", newArray );
    
    collection.Update( query, update );
    

    Or whatever method you can to cast as a valid BSONValue.

    So equivalent to shell:

    { "links" : [ { "text" : "abc" } ] }
    
    > db.collection.update(
          {},
          { $set: 
              { links: [ 
                  { text: "xyz", url: "something" },
                  { text: "zzz", url: "else" }
              ]}
          })
    
    >db.collection.find({},{ _id: 0, links:1 }).pretty()
    
    { "links" : [
                {
                        "text" : "xyz",
                        "url" : "something"
                },
                {
                        "text" : "zzz",
                        "url" : "else"
                }
        ]
    }
    

    So that works.

    You clearly need something else other than embedded code. But hopefully that puts you on the right track.