18

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.

3 Answers 3

22

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);
Sign up to request clarification or add additional context in comments.

1 Comment

Real nice answer. Appreciate your effort. Thanks mate :)
11

Instead of update.Set("Courses.$.Level", "Updated Level"); you can also do it like this:

update.Set(x => x.Courses[-1].Level, "Updated Level");

Source: http://www.mattburkedev.com/updating-inside-a-nested-array-with-the-mongodb-positional-operator-in-c-number/

1 Comment

I know this is old and it helped me solve a similar problem. Taking it one step further, how would you update multiple Courses at the same time?
0

I also had the same question after too much investigation and came across a solution without being forced to use any magic string.

my object structure:

public class SettingDBEntity  
{
  [BsonRepresentation(BsonType.String)]
  public Guid Id { get; set; }

  public string Title { get; set; }

  public List<GoalDBEntity> Inputs { get; set; } = new();
}

public class InputDBEntity
{
  public byte Id { get; set; }

  public string? Title { get; set; }
  public string? Question { get; set; }

  public List<OutputDBEntity> Outputs { get; set; } = new();
 }


 public class OutputDBEntity
 {
  public byte Id { get; set; } 
  public string? Title { get; set; }
  public string? Note { get; set; } 
 }

Now to update just one specific output of a specific Input

var filter = Builders<SettingDBEntity>.Filter.And(
   Builders<SettingDBEntity>.Filter.Eq(r => r.Id, roadmapId), 
   Builders<SettingDBEntity>.Filter.ElemMatch(r => r.Inputs, g => g.Id == 
   inputId));


   var setter = Builders<SettingDBEntity>.Update.Set
         (s => s.Inputs[0].Outputs[outputParameter.Id], 
         CreateOutputObjcet(output));

   var updateResult = await Collection.UpdateOneAsync(filter, setter);

   if (updateResult.ModifiedCount != 1)
           throw new DatabaseException("");

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.