Executing update and delete operations using a Java client

In the previous recipe, we saw how to execute find and insert operations in MongoDB using the Java client; in this recipe, we will see how updates and deletes work in the Java client.

Getting ready

For this recipe, we will start a standalone instance. Refer to the Installing single node MongoDB recipe from Chapter 1, Installing and Starting the Server for instructions on how to start the server.

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

How to do it…

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

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

  1. If you are using an IDE, open this test class and execute it as a JUnit test case.
  2. If you are planning to use Maven to execute this test case, go to the command prompt, change the directory at the root of the project, and execute the following to execute this single test case:
    $ mvn -Dtest=com.packtpub.mongo.cookbook.MongoDriverUpdateAndDeleteTest test
    

Everything should get executed 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 the incoming connections.

How it works…

We created a test data for the recipes using a setupUpdateTestData() method. Here, we simply put documents in the javaTest collection in the javaDriverTest database. We add 20 documents to 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 first create the test data and then execute the following update:

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 is the query that would 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 update, {'$set' : {'gtTen' : true}}. The result of the update is an instance of com.mongodb.WriteResult. The instance of WriteResult tells us the number of documents that got updated and gets the error that occurred while executing the write operation and write concern used for the update. Refer to the Javadocs of the WriteConcern class for more details. This update only updates the first matching document by default 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 that we used is updateMulti on the collection instance. The updateMulti method is a convenient way to update multiple documents. The following is the call that we 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 did was 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 is the query for which the matching documents will be removed from the collection. Note that all matching documents will be removed by default unlike 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. When you use Write Concern on such machines, it may cause 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 would require 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

This is the query that is used to query the document, which is the one that gets updated/deleted.

fields

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

sort

If you haven't noticed already, let me tell you that the method can perform this atomic operation on only one document and also return 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 function is applied on the result before picking up the first document to update.

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

Unlike the preceding attribute, this is not a Boolean value but a DBObject instance that will tell what the update needs to be. Note that the remove 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? This Boolean flag, when given as true, returns the document after the update is executed.

upsert

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

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

Finally, we look at the query builder support in MongoDB's 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 would mean using BasicDBObject instances, which is even painful and 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, which is a utility class to build the complex queries. The preceding query is built using a 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 easy to read as well. There are a lot of methods in the com.mongodb.QueryBuilder class and I would encourage you to go through the Javadocs of this class. The basic idea is to start construction using the start method and the key. We then chain the method 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 the MongoDB Java API.

See also

There are some more operations using the GridFS and geospatial indexes. We will see how to use them in the Java application with a small sample in the advanced query chapter. Refer to Chapter 5, Advanced Operations for such recipes.

The Javadocs for the current version of the MongoDB driver can be found 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