30

I'm using MongoDB 3.2 and MongoDB Java Driver 3.2. I have an array of a couple of hundreds of updated documents which should be now saved/stored in MongoDB. In order to do that, I iterate over the array and call for each document in this array the updateOne() method.

Now, I want to re-implement this logic with a bulk update. I tried to find an example of bulk update in MongoDB 3.2 with MongoDB Java Driver 3.2.

I tried this code:

MongoClient mongo = new MongoClient("localhost", 27017);

DB db = (DB) mongo.getDB("test1");
DBCollection collection = db.getCollection("collection");
BulkWriteOperation builder = collection.initializeUnorderedBulkOperation();

builder.find(new BasicDBObject("_id", 1001)).upsert()
    .replaceOne(new BasicDBObject("_id", 1001).append("author", "newName"));

builder.execute();

But it seems that this approach is based on an outdated MongoDB Java Driver, such as 2.4 and uses deprecated methods.

How to perform a bulk update of documents in MongoDB 3.2 with MongoDB Java Driver 3.2?

Mike
  • 14,010
  • 29
  • 101
  • 161

1 Answers1

42

Using the example in the manual on the new bulkWrite() API, consider the following test collection which contains the following documents:

{ "_id" : 1, "char" : "Brisbane", "class" : "monk", "lvl" : 4 },
{ "_id" : 2, "char" : "Eldon", "class" : "alchemist", "lvl" : 3 },
{ "_id" : 3, "char" : "Meldane", "class" : "ranger", "lvl" : 3 }

The following bulkWrite() performs multiple operations on the characters collection:


Mongo shell:

try {
    db.characters.bulkWrite([
        { 
            insertOne:{
                "document":{
                    "_id" : 4, "char" : "Dithras", "class" : "barbarian", "lvl" : 4
                }
            }
        },
        { 
            insertOne:{
                "document": {
                    "_id" : 5, "char" : "Taeln", "class" : "fighter", "lvl" : 3
                }
            }
        },
        { 
            updateOne: {
                "filter" : { "char" : "Eldon" },
                "update" : { $set : { "status" : "Critical Injury" } }
            }
        },
        { 
            deleteOne: { "filter" : { "char" : "Brisbane"} }
        },
        { 
            replaceOne: {
               "filter" : { "char" : "Meldane" },
               "replacement" : { "char" : "Tanys", "class" : "oracle", "lvl" : 4 }
            }
        }
    ]);
}
catch (e) {  print(e); }

which prints the output:

{
   "acknowledged" : true,
   "deletedCount" : 1,
   "insertedCount" : 2,
   "matchedCount" : 2,
   "upsertedCount" : 0,
   "insertedIds" : {
      "0" : 4,
      "1" : 5
   },
   "upsertedIds" : {

   }
}

The equivalent Java 3.2 implementation follows:

MongoCollection<Document> collection = db.getCollection("characters");
List<WriteModel<Document>> writes = new ArrayList<WriteModel<Document>>();
writes.add(
    new InsertOneModel<Document>(
        new Document("_id", 4)
            .append("char", "Dithras")
            .append("class", "barbarian")
            .append("lvl", 3)
    )
);
writes.add(
    new InsertOneModel<Document>(
        new Document("_id", 5)
            .append("char", "Taeln")
            .append("class", "fighter")
            .append("lvl", 4)
    )
);
writes.add(
    new UpdateOneModel<Document>(
        new Document("char", "Eldon"), // filter
        new Document("$set", new Document("status", "Critical Injury")) // update
    )
);
writes.add(new DeleteOneModel<Document>(new Document("char", "Brisbane")));
writes.add(
    new ReplaceOneModel<Document>(
        new Document("char", "Meldane"), 
        new Document("char", "Tanys")
            .append("class", "oracle")
            .append("lvl", 4)           
    )
);

BulkWriteResult bulkWriteResult = collection.bulkWrite(writes);

For your question use the replaceOne() method and this would be implemented as

MongoCollection<Document> collection = db.getCollection("collection");
List<WriteModel<Document>> writes = Arrays.<WriteModel<Document>>asList(
    new ReplaceOneModel<Document>(
        new Document("_id", 1001), // filter
        new Document("author", "newName"), // update
        new UpdateOptions().upsert(true) // options
    )   
);

BulkWriteResult bulkWriteResult = collection.bulkWrite(writes);
chridam
  • 100,957
  • 23
  • 236
  • 235
  • 1
    Not that I'm aware of, but the [MongoDB Limits and Thresholds](https://docs.mongodb.org/manual/reference/limits/) still apply. – chridam Mar 07 '16 at 15:41
  • 1
    Great & thanks. Two questions. 1. Is there any rule when I have to move from single update to bulk update in `MongoDB`? I mean from amount of documents point of rule. 2. Regarding to `replaceOne()`, all my updated documents contain an update of one-two fields. Why should I use this method and not `updateOne()`? As far as I understand, `replaceOne()` replaces the whole document, while `updateOne()` may just update the value of the specific fields, which should be faster, am I right? – Mike Mar 07 '16 at 15:45
  • 1
    @MikeB. I think this warrants a new question as that is different from what you originally asked. If you post another question I will be as others happy to answer it. The general case here is one question one answer. I can answer your additional question but It should be another question entirely. Please post it separately. – chridam Mar 07 '16 at 16:01
  • 1
    @MikeB. The general answer is `replaceOne` replaces a single document in the collection that matches the filter. If multiple documents match, `replaceOne` will replace the first matching document only. `updateOne()` updates a single document within the collection based on the filter. If `{upsert: true}` option is specified and no documents match the filter, a new document is created using the equality comparisons in filter with the modifications from update. If the filter only has comparison operations, then only the modifications from the update will be applied to the new document. – chridam Mar 07 '16 at 16:09
  • 1
    I posted a separate question: http://stackoverflow.com/questions/35848688/whats-the-difference-between-replaceone-and-updateone-in-mongodb – Mike Mar 07 '16 at 16:19
  • By default if a write fails during the bulk operation then no more data will be written. If you want MongoDB to attempt the remaining writes then use this: `collection.bulkWrite(write, new BulkWriteOptions().ordered(false));` – Paul Aug 30 '16 at 16:44
  • 1
    Wow. Forgetting to add $set to an updateOne operation in a bulk write overwrites the document like a replaceOne instead of updating it or throwing an error like a normal updateOne operation would. Wipes it clean. Thank goodness for backups. – Roman Scher Jun 10 '20 at 23:27
  • Is there a way to check the progress of the bulk operation? – liga Jan 25 '21 at 18:39