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

49,298

Solution 1

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'}}")   );

Solution 2

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

Solution 3

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);

Solution 4

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"} } )

Solution 5

DBCollection dbCollection = db.getCollection("mycollection");
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("_id", "3"); 
// your update condition - or the query
DBObject newObject =  dbCollection.find(dbObject).toArray().get(0);
// I just take the first element. Can iterate through as per your requirement if multiple fields        exist
newObject.put("key","value");
//add field, either a new field or any existing field
dbCollection.findAndModify(dbObject, newObject);

Just use the above steps . You can change the details without affecting other items associated to the same key.

Share:
49,298
longda
Author by

longda

Distributed systems, native applications, and data visualizations. Industry experience in aerospace, media, entertainment, and video games. Interested in React, D3.js, Unity3d, data science, bots, IoT, and smart cities.

Updated on July 09, 2022

Comments

  • longda
    longda almost 2 years

    References:

    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
    
  • longda
    longda over 13 years
    MY EYES! THEY BURN! Thank you so much for this, that makes total sense now... the code that they're making us write for this is SO ugly though! :)
  • longda
    longda over 13 years
    Thanks! I think I like this code a bit better as it's slightly clearer than the standard syntax... I'll try it out.
  • tim_yates
    tim_yates over 13 years
    hahaha...yeah... I've put a rough attempt a a category to build the DBObjects up from a map in as an edit...not tested it tho...
  • hansvb
    hansvb over 13 years
    Yeah, Java makes working with JSON ugly. The Java driver should provide ways to pass in JSON strings (but that is also hindered by lack of multi-line strings and alternate quote characters in Java), and also some builder methods with meaningful names (so you do not have to type $set).
  • Gandalf
    Gandalf over 13 years
    I tried this and could not get it to parse. It didn't like the single quotes, and then the second JSON.parse cannot be cast to a DBObject (it's a HashMap).
  • hansvb
    hansvb over 13 years
    @Gandalf: Sorry about that, untested code. But it should be very similar to the above.