Quick Start: Java and MongoDB - Update Operations

Maxime Beugnet

#Java
Quick Start Java and MongoDB

In a previous blog post, I showed you how to read documents in MongoDB using Java. In this blog post, I will show you how to update these documents.

Getting Set Up

Just like in the previous blog post, I will use the same repository. If you don't have a copy of it yet, you can clone it:

git clone https://github.com/mongodb-developer/java-quick-start

If you have previously cloned this repository, just make sure you are using the latest version:

git pull

If you didn't set up your free cluster on MongoDB Atlas, now is great time to do so. You have all the instructions in this blog post.

Created Data

In the previous blog post, we created the class Read. This time we will work in the Update class.

In my create blog post, we wrote 11 new grades, one for the student with {"student_id": 10000.0} and 10 for the student with {"student_id": 10001.0} in the sample_training.grades collection. We will update theses documents in this blog post.

As a reminder, here are the grades of my student with {"student_id": 10000.0}.

MongoDB Enterprise Cluster0-shard-0:PRIMARY> db.grades.findOne({"student_id":10000.0})
{
	"_id" : ObjectId("5daa0e274f52b44cfea94652"),
	"student_id" : 10000,
	"class_id" : 1,
	"scores" : [
		{
			"type" : "exam",
			"score" : 39.25175977753478
		},
		{
			"type" : "quiz",
			"score" : 80.2908713167313
		},
		{
			"type" : "homework",
			"score" : 63.5444978481843
		},
		{
			"type" : "homework",
			"score" : 82.35202261582563
		}
	]
}

We also discussed BSON types in the create blog post and we noted that student_id and class_id are doubles.

MongoDB treats some types as equivalent for comparison purposes. For instance, numeric types undergo conversion before comparison.

So do not be surprised in this blog post if I filter with an integer number and match a document which contains a double number for example. If you want to filter documents by value types, you can use the $type operator.

You can read more about type bracketing and comparison and sort order in our documentation.

Update One Document

Let's edit the document above. To achieve this, we will use the method updateOne.

Please create a class Update in the com.mongodb.quickstart package with this code:

package com.mongodb.quickstart;

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.ReturnDocument;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.json.JsonWriterSettings;

import java.util.logging.Level;
import java.util.logging.Logger;

import static com.mongodb.client.model.Filters.and;
import static com.mongodb.client.model.Filters.eq;
import static com.mongodb.client.model.Updates.*;

public class Update {

    public static void main(String[] args) {
        Logger.getLogger("org.mongodb.driver").setLevel(Level.WARNING);
        JsonWriterSettings prettyPrint = JsonWriterSettings.builder().indent(true).build();

        try (MongoClient mongoClient = MongoClients.create(System.getProperty("mongodb.uri"))) {
            MongoDatabase sampleTrainingDB = mongoClient.getDatabase("sample_training");
            MongoCollection<Document> gradesCollection = sampleTrainingDB.getCollection("grades");

            // update one document
            Bson filter = eq("student_id", 10000);
            Bson updateOperation = set("comment", "You should learn MongoDB!");
            UpdateResult updateResult = gradesCollection.updateOne(filter, updateOperation);
            System.out.println("=> Updating the doc with {\"student_id\":10000}. Adding comment.");
            System.out.println(gradesCollection.find(filter).first().toJson(prettyPrint));
            System.out.println(updateResult);
        }
    }
}

As you can see in this example, the method updateOne takes 2 parameters:

  • the first one is the filter that identifies the document we want to update,
  • the second one is the update operation. Here, we are setting a new field comment with the value "You should learn MongoDB!".

In order to run this program, make sure you set up your mongodb.uri in your system properties using your IDE if you want to run this code in your favorite IDE (see my first blog post for more details).

Alternatively, you can use this maven command line in your root project (where the src folder is):

mvn compile exec:java -Dexec.mainClass="com.mongodb.quickstart.Update" -Dmongodb.uri=mongodb+srv://USERNAME:PASSWORD@cluster0-abcde.mongodb.net/test?w=majority

The standard output should look like this:

=> Updating the doc with {"student_id":10000}. Adding comment.
{
  "_id": {
    "$oid": "5dd5c1f351f97d4a034109ed"
  },
  "student_id": 10000.0,
  "class_id": 1.0,
  "scores": [
    {
      "type": "exam",
      "score": 21.580800815091415
    },
    {
      "type": "quiz",
      "score": 87.66967927111044
    },
    {
      "type": "homework",
      "score": 96.4060480668003
    },
    {
      "type": "homework",
      "score": 75.44966835508427
    }
  ],
  "comment": "You should learn MongoDB!"
}
AcknowledgedUpdateResult{matchedCount=1, modifiedCount=1, upsertedId=null}

