Document Replacement using update MongoDB Java

In the previous posts we have seen examples Logical Querying MongoDB Java Example
on how we can query a MongoDB using Java. We have seen different ways in which we can invoke find() method and how to do comparison querying and logical querying. This post will focus
on updating of our existing documents present in our mongo database using Java.

Update MongoDB Java Example :

Here also, we will continue with the same example that we have used in our previous posts.

{ 
  "_id" : { "$oid" : "53559e2ca2f880fe942e76c8"} , 
  "firstName" : "eliot" , 
  "lastName" : "Horowitz" , 
  "founder" : "mongo" , 
  "wiki" : "http://en.wikipedia.org/wiki/Eliot_Horowitz" , 
  "hits" : 38000
}

Replace the existing document with a new document:
With this approach we can completely replace an existing document with a new document.This new document can be completely different in structure from the existing document.Ideally this approach is used when you have the requirement of changing the structure of document without changing its primary key i.e _id.

All the documents in collection :

{ "_id" : ObjectId("53559e2ca2f880fe942e76c4"), "firstName" : "rod", "lastName" : "johnson", "founder" : "Spring", "wiki" : "http://en.wikipedia.org/wiki/Rod_Johnson_%28programmer%29", "hits" : 45000}
{ "_id" : ObjectId("53559e2ca2f880fe942e76c5"), "firstName" : "kevin", "lastName" : "ryan", "founder" : "mongo", "wiki" : "http://en.wikipedia.org/wiki/Kevin_P._Ryan", "hits" : 32000 }
{ "_id" : ObjectId("53559e2ca2f880fe942e76c6"), "firstName" : "james", "lastName" : "gosling", "founder" : "java", "wiki" : "http://en.wikipedia.org/wiki/James_Gosling", "hits" : 98000 }
{ "_id" : ObjectId("53559e2ca2f880fe942e76c7"), "firstName" : "doug", "lastName" : "cutting", "founder" : "Spring", "wiki" : "http://en.wikipedia.org/wiki/Doug_Cutting", "hits" : 28000 }
{ "_id" : ObjectId("53559e2ca2f880fe942e76c8"), "firstName" : "eliot", "lastName" : "Horowitz", "founder" : "mongo", "wiki" : "http://en.wikipedia.org/wiki/Eliot_Horowitz", "hits" : 38000 }

Update method in mongodb takes 2 arguments , first one is basically the criteria that will be used to find the documents and second one is the modifier document that will replace all the documents matching the criteria.

Javadoc for update :

calls DBCollection.update(com.mongodb.DBObject, com.mongodb.DBObject, boolean, boolean) with upsert=false and multi=false
Parameters:
q search query for old object to update
o object with which to update q
Returns:
Throws:
MongoException
@dochub
update

Now in the above example I am interested in adding the middle name and country to “Eliot” record.Moreover I also want to club all the fields (first name,middle name , last name and country) into 1 single field personalDetails. This example will also give you an idea on the nested documents in MongoDB.
Java Code :

package com.lotusmedia.mongo;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;

public class DocumentReplacement {
	
	private static void replaceDocument(){
		DBCollection lotusCollection=MongoUtils.getCollection("lotusCollection");
	
        	//Search Object
		 DBObject searchObject =new BasicDBObject();
		 searchObject.put("firstName", "eliot");		
		
		//Modifier Document
		DBObject parentObject=lotusCollection.findOne(searchObject);
		DBObject childObject = new BasicDBObject();
		childObject.put("firstName", parentObject.get("firstName"));
		childObject.put("middleName","R");
		childObject.put("lastName", parentObject.get("lastName"));
		childObject.put("country", "USA");
		parentObject.put("personalDetails", childObject);
		parentObject.removeField("firstName");
		parentObject.removeField("lastName");
		
		//call update
		lotusCollection.update(searchObject, parentObject); //multi is false
		System.out.println("Document Replaced Successfully");
	}
	

	public static void main(String[] args) {
		DocumentReplacement.replaceDocument();
	}

}

Once the update method is executed it will replace our original document with the new one that we had provided in the argument and it will look like this. Now you will see an embedded document is also present inside the parent documents containing firtsName,middleName,lastName and country.

{
  "_id" : { "$oid" : "53559e2ca2f880fe942e76c8"} ,
  "personalDetails" : { "firstName" : "eliot" , "middleName" : "R" , "lastName" : "Horowitz" , "country" : "USA"}
  "founder" : "mongo" , 
  "wiki" : "http://en.wikipedia.org/wiki/Eliot_Horowitz" , 
  "hits" : 38000 , 
}

One important thing that you will notice that _id of both the older and newer document is same because we have used the original document itself to create the new document.
Now consider a scenario when your search criteria returns more than 1 document , then there are 2 scenarios possible.Assume I have changed my searchObject criteria to this before calling update.

BasicDBObject searchObject = new BasicDBObject();
searchObject.put("founder", "mongo");
lotusCollection.update(searchObject, parentObject);

Now this search criteria will return me 2 documents as we have 2 spring founders in our collection Kevin and Eliot.

  1. When the record that you want to update comes first in the collection.In our example we want to update Eliot details but in our collection it comes in the last and before it one more record satisfying the same criteria i.e Kevin , so when it tries to update this record it tries to update _id also , but this _id is already present in the collection associated with Eliot record, hence updation will fail and it will give you an obvious exception.
    Exception in thread "main" com.mongodb.MongoException: The _id field cannot be changed from {_id: ObjectId('53559e2ca2f880fe942e76c5')} to {_id: ObjectId('53559e2ca2f880fe942e76c8')}.
    	at com.mongodb.CommandResult.getException(CommandResult.java:100)
    	at com.mongodb.CommandResult.throwOnError(CommandResult.java:134)
    	at com.mongodb.DBTCPConnector._checkWriteError(DBTCPConnector.java:142)
    	at com.mongodb.DBTCPConnector.say(DBTCPConnector.java:183)
    	at com.mongodb.DBTCPConnector.say(DBTCPConnector.java:155)
    	at com.mongodb.DBApiLayer$MyCollection.update(DBApiLayer.java:349)
    	at com.mongodb.DBCollection.update(DBCollection.java:177)
    	at com.mongodb.DBCollection.update(DBCollection.java:208)
    	at com.mongodb.DBCollection.update(DBCollection.java:220)
    	at com.lotusmedia.mongo.DocumentReplacement.replaceDocument(DocumentReplacement.java:35)
    	at com.lotusmedia.mongo.DocumentReplacement.main(DocumentReplacement.java:41)
    
  2. When the record that we want to update comes first in the collection.In our example assume if our Eliot record comes before Kevin , then it won’t give you any exception.

Honestly I found this behavior annoying as updation of records depends on the location where they are present in the collection.Ideally behavior should be consistent for both the scenarios.

I tried to find the reason behind about it and what I observed by default multi flag is false (can be seen in update javadoc at top), that means it will update only the first matching record and if we make this flag true then we will observe the same exception as mentioned above.

This is one of the way of updating documents in the mongodb , where we are fetching the document first, modifying it and then putting it back into the collection. Ideally it is not the recommended way of doing this and mongodb provides modifiers also to achieve the same thing.

Update using modifiers we will see in the upcoming posts.

Let'sConnect

Saurabh Jain

A Developer working on Enterprise applications ,Distributed Systems, Hadoop and BigData.This blog is about my experience working mostly on Java technologies ,NoSQL ,git , maven and Hadoop ecosystem.
Let'sConnect

One thought on “Document Replacement using update MongoDB Java

Add Comment Register



Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>