I'm using Casbah (mongodb scala library). I have an insert that doesn't work.
val builder = MongoDBObject.newBuilder
builder += "_id" -> token.uuid
builder += "email" -> token.email
builder += "creationTime" -> token.creationTime
builder += "expirationTime" -> token.expirationTime
builder += "isSignUp" -> token.isSignUp
val writeResult = mycollection += (builder.result)
If I change this for something simpler (like, a simple {"hello": "world"} document), the insert is done. So I know there's something that doesn't work with this particular insert. However, I find no way to know why. I'd like to get some feedback from Mongo or from Casbah.
However the WriteResult class, which apparently comes directly from the Java MongoDB driver, seems very opaque: http://api.mongodb.com/java/3.0/com/mongodb/WriteResult.html
How can I get some info about why an insert is failing? I'm not asking about this particular insert. Just, in general, how can I get info about the error that caused an insert to fail?
Thanks for your help.
Casbah is a Scala wrapper over the Java MongoDB driver.
mycollection += (builder.result)
is translated into
mycollection.save(builder.result)
If the operation had an error it will throw an exception like described here.
The WriteResult containing information about the write if no error happened.
I would check:
getN and isUpdateOfExisting values in WriteResult because save is doing either update or insert (read more here).
wasAcknowledged value in WriteResult to make sure you get the exception and you don't have the WriteConcern set to UNACKNOWLEDGED.
Related
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.
I've setup full text search and MongoDB and it's working quite well (Mongo 2.6.5).
However it does an OR instead of and AND.
1) Is it possible to make the query an AND query, while still getting all the benefits of full text search (stemming etc.)
2) And if so, is it possible to add this option via the Morphia wrapper library
EDIT
I see that the full text search includes a 'score' for each document returned. Is it possible to only return docs with a certain score or above. Is there some score that would represent a 'fuzzy' and query. That is usually all tokens are in the document but not absolutely always. If so this would solve the problem as well.
Naturally if possible to do this via Morphia that would be super helpful. But I can use the native java driver as well.
Any pointers in the correct direction, much appreciated.
EDIT
Code looks like this, I'm using Morphia 1.0.1:
Datastore ds = Dao.instance().getDatabase();
Query<Product> q = ds.createQuery(Product.class).search("grey vests");
List<Product> prods = q.asList();
Printing the query gives:
{ "$text" : { "$search" : "grey vests"}}
Note: I am able to do take an intersection of multiple result sets to create an AND query. However this is very slow since something like "grey" will return a massive result set and be slow at feeding the results back.
EDIT
I've tried to chain the search() calls and add a single 'token' to each call. But I am getting a run time error. Code becomes:
q.search("grey").search("vests");
The query I get is (which seems like it's doing the right thing) ...
{ "$and" : [ { "$text" : { "$search" : "grey"}} , { "$text" : { "$search" : "vests"}}]}
The error is:
com.mongodb.MongoQueryException: Query failed with error code 17287 and error message 'Can't canonicalize query: BadValue Too many text expressions' on server ...
at com.mongodb.connection.ProtocolHelper.getQueryFailureException(ProtocolHelper.java:93)
Using Mongo Java Driver 2.13 and Mongo 3.0.
I am trying to move from Spring Data save() to MongoDB API's Bulk Writing since I am saving/updating about 100K objects. I am trying to write the Service/Repository layer code where I can pass in a Collection of my specific Objects and be able to either create new records or update existing records, or in other words upsert. When I do an insert the performance is very acceptable.
If I update the code to do upserts the performance is just way too slow. Am I doing something wrong in the following code sample (note it is scaled down to just the necessary logic, i.e. no error handling):
public void save(Collection<MyDomainObject> objects) {
BulkWriteOperation bulkWriter = dbCollection.initializeUnorderedBulkOperation();
for(MyDomainObject mdo : objects) {
DBObject dbObject = convert(mdo);
bulkWriter.find(new BasicDBObject("id",mdo.getId()))
.upsert().updateOne(new BasicDBObject("$set",dbObject));
}
bulkWriter.execute(writeConcern);
}
Note that I also tried replaceOne() instead of updateOne() with the same results.
I also noticed in the Mongo log that "nscannedObjects" keeps increasing while "nMatched", "nModified" and "upsert" are never larger than 1. Does this mean that it is table scanning for each record?
Am I using upsert the correct way? Any other suggestions?
Thanks to ry_donahue I figured out the issue.
It was not using the correct ID field, which is the index. In the conversion of the domain object to a DBObject there ended up being an "id" and an "_id" field.
I also changed updateOne() to replaceOne(). So now the code looks like this:
public void save(Collection<MyDomainObject> objects) {
BulkWriteOperation bulkWriter = dbCollection.initializeUnorderedBulkOperation();
for(MyDomainObject mdo : objects) {
DBObject dbObject = convert(mdo);
bulkWriter.find(new BasicDBObject("_id",new ObjectId(mdo.getId()))).upsert().replaceOne(dbObject);
}
bulkWriter.execute(writeConcern);
}
This now gives very good performance.
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.