Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I update fields of documents in mongo db using the java driver?

References:

  • http://www.mongodb.org/display/DOCS/Java+Tutorial

Still pretty new to mongo db but I'm trying to update part of an existing document inside a collection... unfortunately, the link above doesn't have an update example.

Essentially, i just want to be able to:

  1. Add new fields to a document
  2. Update existing fields of a document to a new value

Here's my code (Grails + Groovy + Java + MongoDB + the java driver):

def shape = mongo.shapes.findOne(new BasicDBObject("data", "http://www.foo.com")); // get the document
mongo.shapes.update(new BasicDBObject("_id", shape._id), new BasicDBObject("isProcessed", 0));  // add a new "isProcessed" field set to 0
mongo.shapes.update(new BasicDBObject("_id", shape._id), new BasicDBObject("data", "http://www.bar.com"));

This pretty much clobbers the entire object... I might try just modifying the original shape object and then running the update on that. But until then, does anyone have experience updating just individual fields (rather than the entire document)?

EDIT:

I just tried it and was able to successfully update by sending the entire object across with new and/or updated fields and that works. I wonder if the driver is smart enough to only update the smallest subset of changes or if it's just blindly updating the entire thing? (In the case below, is it just updating the foo field across the wire or the entire shape document?)

Code:

def shape = mongo.shapes.findOne(); // get the first shape to use as a base
shape.removeField("_id");  // remove the id field
shape.put("foo","bar");  // add a new field "foo"
mongo.shapes.insert(shape);  // insert the new shape
def shape2 = mongo.shapes.findOne(new BasicDBObject("foo", "bar"));  // get the newly inserted shape (and more importantly, it's id)
shape2.put("foo", "bat");  // update the "foo" field to a new value
mongo.shapes.update(new BasicDBObject("_id", shape2._id), shape2);  // update the existing document in mongo
like image 772
longda Avatar asked Aug 27 '10 00:08

longda


4 Answers

I wonder if the driver is smart enough to only update the smallest subset of changes or if it's just blindly updating the entire thing?

No, if you use the "normal" update method, the whole object will be sent over the wire. I suspect that the database server itself will be clever enough to only update the necessary indexes (and not the ones that did not change), if possible (i.e. the object could be updated in place and did not have to be moved because it grew too much)

What you can do is use the "atomic update modifier" functions. The Java documentation is a bit light on them, but since the driver just transmits the JSON, the stuff from the non-Java tutorials should work, for example:

shapes.update((DBObject)JSON.parse(    "{ 'foo' : 'bar'}"),  
    (DBObject) JSON.parse(          "{ '$set' : { 'foo': 'bat'}}")   );
like image 130
Thilo Avatar answered Oct 21 '22 20:10

Thilo


Found an example here, which seems to show the usage for the update call. So for your example, I believe something like this should work?

// Find an object
def shape2 = mongo.shapes.findOne( new BasicDBObject( 'foo', 'bar' ) )
// And update the foo field from 'bar' to 'bat'
mongo.shapes.update( shape2, new BasicDBObject( '$set', new BasicDBObject( 'foo', 'bat' ) ) )

Edit

You might be able to use a category to construct your BasicDBObjects in a more groovy way...

Something like this might do it:

class BasicDBObjectMapBuilder {
  static String toDbObj( String s ) { s }
  static BasicDBObject toDbObj( Map m ) {
    m.inject( null ) { r, it -> new BasicDBObject( it.key, it.value.toDbObj() ) }
  }
}

use( BasicDBObjectMapBuilder ) {
  def shape2 = mongo.shapes.findOne( new BasicDBObject( 'foo', 'bar' ) )
  // And update the foo field from 'bar' to 'bat'
  mongo.shapes.update( shape2, [ '$set':[ 'foo', 'bat' ] ].toDbObj() )
}

I haven't tested this though...

Edit 2

Actually, BasicDBObject is a Map, so you should be able to do:

  mongo.shapes.update( shape2, [ '$set':[ 'foo', 'bat' ] ] as BasicDBObject )

without needing the builder

like image 34
tim_yates Avatar answered Oct 21 '22 19:10

tim_yates


A lot of the answers on this post are using older versions of the Mongo Java Driver. If you're using a newer version of the Java Driver (v3.0+) then the preferred method seems to be to use the Document object instead of the DBObject interface.

Here is an example:

MongoClient client = new MongoClient();
MongoCollection<Document> fooCollection = client.getDatabase("test").getCollection("foo");

Bson filter = Filters.eq("_id", "123d45678c467bb433c99f99");
Bson updates = Updates.set("isFoo", true);
fooCollection.findOneAndUpdate(filter, updates);
like image 7
robjwilkins Avatar answered Oct 21 '22 19:10

robjwilkins


This answer uses the mongo shell, but shows how you can go deep into a JSON object structure to modify a specific field without overwriting the rest.

Given the JSON object in a collection called 'my_collection':

{ 
  "_id" : ObjectId("50fdb2a73f7bc7a5acecc4f8"), 
  "data" : { 
    "list" : [ 0, 1, 2, 7, 4, 5 ], 
    "subobj" : { 
       "var_a":"valuea",
       "var_b":"valueb" 
     }
  }
}

To update 'var_b' , without overwriting anything else:

db.my_collection.update({"_id":"50fdb2a73f7bc7a5acecc4f8"}, { "$set":{"data.subobj.var_b":"new_value"}})

To update the 3rd element in the array 'list' with value '99', without overwriting anything else:

db.my_collection.update({"_id":"50fdb2a73f7bc7a5acecc4f8"}, { "$set":{"data.list.2":"99"} } )
like image 3
ElStepherino Avatar answered Oct 21 '22 18:10

ElStepherino