Search code examples
c#mongodbmongodb-.net-driver

Upsert Dictionary in MongoDb


To the best of my knowledge the mongodb knows Dictionary as an object, and it can't do any operation related to array. I changed the serialization and tried with various type of dictionary's serialization. but there is no chance.
so I load my field(dictionary)(whole of them) into memory, update it and set it back to mongodb.
Is there any way to upsert dictionary in mongodb with c# driver?


my document type:

public class Site
    {
        public string Id { get; set; }
        //[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)]
        public Dictionary<string,string> Properties { get; set; }
    }

my update operation:

public ServiceResult UpdateProperties(string id, Dictionary<string,string> properties)
        {
            var baseList = Collection.Find(m => m.Id == id)
                .Project(s => s.Properties)
                .FirstOrDefault();

            if (baseList == null)
            {
                baseList = properties;
            }
            else
            {
                baseList.Upsert(properties); //update,insert dic by new one
            }

            var filter = Builders<Site>.Filter
                .Eq(m => m.Id, id);

            var update = Builders<Site>.Update
                .Set(m => m.Properties, baseList);

            try
            {
                Collection.UpdateOne(filter, update);

                return ServiceResult.Okay(Messages.ItemUpdated);

            }
            catch (Exception ex)
            {
                return ServiceResult.Exception(ex);
            }    
        }

I really appreciate any help you can provide.


disambiguation :

public static class DictionaryExtensions
    {
        public static void Upsert<TKey, TValue>(this Dictionary<TKey, TValue> source, 
                                          Dictionary<TKey, TValue> newOne)
        {
            foreach (var item in newOne)
            {
                source[item.Key] = item.Value;
            }
        }
    }

Solution

  • You could go through all properties you want to update/insert and do it for each property:

    UpdateDefinition<Site> upsert = null;
    if (properties.Any())
    {
        var firstprop = properties.First();
        upsert = Builders<Site>.Update.Set(nameof(Site.Properties) + "." + firstprop.Key, 
                                   firstprop.Value);
    
        foreach (var updateVal in properties.Skip(1))
        {
            upsert = upsert.Set(nameof(Site.Properties) + "." + updateVal.Key, 
                                              updateVal.Value);
        }
    
        collection.UpdateOne(r => r.Id == "YourId", upsert, 
                                                   new UpdateOptions { IsUpsert = true });
    }
    

    Previous version of the answer, with multiple updates:

    foreach (var updateVal in properties)
    {
        collection.UpdateOne(r => r.Id == "YourId", 
            Builders<Site>.Update.Set( nameof(Site.Properties)+ "." + updateVal.Key, 
                                       updateVal.Value), 
                                       new UpdateOptions { IsUpsert = true});
    }
    

    Note, that will just add new Key/Values or update existing, this won't remove anything.