Home > Enterprise >  How to avoid duplicate insertion in MongoDB using c# without using "_id" key?
How to avoid duplicate insertion in MongoDB using c# without using "_id" key?

Time:06-15

I have a data like say of 5 fields in a document and suppose these 3 fields these 3 fields uniquely help identify a document.

Name:A, Type: B, Contact: C

I want to avoid duplicate entry(Insertion) in the document for these fields. I am using below code for now

_mongoCollection.GetCollection<ModelClass>('collectionName').Find(s => s.Name==A && s.Type==B && s.Contact==C).FirstORDefault();

And if this comes out to be null; we insert using :

_mongoCollection.getCollection<BsonDocument>('collectionName');

and then insert Using InsertOne. Is there a better way to implement this and avoid duplicacy?

CodePudding user response:

You could use an Upsert for this:

var coll = _mongoCollection.getCollection<ModelClass>("collectionName");
var model = new ModelClass() 
{
  Name = A, 
  Type = B, 
  Contact = C, 
  Field4 = "F4", 
  Field5 = "F5",
};
await coll.ReplaceOneAsync(s => s.Name == model.Name 
  && s.Type == model.Type 
  && s.Contact == model.Contact, 
  model, 
  new ReplaceOptions() { IsUpsert = true });

The above code checks if a matching document exists; if it does, it is replaced, otherwise a new document is inserted. You could also use ReplaceOne if your method is not async or UpdateOne(Async) for a more fine-grained update of an existing document.

Upsert with update

For instance, if you want to only insert a new document if there is no document yet, you can use the following upsert that does not replace the document:

var coll = _mongoCollection.getCollection<ModelClass>("collectionName");
var model = new ModelClass() 
{
  Name = A, 
  Type = B, 
  Contact = C, 
  Field4 = "F4", 
  Field5 = "F5",
};
var update = Builders<ModelClass>.Update
  .SetOnInsert(x => x.Field4, model.Field4)
  .SetOnInsert(x => x.Field5, model.Field5);
var result = await coll.UpdateOneAsync(
  x => x.Name == model.Name && x.Type == model.Type && x.Contact == model.Contact, 
  update, 
  new UpdateOptions() { IsUpsert = true });

The above code creates a new document if none exists yet. If a document is inserted, you can check result.UpsertedId. If this is null, no document has been inserted, otherwise it contains the id of the inserted document.

The update uses only SetOnInsert operations for the two remaining fields. If the document already exists, it remains unchanged.

Bulk upsert with update

If you want to perform multiple upserts with a bulk operation, you can use the following code:

var update = Builders<ModelClass>.Update
    .SetOnInsert(x => x.Field4, model.Field4)
    .SetOnInsert(x => x.Field5, model.Field5);
var bldr = Builders<ModelClass>.Filter;
var updates = new List<WriteModel<ModelClass>>();
var updateOneModel = new UpdateOneModel<ModelClass>(
    bldr.Eq(x => x.Name, model.Name) & bldr.Eq(x => x.Type, model.Type) & bldr.Eq(x => x.Contact, model.Contact), 
    update);
updateOneModel.IsUpsert = true;
updates.Add(updateOneModel);
// ... add additional UpdateModels
var result = await coll.BulkWriteAsync(updates);

In this case, result contains a list of upserted ids; there also is an Index property that you can use to find out which UpdateModels have led to an upsert and which haven't.

  • Related