'MongoDb C# driver - upsert many based on "candidate" key

I have mongodb collection of the following type:

public class Entity
{
    [BsonRepresentation(BsonType.ObjectId)]
    public string Id { get; set; }
    public int Field1{ get; set; }
    public int Field2{ get; set; }
}

On the client I have an array of such entities with Field1 and Field2 set and without Id.

I want to upsert them in one batch based on Field1 and Field2 combination (think unique "candidate" key) - if such combination exists - do nothing/update existing (it is the same), if it does not exist - insert new entity.

It can be easily done with multiple UpdateAsync calls:

UpdateOptions() { IsUpsert = true }    
var filter = new FilterDefinitionBuilder<Enity>().Where(p => p.Field1 == entiy.Field1 && p.Field2 == entity.Field2);
await UpsertAsync(entity, filter);

but that is not many/bulk operation.

I'm looking through API/documentation:

  • With BulkWriteAsync - see the answer
  • With InsertManyAsync I cannot see how can I specify upsert
  • With UpdateManyAsync its cryptic how can I send field value combinations to insert/update


Solution 1:[1]

This can be done as follows:

        List<UpdateOneModel<Entity>> requests = new List<UpdateOneModel<Entity>>(entities.Count());
        foreach (var entity in entities)
        {
            var filter = new FilterDefinitionBuilder<Entity>().Where(m => m.Field1 == entity.Field1 && m.Field2== entity.Field2);
            var update = new UpdateDefinitionBuilder<Entity>().Set(m => m.Field1, entity.Field1).Set(m => m.Field2, entity.Field2);
            var request = new UpdateOneModel<Entity>(filter, update);
            request.IsUpsert = true;
            requests.Add(request);
        }
        await Collection.BulkWriteAsync(requests);

Solution 2:[2]

Encountered it today and thought I'd share a little shortcut to the accepted answer (thanks Conrtlendt) I came up with in case anyone is interested:

await _mongoCollection.BulkWriteAsync(
                items.Select(i=>
                    new UpdateOneModel<T>(
                        filter: Builders<T>.Filter.Where(m=>m.Field1 == i.Field1 /*add more conditions*/),
                        update: Builders<T>.Update.Set(m=>m.Field2, i.Field2)/*add more .Set(...)*/)
            {
                IsUpsert = true
            }));

it will also work with ReplaceOneModel (if you want to set all fields at one time without specifying set.set.set... :)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Cortlendt
Solution 2