Overview
In this guide, you can learn how to perform multiple write operations in a single database call by using bulk write operations.
Consider a scenario in which you want to insert a document, update multiple other documents, then delete a document. If you use individual methods, each operation requires its own database call.
By using a bulk write operation, you can perform multiple write operations in fewer database calls. You can perform bulk write operations at the following levels:
Collection: You can use the
MongoCollection.bulkWrite()method to perform bulk write operations on a single collection. In this method, each kind of write operation requires at least one database call. For example,MongoCollection.bulkWrite()puts multiple update operations in one call, but makes two separate calls to the database for an insert operation and a replace operation.Client: If your application connects to MongoDB Server version 8.0 or later, you can use the
MongoClient.bulkWrite()method to perform bulk write operations on multiple collections and databases in the same cluster. This method performs all write operations in one database call.
Sample Data
The examples in this guide use the sample_restaurants.restaurants and sample_mflix.movies collections from the Atlas sample
datasets. To learn how to create a free MongoDB Atlas cluster and load the sample datasets, see Get Started with Atlas.
The documents in these collections are modeled by the following Kotlin data classes:
data class Restaurant( val name: String, val borough: String, val cuisine: String, val stars: Int? = null, ) data class Movie( val title: String, val year: Int, val seen: Boolean? = null, )
Collection Bulk Write
Bulk write operations contain one or more write operations. To perform a bulk write operation at the collection level, pass a List of WriteModel documents to the MongoCollection.bulkWrite() method. A WriteModel is a model that represents a write operation.
For each write operation you want to perform, create an instance of one of the following classes that inherit from WriteModel:
InsertOneModelUpdateOneModelUpdateManyModelReplaceOneModelDeleteOneModelDeleteManyModel
The following sections show how to create and use instances of the preceding classes. The Perform the Bulk Operation section demonstrates how to pass a list of models to the bulkWrite() method to perform the bulk operation.
Insert Operations
To perform an insert operation, create an InsertOneModel instance and specify the document you want to insert.
The following example creates an instance of InsertOneModel:
val blueMoon = InsertOneModel(Restaurant("Blue Moon Grill", "Brooklyn", "American"))
To insert multiple documents, create an instance of InsertOneModel for each document.
Important
When performing a bulk operation, the InsertOneModel cannot instruct insertion of a document that has an _id value that already exists in the collection. In this situation, the driver throws a MongoBulkWriteException.
Update Operations
To update a document, create an instance of UpdateOneModel and pass the following arguments:
A query filter that specifies the criteria used to match documents in your collection
The update operation you want to perform. For more information about update operators, see the Field Update Operators guide in the MongoDB Server manual.
An UpdateOneModel instance specifies an update for the first document that matches your query filter.
The following example creates an instance of UpdateOneModel:
val updateOneFilter = Filters.eq(Restaurant::name.name, "White Horse Tavern") val updateOneDoc = Updates.set(Restaurant::borough.name, "Queens") val tavernUpdate = UpdateOneModel<Restaurant>(updateOneFilter, updateOneDoc)
If multiple documents match the query filter specified in the UpdateOneModel instance, the operation updates the first result. You can specify a sort in an UpdateOptions instance to apply an order to matched documents before the server performs the update operation, as shown in the following code:
val opts = UpdateOptions().sort(Sorts.ascending(Restaurant::name.name))
To update multiple documents, create an instance of UpdateManyModel and pass the same arguments as for UpdateOneModel. The UpdateManyModel class specifies updates for all documents that match your query filter.
The following example creates an instance of UpdateManyModel to direct the driver to update all matching documents:
val updateManyFilter = Filters.eq(Restaurant::name.name, "Wendy's") val updateManyDoc = Updates.set(Restaurant::cuisine.name, "Fast food") val wendysUpdate = UpdateManyModel<Restaurant>(updateManyFilter, updateManyDoc)
Replace Operations
A replace operation removes all fields and values of a specified document and replaces them with new fields and values that you specify. To perform a replace operation, create an instance of ReplaceOneModel and pass a query filter and the fields and values you want to replace the matching document with.
The following example creates an instance of ReplaceOneModel:
val replaceFilter = Filters.eq(Restaurant::name.name, "Cooper Town Diner") val replaceDoc = Restaurant("Smith Town Diner", "Brooklyn", "American") val replacement = ReplaceOneModel(replaceFilter, replaceDoc)
If multiple documents match the query filter specified in the ReplaceOneModel instance, the operation replaces the first result. You can specify a sort in a ReplaceOptions instance to apply an order to matched documents before the server performs the replace operation, as shown in the following code:
val opts = ReplaceOptions().sort(Sorts.ascending(Restaurant::name.name))
Tip
Replace Multiple Documents
To replace multiple documents, create an instance of ReplaceOneModel for each document.
Delete Operations
To delete a document, create an instance of DeleteOneModel and pass a query filter specifying the document you want to delete. A DeleteOneModel instance provides instructions to delete only the first document that matches your query filter.
The following example creates an instance of DeleteOneModel:
val deleteOne = DeleteOneModel<Restaurant>(Filters.eq( Restaurant::name.name, "Morris Park Bake Shop" ))
To delete multiple documents, create an instance of DeleteManyModel and pass a query filter specifying the document you want to delete. An instance of DeleteManyModel provides instructions to remove all documents that match your query filter.
The following example creates an instance of DeleteManyModel:
val deleteMany = DeleteManyModel<Restaurant>(Filters.eq( Restaurant::cuisine.name, "Experimental" ))
Perform the Bulk Operation
After you define a model instance for each operation you want to perform, pass a list of these instances to the bulkWrite() method. By default, the method runs the operations in the order specified by the list of models.
The following example performs multiple write operations by using the bulkWrite() method:
val insertOneMdl = InsertOneModel(Restaurant("Red's Pizza", "Brooklyn", "Pizzeria")) val updateOneMdl = UpdateOneModel<Restaurant>( Filters.eq(Restaurant::name.name, "Moonlit Tavern"), Updates.set(Restaurant::borough.name, "Queens") ) val deleteManyMdl = DeleteManyModel<Restaurant>( Filters.eq(Restaurant::name.name, "Crepe") ) val bulkResult = collection.bulkWrite( listOf(insertOneMdl, updateOneMdl, deleteManyMdl) ) println(bulkResult)
AcknowledgedBulkWriteResult{insertedCount=1, matchedCount=5, removedCount=3, modifiedCount=2, upserts=[], inserts=[BulkWriteInsert{index=0, id=BsonObjectId{value=...}}]}
If any of the write operations fail, the Kotlin Sync driver raises a BulkWriteError and does not perform any further operations. BulkWriteError provides a details field that includes the operation that failed, and details about the exception.
Note
When the driver runs a bulk operation, it uses the write concern of the target collection. The driver reports all write concern errors after attempting all operations, regardless of execution order.
Customize Bulk Write Operation
The bulkWrite() method optionally accepts a parameter which specifies options you can use to configure the bulk write operation. If you don't specify any options, the driver performs the bulk operation with default settings.
The following table describes the setter methods that you can use to configure a BulkWriteOptions instance:
Property | Description |
|---|---|
| If |
| Specifies whether the update operation bypasses document validation. This lets you
update documents that don't meet the schema validation requirements, if any
exist. For more information about schema validation, see Schema
Validation in the MongoDB
Server manual. |
| Sets a comment to attach to the operation. |
| Provides a map of parameter names and values to set top-level variables for the operation. Values must be constant or closed expressions that don't reference document fields. |
The following code creates options and uses the ordered(false) option to specify an unordered bulk write. Then, the example uses the bulkWrite() method to perform a bulk operation:
val opts = BulkWriteOptions().ordered(false) collection.bulkWrite(bulkOperations, opts)
If any of the write operations in an unordered bulk write fail, the Kotlin Sync driver reports the errors only after attempting all operations.
Note
Unordered bulk operations do not guarantee an order of execution. The order can differ from the way you list them to optimize the runtime.
Return Value
The bulkWrite() method returns a BulkWriteResult object. You can access the following information from a BulkWriteResult instance:
Property | Description |
|---|---|
| Indicates if the server acknowledged the write operation. |
| The number of documents deleted, if any. |
| The number of documents inserted, if any. |
| The list of inserted documents, if any. |
| The number of documents matched for an update, if applicable. |
| The number of documents modified, if any. |
| The list of upserted documents, if any. |
Client Bulk Write
When connecting to a deployment running MongoDB Server 8.0 or later, you can use the MongoClient.bulkWrite() method to write to multiple databases and collections in the same cluster. The MongoClient.bulkWrite() method performs all write operations in a single call.
The MongoClient.bulkWrite() method takes a list of ClientNamespacedWriteModel instances to represent different write operations. You can construct instances of the ClientNamespacedWriteModel interface by using instance methods. For example, an instance of ClientNamespacedInsertOneModel represents an operation to insert one document, and you can create this model by using the ClientNamespacedWriteModel.insertOne() method.
The models and their corresponding instance methods are described in the table below.
Model | Instance Method | Description | Parameters |
|---|---|---|---|
|
| Creates a model to insert a document into the |
|
|
| Creates a model to update the first document in the |
You must pass a value for either the |
|
| Creates a model to update all documents in the |
You must pass a value for either the |
|
| Creates a model to replace the first document in the |
|
|
| Creates a model to delete the first document in the |
|
|
| Creates a model to delete all documents in the |
|
The following sections provide some examples of how to create models and use the client bulkWrite() method.
Insert Operations
This example shows how to create models that contain instructions to insert two documents. One document is inserted into the sample_restaurants.restaurants collection, and the other document is inserted into the sample_mflix.movies collection. The MongoNamespace instance defines the target database and collection that each write operation applies to.
val restaurantToInsert = ClientNamespacedWriteModel .insertOne( MongoNamespace("sample_restaurants", "restaurants"), Restaurant("Blue Moon Grill", "Brooklyn", "American") ) val movieToInsert = ClientNamespacedWriteModel .insertOne( MongoNamespace("sample_mflix", "movies"), Movie("Silly Days", 2022) )
Update Operation
The following example shows how to use the bulkWrite() method to update existing documents in the sample_restaurants.restaurants and sample_mflix.movies collections:
val restaurantUpdate = ClientNamespacedWriteModel .updateOne( MongoNamespace("sample_restaurants", "restaurants"), Filters.eq(Restaurant::name.name, "Villa Berulia"), Updates.inc(Restaurant::stars.name, 1) ) val movieUpdate = ClientNamespacedWriteModel .updateMany( MongoNamespace("sample_mflix", "movies"), Filters.eq(Movie::title.name, "Carrie"), Updates.set(Movie::seen.name, true) )
This example increments the value of the stars field by 1 in the document that has a name value of "Villa Berulia" in the restaurants collection. It also sets the value of the seen field to true in all documents that have a title value of "Carrie" in the movies collection.
If multiple documents match the query filter specified in a ClientNamespacedUpdateOneModel instance, the operation updates the first result. You can specify a sort order in a ClientUpdateOneOptions instance to apply an order to matched documents before the server performs the update operation, as shown in the following code:
val options = ClientUpdateOneOptions .clientUpdateOneOptions() .sort(Sorts.ascending("_id"))
Replace Operations
The following example shows how to create models to replace existing documents in the sample_restaurants.restaurants and sample_mflix.movies collections:
val restaurantReplacement = ClientNamespacedWriteModel .replaceOne( MongoNamespace("sample_restaurants", "restaurants"), Filters.eq("_id", 1), Restaurant("Smith Town Diner", "Brooklyn", "American") ) val movieReplacement = ClientNamespacedWriteModel .replaceOne( MongoNamespace("sample_mflix", "movies"), Filters.eq("_id", 1), Movie("Loving Sylvie", 1999) )
After this example runs successfully, the document that has an _id value of 1 in the restaurants collection is replaced with a new document. The document in the movies collection that has an _id value of 1 is also replaced with a new document.
If multiple documents match the query filter specified in a ClientNamespacedReplaceOneModel instance, the operation replaces the first result. You can specify a sort order in a ClientReplaceOneOptions instance to apply an order to matched documents before the driver performs the replace operation, as shown in the following code:
val options = ClientReplaceOneOptions .clientReplaceOneOptions() .sort(Sorts.ascending("_id"))
Perform the Bulk Operation
After you define a ClientNamespacedWriteModel instance for each operation you want to perform, pass a list of these instances to the client bulkWrite() method. By default, the method runs the operations in the order they're specified.
The following example performs multiple write operations by using the bulkWrite() method:
val restaurantNamespace = MongoNamespace("sample_restaurants", "restaurants") val movieNamespace = MongoNamespace("sample_mflix", "movies") val bulkOperations = listOf( ClientNamespacedWriteModel .insertOne( restaurantNamespace, Restaurant("Drea's", "Brooklyn", "Mexican") ), ClientNamespacedWriteModel .replaceOne( movieNamespace, Filters.eq("_id", 1), Movie("Underneath It All", 2002) ) ) val clientBulkResult = mongoClient .bulkWrite(bulkOperations) println(clientBulkResult.toString())
AcknowledgedSummaryClientBulkWriteResult{insertedCount=1, matchedCount=1, ...}
If any of the write operations fail, the driver raises a ClientBulkWriteException and does not perform any further individual operations. ClientBulkWriteException includes a BulkWriteError that can be accessed by using the ClientBulkWriteException.getWriteErrors() method, which provides details of the individual failure.
Customize Bulk Write
You can pass an instance of ClientBulkWriteOptions to the bulkWrite() method to customize how the driver performs the bulk write operation.
Order of Execution
By default, the driver runs the individual operations in a bulk operation in the order that you specify them until an error occurs, or until the operation completes successfully.
However, you can pass false to the ordered() method when creating a ClientBulkWriteOptions instance to direct the driver to perform write operations in an unordered way. When using the unordered option, an error-producing operation does not prevent the driver from running other write operations in the bulk write operation.
The following code sets the ordered option to false in an instance of ClientBulkWriteOptions and performs a bulk write operation to insert multiple documents.
val namespace = MongoNamespace("sample_restaurants", "restaurants") val options = ClientBulkWriteOptions .clientBulkWriteOptions() .ordered(false) val bulkOps = listOf( ClientNamespacedWriteModel.insertOne( namespace, Document("_id", 1).append("name", "Freezyland") ), // Causes a duplicate key error ClientNamespacedWriteModel.insertOne( namespace, Document("_id", 1).append("name", "Coffee Stand No. 1") ), ClientNamespacedWriteModel.insertOne<Any>( namespace, Document("name", "Kelly's Krepes") ) ) val result = mongoClient .bulkWrite(bulkOps, options)
Even though the write operation inserting a document with a duplicate key results in an error, the other operations are performed because the write operation is unordered.
Additional Information
To learn how to perform individual write operations, see the following guides:
API Documentation
To learn more about any of the methods or types discussed in this guide, see the following API Documentation: