11

what is the idiomatic way to upsert a document using version 3 of the mongodb java driver (specifically v3.0.1)?

We have a collection for sessions and when a new session gets created or modified, we want to upsert it in one operation - rather than having to query if a document exists yet and then either inserting or replacing.

Our old upsertion code used the scala driver casbah 2.7.3. It looked like:

import com.mongodb.casbah.MongoCollection
import com.mongdb.DBObject
val sessionCollection: MongoCollection = ...
val sessionKey: String = ...
val sessionDocument: DBObject = ... // Either create a new one, or find and modify an existing one

sessionCollection.update(
    "_id" -> sessionKey,
    sessionDocument
    upsert = true
)

In our current project we're just using the plain java 3.0.1 driver and we're using BsonDocument instead of DBObject to make it more typsafe. I tried to replace the above with something like:

import com.mongodb.client.MongoCollection
val sessionCollection: MongoCollection = ...
val sessionKey: String = ...
val sessionDocument: BsonDocument = // Either create a new one, or find and modify an existing one

val updateOptions = new UpdateOptions
updateOptions.upsert(true)

sessionCollection.updateOne(
    "_id" -> new BsonString(sessionKey),
    sessionDocument,
    updateOptions
)

This throws the error "java.lang.IllegalArgumentException: Invalid BSON field name ...". The error is covered in this question but the op in that question wasn't trying to upsert in one operation - they were using context to decide whether to replace/update/insert etc...

I'm happy with code samples in scala or java.

Thanks!

Community
  • 1
  • 1
rmin
  • 1,018
  • 1
  • 9
  • 18

1 Answers1

14

In the Mongo Java Driver 3.0 series we added a new Crud API which is more explicit and therefore beginner friendly. This initiative has been rolled out over a number of MongoDB Drivers but it does contain some changes compared to the older API.

As you are not updating an existing document using an update operator, the updateOne method is not appropriate.

The operation you describe is a replaceOne operation and can be run like so:

sessionCollection.replaceOne(
    "_id" -> new BsonString(sessionKey),
    sessionDocument,
    (new UpdateOptions()).upsert(true)
)
Ross
  • 17,861
  • 2
  • 55
  • 73
  • thanks for the help. I think though what you're suggesting is not what I'm after. I want an operation that inserts a new session if none exists with that id, or updates the existing session if it does exist (ie. an "upsertion"). `replaceOne` only does the latter. When I use your code snippet to try and insert a fresh session in the database, it naturally returns `AcknowledgedUpdateResult{matchedCount=0, modifiedCount=null, upsertedId=null}` and has no effect on the database. – rmin Jun 09 '15 at 07:46
  • 1
    Make sure you set the upserted in the updateOptions, I've clarified the example. As you aren't updating via an update operator, but rather are doing an upsert replace then you need the `replaceOne` method. – Ross Jun 09 '15 at 08:54
  • 2
    Thanks Ross! It works now. If your answer is meant to be scala code, can you put brackets around `new UpdateOptions` - ie. change it to `(new UpdateOptions).upsert(true)`. Otherwise it doesn't compile. – rmin Jun 11 '15 at 01:22