Carl Thomas
Carl Thomas

Reputation: 3823

MongoDB C# Update only fields that are not null

I have a document e.g.

{
    "_id": "ABC",
    "name": "Bob",
    "age": 42
}

I have 2 functions that run separate at separate times. Function A updates the name and function B updates the age. Both of these functions call an external rest service which brings back name or age depending on the service.

I have a class that maps to the mongo document

public class Person {
    [BsonId]
    public string Id { get; set; }

    [BsonElement("name")]
    public string Name { get; set; }

    [BsonElement("age")]
    public int Age { get; set; }
}

Function A will populate only the Id and the Name properties of this class whilst function B will populate the Id and Age properties.

How can I update the mongo document by just passing the Person object from function B so that it keeps the existing value of function A?

I know you can do Builders<Person>.Update.Set(x => x.Age, <new_age>) but I want my code to be a bit more dynamic so that if I add another field called 'Phone' I can just call a generic method that will update only the fields that have a value and are not null.

Upvotes: 3

Views: 3251

Answers (1)

Victor SDK
Victor SDK

Reputation: 473

Only add the elements you want to update to a new BsonDocument, in order to create your update definition.

private static void SetUpdatableElements(BsonDocument bdoc, BsonDocument updateDefintion, HashSet<string>() excluded = null, string parentName = "")
{
    var excluded = excluded ?? new HashSet<string>()
    parentName = !string.IsNullOrWhiteSpace(parentName) ? $"{parentName}." : "";
    foreach (var item in bdoc)
    {
        if (item.Value.IsObjectId || // skip _id                     
            item.Value.IsBsonNull || // skip properties with null values
            excluded.Contains(item.Name)) // skip other properties that should not be updated
        {
            continue;
        }
        if (!item.Value.IsBsonDocument) // to avoid override nested objects)
        {
            updateDefintion = updateDefintion.Add($"{parentName}{item.Name}", item.Value);
            continue;
        }
        // recursively set nested elements to avoid overriding the full object
        SetUpdatableElements(item.Value.ToBsonDocument(), updateDefintion, item.Name);
    }
}

The caller:

var person = new Person() { Id = "ABC", Name = "bob", Age:42, Phone="123 456 7890"}
var updateDefintion = new BsonDocument();
var bDoc = person.ToBsonDocument();
SetUpdatableElements(person.ToBsonDocument(), updateDefintion, new HashSet<string>(nameof(person.Name)))
collection.UpdateOneAsync<Person>(p => p.Id == person.Id, new BsonDocument("$set", updateDefintion));

At the end, your updateDefinition should be something like:

{ {
    "$set": {
        "age": 42
        "phone": "123 456 7890"
    }
} }

Upvotes: 2

Related Questions