Search code examples
c#mongodbmongodb-.net-driver

Update property in nested array of entities in MongoDB


Is there a straight forward way to update nested array of entities in MongoDB. I am using MongoDB C# Driver for making the DB call from application. Below is an exmaple : say I have a Student collection where each document has a nested array of Course with some necessary fields populated and Course by itself is a separate collection like:

{
 "_id": "234dssfcv456",
 "Name": "Jean Douglas",
 "Age": 32,
 "Courses": 
  [
    {
       "_id": "1234",
       "Name": "Computer Science",
       "Level": "Basic" 
    },
    {
       "_id": "3456",
       "Name": "Bio Science",
       "Level": "Intermediate" 
    }
  ] 
}

I know I can update the nested entity by index something like below but I don't know the index and rather know only the nested Course object Id only.

db.College.Student.update(
    {"Student._id": "234dssfcv456"}, 
    {$set: {
        "Student.$.Courses.1.Level": "Basic"
    }}

Right now, am reading the entire nested array of courses -> doing the modification at application end -> then passing the entire array for update with the filedname "Courses" which is going to replace the existing array with the one passed.

But was thinking, is there an way I can update one entity in array with the Id available. Please suggest.

*** On the right side in Related question section all shows updating the nested array of objects using the index of the object item which is not a possibility for me.


Solution

  • Mongo shell:

    > db.students.find( {_id:"234dssfcv456", "Courses._id":"1234"} ).pretty()
    > db.students.update( {_id:"234dssfcv456", "Courses._id":"3456"}, { $set: { "Courses.$.Level" : "Updated" } } )
    

    C# Mongo schema:

    public class Student {
      [BsonId]
      [BsonRepresentation(BsonType.String)]
      public string Id { get; set; }
      public string Name { get; set; }
      public int Age { get; set; }
      public Course[] Courses { get; set; }
    }
    
    public class Course {
      [BsonId]
      [BsonRepresentation(BsonType.String)]
      public string Id { get; set; }
      public string Name { get; set; }
      public string Level { get; set; }
    }
    

    Lookup Mongo docs for the positional operator. With driver higher than version 2.2.3.3 I am using:

      var _client = new MongoClient(@"....");
      var _database = _client.GetDatabase("...");
      var _students =  _database.GetCollection<Student>("students");
    
      var filter = Builders<Student>.Filter;
      var studentIdAndCourseIdFilter = filter.And(
        filter.Eq(x => x.Id, "234dssfcv456"),
        filter.ElemMatch(x => x.Courses, c => c.Id == "1234") );
       // find student with id and course id
       var student = _students.Find(studentIdAndCourseIdFilter).SingleOrDefault();
    
      // update with positional operator
      var update = Builders<Student>.Update;      
      var courseLevelSetter = update.Set("Courses.$.Level", "Updated Level");
      _students.UpdateOne(studentIdAndCourseIdFilter, courseLevelSetter);