Executing update and delete operations using a Java client

In the previous recipe, we saw how to execute the find and insert operations in MongoDB using a Java client. In this recipe, we will see how the update and delete operations work from a Java client.

Getting ready

For this recipe, we will start a standalone instance. Refer to the Single node installation of MongoDB recipe in Chapter 1, Installing and Starting the MongoDB Server, to learn how to start the server.

The next step is to download the Java project mongo-cookbook-javadriver from the book's website. This recipe uses a JUnit test case to test out various features of the Java client. In this whole process, we will make use of some of the most common API calls and, thus, learn how to use them.

How to do it…

To execute the test case, one can either import the project in an IDE such as Eclipse and execute the test case or execute the test case from the command prompt using Maven.

The test case we are going to execute for this recipe is com.packtpub.mongo.cookbook.MongoDriverUpdateAndDeleteTest.

If you are using an IDE, open this test class and execute it as a JUnit test case. If you plan to use Maven to execute this test case, go to the command prompt, change the directory to the root of the project, and execute the following command to execute this single test case:

$ mvn -Dtest=com.packtpub.mongo.cookbook.MongoDriverUpdateAndDeleteTest test

Everything should execute fine if the Java SDK and Maven are properly set up and the MongoDB server is up and running and listening to port 27017 for incoming connections.

How it works…

We will create test data for the recipes using the setupUpdateTestData() method. Here, we will simply put documents in the javaTest collection in the javaDriverTest database. We will add 20 documents in this collection with the value of i ranging from 1 to 20. This test data is used in different test case methods to create test data.

Let's now take a look at the methods in this class. We will first look at basicUpdateTest(). In this method, we will first create test data and then execute the following update method:

collection.update(
  new BasicDBObject("i", new BasicDBObject("$gt", 10)),
  new BasicDBObject("$set", new BasicDBObject("gtTen", true)));

The update method here takes two arguments: the first one is the query that will be used to select the eligible documents for the update, and the second parameter is the actual update. The first parameter looks confusing due to nested BasicDBObject instances; however, it is the {'i' : {'$gt' : 10}} condition, and the second parameter is the {'$set' : {'gtTen' : true}} update. The result of the update is an instance of com.mongodb.WriteResult. The instance of WriteResult tells us about the number of documents that got updated, the error that occurred while executing the write operation, and the write concern used for the update. Refer to the Java docs of the WriteConcern class for more details. This update, by default, only updates the first matching document, and only if multiple documents match the query.

The next method that we will look at is multiUpdateTest, which will update all the matching documents for the given query instead of the first matching document. The method we used on the collection instance is updateMulti. The updateMulti method is just a convenient method to update multiple documents. The following is the call that we will make to update multiple documents:

collection.updateMulti(new BasicDBObject("i",
  new BasicDBObject("$gt", 10)),
  new BasicDBObject("$set", new BasicDBObject("gtTen", true)));

The next operation that we will perform is to remove documents. The test case method to remove documents is deleteTest(). The documents are removed as follows:

collection.remove(new BasicDBObject(
  "i", new BasicDBObject("$gt", 10)),
  WriteConcern.JOURNALED);

We have two parameters here. The first one is the query for which matching documents will be removed from the collection. Note that all the matching documents will be removed by default, unlike in update, where only the first matching document will be removed by default. The second parameter is the write concern to be used for the remove operation.

Note that, when the server is started on a 32-bit machine, journaling is disabled by default. Using the journaling write concern on such machines causes the operation to fail with the following exception:

com.mongodb.CommandFailureException: { "serverUsed" : "localhost/127.0.0.1:27017" , "connectionId" : 5 , "n" : 0 , "badGLE" : { "getlasterror" : 1 , "j" : true} , "ok" : 0.0 , "errmsg" : "cannot use 'j' option when a host does not have journaling enabled" , "code" : 2}

This will need the server to be started with the --journal option. On 64-bit machines, this is not necessary, as journaling is enabled by default.

We will look at the findAndModify operation next. The test case method to perform this operation is findAndModifyTest. The following lines of code are used to perform this operation:

DBObject old = collection.findAndModify(
    new BasicDBObject("i", 10),
    new BasicDBObject("i", 100));

The operation is the query that will find the matching documents and then update them. The return type of the operation is an instance of DBObject before the update is applied. One important feature of the findAndModify operation is that the find and update operations are performed atomically.

The preceding method is a simple version of the findAndModify operation. There is an overloaded version of this method with the following signature:

DBObject findAndModify(DBObject query, DBObject fields, DBObject sort ,boolean remove, DBObject update, boolean returnNew, boolean upsert)

Let's see what these parameters are in the following table:

Parameter

Description

query

The find and modify operations have to find and modify the documents. This value of this parameter is the query that is used to query the documents that would be later modified.

fields

The find method supports a projection of the fields that it needs to be selected in the result document(s). The parameter here does the same job of selecting a fixed set of fields from the resulting document.

sort

If you haven't noticed already, let me tell you that the sort method can perform an atomic operation on only one document and also returns one document. This sort function can be used in cases where the query selects multiple documents, and only the first gets chosen for the operation. The sort operation is applied on the result before picking up the first document to update it.

remove

This is a Boolean flag that indicates whether to remove or update the document. If this value is true, the document will be removed.

update

This, unlike the remove attribute, is not a Boolean value but a DBObject instance that will tell what the update needs to be. Note that the removed Boolean flag gets precedence over this parameter. If the remove attribute is true, the update will not happen even if one is provided.

returnNew

The find operation returns a document, but which one? The one before the update was executed or the one after the update gets executed? When this Boolean flag is given as true, it returns the document after the update is executed.

upsert

This is a Boolean flag again that, when true, executes the upsert operation. It is relevant only when the intended operation is update.

There are more overloaded methods of this operation. Refer to the Java docs of com.mongodb.DBCollection for more methods. The findAndModify method we used ultimately invokes the method we discussed earlier with the fields and sort parameters as null and the remaining remove, returnNew, and upsert parameters being false.

Finally, we will look at query builder support in MongoDB Java API.

All the queries in Mongo are DBObject instances with possibly more nested DBObject instances in them. Things are simple for small queries, but they start getting ugly for more complicated queries. Consider a relatively simple query where we want to query for documents with i > 10 and i < 15. The Mongo query for this is {$and:[{i:{$gt:10}}, {i:{$lt:15}}]}. Writing this in Java using BasicDBObject instances is painful, and it looks as follows:

DBObject query = new BasicDBObject("$and",
  new BasicDBObject[] {
    new BasicDBObject("i", new BasicDBObject("$gt", 10)),
      new BasicDBObject("i", new BasicDBObject("$lt", 15))
  }
);

Thankfully, however, there is a class called com.mongodb.QueryBuilder that is a utility class to build complex queries. The preceding query is built using query builder as follows:

DBObject query = QueryBuilder.start("i").greaterThan(10).and("i").lessThan(15).get();

This is less error-prone when writing a query and is easy to read as well. There are a lot of methods in the com.mongodb.QueryBuilder class, and I would like you to go through the Java docs of this class. The basic idea is to start construction using the start method and the key. We will then chain the method's calls to add different conditions and, when the addition of various conditions is done, the query is constructed using the get() method, which returns DBObject. Refer to the queryBuilderSample method in the test class for a sample usage of query builder support of MongoDB Java API.

See also

  • Chapter 5, Advanced Operations, to know some more operations using GridFS and geospatial indexes and how to use them from the Java application with a small sample
  • The Java docs for the current version of the MongoDB driver at https://api.mongodb.org/java/current/
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset