Trying the following in the Mongo shell (and tried it in Java) to update or insert a document. The filter should return false but I am expected the document to be created due to upsert = true
db.eventLogs.updateOne(
{monthid:"062017", elementid:123456},
{$push: {
events: {sensor1:"sensor1 value",
sensor2:"sensor2 value"
}
}
},
{Upsert:true} )
The statement is acknowledged but nothing is inserted into the collection even though Upsert = true.
I'm clearly being an idiot so can someone put me out of my misery?
Java version...
Bson filter = Document.parse(argFilterJson);
Bson operations = Document.parse(argOperationsJson);
UpdateOptions options = new UpdateOptions().upsert(true);
return this.getCollection(argDBName, argCollectionName).updateOne(filter, operations, options);
The values of argFilterJson and argOperationsJson are exactly as per the shell.
Weirdly, after restarting my MongoDB server, the Java code has started to work as expected.
Related
I'm trying to replace (not update with $set) documents in MongoDB using mongo-hadoop in Spark (mongo-hadoop-core-1.4.2.jar & mongo-java-driver-3.2.1.jar) :
BasicDBObject query = new BasicDBObject();
query.append("_id", 6972);
BasicDBObject update = new BasicDBObject();
update.append("_id", 6988);
update.append("f1", "ACTIVE_USER");
Then I'm writing something like this :
new MongoUpdateWritable(query, update, false, true);
But this is failing with :
Caused by: java.lang.IllegalArgumentException: Invalid BSON field name f1
I can do :
new MongoUpdateWritable(query, new BasicDBObject("$set", update), false, true);
But I want to replace the whole document.
Update:
mongo-hadoop added support for replacing documents in 2.0.0-rc0. From then on, to replace a document entirely, you'll have to use
new MongoUpdateWritable(query, update, false/*or true*/, false, true)
Note: you can't use replacing and multiUpdate = true together, and replacing also doesn't change the _id value - see Replace a Document Entirely:
The update() method does not replace the _id value.
...
update() cannot update multiple documents.
Looking at the code, that doesn't seem to be possible: MongoOutputCommitter always issues an update command (that can only contain $operators) - see MongoOutputCommitter.java line 155 and below. They use BulkUpdateRequestBuilder's update / updateOne methods, but to issue a replace command they need to use replaceOne method.
So, no such feature. Maybe you'll make a pull request :)
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!
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)
)
I would like to handle failure of insert to collection (using Java) in order to be sure that my insert was successful. In case that insert fails, I want to perform some fall-back action.
Assuming following code in Java and latest mongo driver (version 2.11.3):
BasicDBObject doc = new BasicDBObject("name", "MongoDB");
WriteResult result = coll.insert(WriteConcern.SAFE, doc);
I am confused by the fact that the insert returns WriteResult and it could throw MongoException. What am I supposed to do in order to safely detect any possible failure of insert? Please, provide code example. And if you can clarify when insert throws exception and when it just returns some error write result. I tried to search in java driver API docs at http://api.mongodb.org/java/2.11.3/ for it; howerever, this infromation is missing there.
WriteResult result;
try {
result = coll.insert(WriteConcern.SAFE, doc);
}
catch(MongoException ex){
logger.warn("Insert failed.", ex);
throw ex;
}
//Shall I check result here for additional errors?
If I should check, what type of error I am able to detect by checking insert result?
You need to take a look at "WriteConcern", it has the all behaviors you need.
You can use it per one write like this:
coll.insert(dbObj, WriteConcern.SAFE);
If you use WriteConcern.SAFE your operation will wait for an acknowledgement from the primary server, so if no exception is raised then you're ok.
Or you can set default behaviour for all write operations when you are creating MongoClient:
MongoClientOptions.Builder builder = new MongoClientOptions.Builder();
builder.writeConcern(WriteConcern.JOURNAL_SAFE);
MongoClient mongoClient = new MongoClient(
new ServerAddress("localhost"), builder.build());
[Based on Colin Morelli's comment] If you don't use a WriteConcern that raises exceptions, you can use the WriteResult.getLastError() to determine if it was successful or not. Similarly, if you use WriteConcern.SAFE, and the write succeeds, WriteResult will have useful information on it such as the number of records that were written.
Here you can read about WriteConcern in general.
I'm just wondering if there's way to check if a given document is updated or inserted in MongoDB after a upsert operation in Java:
DBCollection col = getCollection();
// either this
col.findAndModify(query, null, null, false, contact, false, true);
// or this
WriteResult wr = col.update(query, contact, true, false);
Generally, WriteResult will hold information regarding whether it was successful or not; if successful, the document will be there.
You could also do a simple col.count(new BasicDBObject("_id",{id of your document})) and check if the value is 1.
1-> step : set the profiler using > db.setProfilingLevel(2); on the document target.
2-> run this query
db.system.profile.find({},{ts:1, op:1, ns:1}).sort({ts: -1}).forEach(printjson);
it will show you in the op attribute if the given document is updated or inserted and ns is the document name. ts when the query run.
I'm totally new to MongoDB. I'm using Morphia to access it from Java.
I need to do something along the lines of this:
public boolean isUnique(short s){
//OPERATION 1: Check in mongo if object with field "id" = s exists
//if so, return false
//else,
//OPERATION 2: create this object in the database
//and return true
}
The problem for me to grasp is not the actual syntax, but the problem with atomicity. How do I assure that only one thread/process can have access to the document so that OP1 and OP2 are atomic?
I think this has to be managed on a database level since the Java server is in a clustered environment.
Regards,
Anders
You could replace both operations with one upsert of the {id: s} document that becomes a no-op if the document already exists, but an insert if it doesn't. Calling the getLastError command (the driver can do this for you) will tell you whether a new document was created or not.
I'm using Java and Morphia so here is what the actual code looks like, if anyone should be wanting to achieve the same thing in the future. It's the true in the call to updateFirst that tell Mongo that it's an upsert:
public boolean checkIfExistsAndInsertOtherwise(short id){
Datastore datastore = getDatastore();
Query<OrganizationId> updateQuery = datastore.createQuery(OrganizationId.class).field("identificationNumber").equal(id);
//Bogus operation, just set id to itself
UpdateOperations<OrganizationId> ops = datastore.createUpdateOperations(OrganizationId.class).set("identificationNumber", id);
UpdateResults<OrganizationId> result = datastore.updateFirst(updateQuery, ops, true,WriteConcern.SAFE);
return result.getInsertedCount() == 0;
}