Upsert

An upsert is a mix between an insert operation and an update one. It happens when you want to update a document, assuming it exists, but it actually doesn't exist yet in your database.

In MongoDB, you can set an option to create this document on the fly and carry on with your update operation: this is an upsert operation.

In this example, I want to add a comment to the grades of my student 10002 for the class 10 but this document doesn't exist yet.

filter = and(eq("student_id", 10002d), eq("class_id", 10d));
updateOperation = push("comments", "You will learn a lot if you read the MongoDB blog!");
UpdateOptions options = new UpdateOptions().upsert(true);
updateResult = gradesCollection.updateOne(filter, updateOperation, options);
System.out.println("\n=> Upsert document with {\"student_id\":10002.0, \"class_id\": 10.0} because it doesn't exist yet.");
System.out.println(updateResult);
System.out.println(gradesCollection.find(filter).first().toJson(prettyPrint));

As you can see, I'm using the third parameter of the update operation to set the option upsert to true.

I'm also using here the static method Updates.push() to push a new value in my array comments which does not exist yet, so I'm creating an array of one element in this case.

This is the output we get:

=> Upsert document with {"student_id":10002.0, "class_id": 10.0} because it doesn't exist yet.
AcknowledgedUpdateResult{matchedCount=0, modifiedCount=0, upsertedId=BsonObjectId{value=5ddeb7b7224ad1d5cfab3733}}
{
  "_id": {
    "$oid": "5ddeb7b7224ad1d5cfab3733"
  },
  "class_id": 10.0,
  "student_id": 10002.0,
  "comments": [
    "You will learn a lot if you read the MongoDB blog!"
  ]
}

Update many documents

The same way I was able to update one document with updateOne(), I can update multiple documents with updateMany().

filter = eq("student_id", 10001);
updateResult = gradesCollection.updateMany(filter, updateOperation);
System.out.println("\n=> Updating all the documents with {\"student_id\":10001}.");
System.out.println(updateResult);

In this example, I'm using the same updateOperation as earlier, so I'm creating a new one element array comments in these 10 documents.

Here is the output:

=> Updating all the documents with {"student_id":10001}.
AcknowledgedUpdateResult{matchedCount=10, modifiedCount=10, upsertedId=null}

The findOneAndUpdate method

Finally, we have one last very useful method available in the MongoDB Java Driver: findOneAndUpdate().

In most web application, when a user update something, he wants to see this update reflected in his web page. Without the findOneAndUpdate() method, you would have to run an update operation and then fetch the document with a find operation to make sure you are printing the latest version of this object in the web page.

The findOneAndUpdate() method allow you to combine these two operations in one.

// findOneAndUpdate
filter = eq("student_id", 10000);
Bson update1 = inc("x", 10); // increment x by 10. As x doesn't exist yet, x=10.
Bson update2 = rename("class_id", "new_class_id"); // rename variable "class_id" in "new_class_id".
Bson update3 = mul("scores.0.score", 2); // multiply the first score in the array by 2.
Bson update4 = addToSet("comments", "This comment is uniq"); // creating an array with a comment.
Bson update5 = addToSet("comments", "This comment is uniq"); // using addToSet so no effect.
Bson updates = combine(update1, update2, update3, update4, update5);
// returns the old version of the document before the update.
Document oldVersion = gradesCollection.findOneAndUpdate(filter, updates);
System.out.println("\n=> FindOneAndUpdate operation. Printing the old version by default:");
System.out.println(oldVersion.toJson(prettyPrint));

// but I can also request the new version
filter = eq("student_id", 10001);
FindOneAndUpdateOptions optionAfter = new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER);
Document newVersion = gradesCollection.findOneAndUpdate(filter, updates, optionAfter);
System.out.println("\n=> FindOneAndUpdate operation. But we can also ask for the new version of the doc:");
System.out.println(newVersion.toJson(prettyPrint));

Here is the output:

=> FindOneAndUpdate operation. Printing the old version by default:
{
  "_id": {
    "$oid": "5dd5d46544fdc35505a8271b"
  },
  "student_id": 10000.0,
  "class_id": 1.0,
  "scores": [
    {
      "type": "exam",
      "score": 69.52994626959251
    },
    {
      "type": "quiz",
      "score": 87.27457417188077
    },
    {
      "type": "homework",
      "score": 83.40970667948744
    },
    {
      "type": "homework",
      "score": 40.43663797673247
    }
  ],
  "comment": "You should learn MongoDB!"
}

