MongoDB - Cannot create field 'ChildProperty' in element {ParentProperty: is null}

Viewed 34

I have a User class:

public class User
{
    public Guid? Id { get; set; }
    public String? Name { get; set; }
    public Address? Address { get; set; }
}

and Address class:

public class Address
{
    public String? Street { get; set; }
    public String? City { get; set; }
    public String? State { get; set; }
}

I am trying to implement partial updating. This is done dynamically, but let's say someone sends to update a User's City and State, I do:

var filter = Builders<BsonDocument>.Filter.Eq("Id", id);
var updates = new List<UpdateDefinition<BsonDocument>>();
updates.Add(Builders<BsonDocument>.Update.Set("Address.City", "New City Value"));
updates.Add(Builders<BsonDocument>.Update.Set("Address.State", "New State Value"));
var update = Builders<BsonDocument>.Update.Combine(updates);
var bsonDocument = await collection.FindOneAndUpdateAsync(filter, update, new FindOneAndUpdateOptions<BsonDocument>
{
    ReturnDocument = ReturnDocument.After
});

This works well, except if a User's Address is null. In that case, I get the error:

MongoDB.Driver.MongoCommandException: Command findAndModify failed: Cannot create field 'City' in element {Address: null}.

Is there any way to ensure the Address object is created so that the City and State properties get set? I would like to do it without getting the current object from the database.

1 Answers

Think that you may work with update with aggregation pipeline to update the Address field dynamically.

Work with $cond operator to check whether Address is null.

If yes, set the whole object to Address.

If no, merge the current Address value with the document.

The query below may looks complex:

db.collection.update({
  Id: /* Id */
},
[
  {
    $set: {
      Address: {
        $cond: {
          if: {
            $eq: [
              "$Address",
              null
            ]
          },
          then: {
            "City": "New City Value",
            "State": "New State Value"
          },
          else: {
            $mergeObjects: [
              "$Address",
              {
                "City": "New City Value",
                "State": "New State Value"
              }
            ]
          }
        }
      }
    }
  }
])
var addressDocument = new BsonDocument
{
    { "City", "New City Value" },
    { "State", "New State Value" }
};

var update = Builders<BsonDocument>.Update.Pipeline(new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>
(
    new PipelineStageDefinition<BsonDocument, BsonDocument>[]
    {
        new BsonDocument("$set", 
            new BsonDocument("Address", 
                new BsonDocument("$cond", 
                    new BsonDocument
                    {
                        { 
                            "if", 
                            new BsonDocument("$eq", 
                                BsonArray.Create(new object[] { "$Address", null })) 
                        },
                        {
                            "then",
                            addressDocument
                        },
                        {
                            "else",
                            new BsonDocument("$mergeObjects", 
                                BsonArray.Create(new object[] { "$Address", addressDocument }))
                        }
                    }
                )
            )
        )
    }
));
Related