Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating an embedded document in MongoDB with official C# driver

If I have a Company collection which contains embedded Divisions:

{ 
  "_id": 1 
  "_t": "Company", 
  "Name": "Test Company" 
  "Divisions": [ 
    { 
       "_id": 1 
       "_t": "Division", 
       "Name": "Test Division 1" 
    }, 
    { 
       "_id": 2 
       "_t": "Division", 
       "Name": "Test Division 2" 
    } 
  ] 
} 

What is the best way to save/update an entire Division when using the official 10gen C# driver? (The latest 0.9 release.)

I'm using Update.AddToSetWrapped to add Divisions, and that works fine, but I'd also like to be able to update documents depending on their _id.

For example, if I define the following Update method:

public void UpdateDivision(IDivision division) 
{ 
  var mongo = MongoServer.Create(_connectionString); 
  var database = mongo.GetDatabase(_databaseName); 
  var query = Query.EQ("_id", division.CompanyId); 
  var update = Update.AddToSetWrapped("Divisions", division); 
  database.GetCollection<Company>("Company") 
          .Update(query, update, UpdateFlags.Upsert, SafeMode.True); 
} 

and call it like so:

var division = GetDivisionById(1); 
division.Name = "New Name"; 
UpdateDivision(division);

Then a new instance of Division will be added to the set, because although the "_id" is still 1, the Name is different, and therefore it is a unique document.

So what is there a good way to update a whole embedded document?

Until I come up with a better solution I am going to first $pull the original Division and then $addToSet with the modified Division. That works but is obviously not ideal as it performs two separate updates.

like image 427
Martin Owen Avatar asked Dec 07 '10 16:12

Martin Owen


1 Answers

You could use the positional array modification feature of MongoDB to update an entire division in the array at once as follows:

var division = GetDivisionById(1);
division.Name = "New Name";
// change any other properties of division you want
collection.Update(
    Query.EQ("Divisions._id", 1),
    Update.Set("Divisions.$", BsonDocumentWrapper.Create<IDivision>(division))
);

The key things going on here are:

  1. The use of the "$" in Update.Set
  2. Since Update.Set requires a BsonValue as its second argument we have to use a BsonDocumentWrapper to hold the division value (the IDivision type parameter to Create sets the nominalType at serialization to IDivision which results in the "_t" discriminator being written).
like image 58
Robert Stam Avatar answered Oct 31 '22 21:10

Robert Stam