=> FindOneAndUpdate operation. But we can also ask for the new version of the doc:
{
  "_id": {
    "$oid": "5dd5d46544fdc35505a82725"
  },
  "student_id": 10001.0,
  "scores": [
    {
      "type": "exam",
      "score": 138.42535412437857
    },
    {
      "type": "quiz",
      "score": 84.66740178906916
    },
    {
      "type": "homework",
      "score": 36.773091359279675
    },
    {
      "type": "homework",
      "score": 14.90842128691825
    }
  ],
  "comments": [
    "You will learn a lot if you read the MongoDB blog!",
    "This comment is uniq"
  ],
  "new_class_id": 10.0,
  "x": 10
}

As you can see in this example, you can choose which version of the document you want to return using the appropriate option.

I also used this example to show you a bunch of update operators:

  • set will set a value,
  • inc will increment a value,
  • rename will rename a field,
  • mul will multiply the value by the given number,
  • addToSet is similar to push but will only push the value in the array if the value doesn't exist already.

There are a few other update operators. You can consult the entire list in our documentation.

The Final Code

package com.mongodb.quickstart;

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.FindOneAndUpdateOptions;
import com.mongodb.client.model.ReturnDocument;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.json.JsonWriterSettings;

import java.util.logging.Level;
import java.util.logging.Logger;

import static com.mongodb.client.model.Filters.and;
import static com.mongodb.client.model.Filters.eq;
import static com.mongodb.client.model.Updates.*;

public class Update {

    public static void main(String[] args) {
        Logger.getLogger("org.mongodb.driver").setLevel(Level.WARNING);
        JsonWriterSettings prettyPrint = JsonWriterSettings.builder().indent(true).build();

        try (MongoClient mongoClient = MongoClients.create(System.getProperty("mongodb.uri"))) {
            MongoDatabase sampleTrainingDB = mongoClient.getDatabase("sample_training");
            MongoCollection<Document> gradesCollection = sampleTrainingDB.getCollection("grades");

            // update one document
            Bson filter = eq("student_id", 10000);
            Bson updateOperation = set("comment", "You should learn MongoDB!");
            UpdateResult updateResult = gradesCollection.updateOne(filter, updateOperation);
            System.out.println("=> Updating the doc with {\"student_id\":10000}. Adding comment.");
            System.out.println(gradesCollection.find(filter).first().toJson(prettyPrint));
            System.out.println(updateResult);

            // upsert
            filter = and(eq("student_id", 10002d), eq("class_id", 10d));
            updateOperation = push("comments", "You will learn a lot if you read the MongoDB blog!");
            UpdateOptions options = new UpdateOptions().upsert(true);
            updateResult = gradesCollection.updateOne(filter, updateOperation, options);
            System.out.println("\n=> Upsert document with {\"student_id\":10002.0, \"class_id\": 10.0} because it doesn't exist yet.");
            System.out.println(updateResult);
            System.out.println(gradesCollection.find(filter).first().toJson(prettyPrint));

            // update many documents
            filter = eq("student_id", 10001);
            updateResult = gradesCollection.updateMany(filter, updateOperation);
            System.out.println("\n=> Updating all the documents with {\"student_id\":10001}.");
            System.out.println(updateResult);

            // findOneAndUpdate
            filter = eq("student_id", 10000);
            Bson update1 = inc("x", 10); // increment x by 10. As x doesn't exist yet, x=10.
            Bson update2 = rename("class_id", "new_class_id"); // rename variable "class_id" in "new_class_id".
            Bson update3 = mul("scores.0.score", 2); // multiply the first score in the array by 2.
            Bson update4 = addToSet("comments", "This comment is uniq"); // creating an array with a comment.
            Bson update5 = addToSet("comments", "This comment is uniq"); // using addToSet so no effect.
            Bson updates = combine(update1, update2, update3, update4, update5);
            // returns the old version of the document before the update.
            Document oldVersion = gradesCollection.findOneAndUpdate(filter, updates);
            System.out.println("\n=> FindOneAndUpdate operation. Printing the old version by default:");
            System.out.println(oldVersion.toJson(prettyPrint));

            // but I can also request the new version
            filter = eq("student_id", 10001);
            FindOneAndUpdateOptions optionAfter = new FindOneAndUpdateOptions().returnDocument(ReturnDocument.AFTER);
            Document newVersion = gradesCollection.findOneAndUpdate(filter, updates, optionAfter);
            System.out.println("\n=> FindOneAndUpdate operation. But we can also ask for the new version of the doc:");
            System.out.println(newVersion.toJson(prettyPrint));
        }
    }
}

Wrapping Up

The MongoDB Java Driver provides many different options to update documents so you can choose which one is the most adapted to your code.

If you want to learn more and deepen your knowledge faster, I recommend you check out the M220J: MongoDB for Java Developers training available for free on MongoDB University.

In the next blog post, I will cover delete operations so stay tuned.

Articles in this Quick Start Java and MongoDB series: