Java - MongoDB Minimum Operator - java

I have tried to following to calculate the minimum in Java using the Java driver for MongoDB:
for (int i = 0; i < uniqueRegions.size(); i++) {
BasicDBObject query = new BasicDBObject("Region", uniqueRegions.get(i))
.append("Year", new BasicDBObject("$min", "Year"));
cursor = coll.find(query);
try {
while (cursor.hasNext()) {
System.out.println(uniqueRegions.get(i));
System.out.println(cursor.next());
}
} finally {
cursor.close();
}
}
However I get the error:
Exception in thread "main" com.mongodb.MongoException: Can't canonicalize query: BadValue unknown operator: $min
I've created a query with the $min operator isolated:
BasicDBObject query = new BasicDBObject("$min", "Year");
and it still generates the error so the problem is with how I'm using it. Can someone tell me the correct syntax to get this to work please.

Related

MongoDB - Implementing nor query in java

I have the following query which I am trying to implement in Java (I am using 3.4.2 mongo-java driver) :
Json:
{
"_id" : "Team:2334918",
"fieldName" : [
"Delivery",
"Support"
],
"isDeleted" : false
}
Here the query:
db.myCollection.find({$nor: [{“fieldName”: {$exists: false}},{“fieldName”:{$size:0}}]})
I have written the following code to implement the above query:
MongoClient mc = ctm.getMongoConn();
db = ctm.getDatabase(mc);
col = db.getCollection(collectionName);
BasicDBObject searchObject = new BasicDBObject();
List<BasicDBObject> searchArguments = new ArrayList<BasicDBObject>();
searchArguments.add(new BasicDBObject("fieldName",new BasicDBObject("$exists",false)));
searchArguments.add(new BasicDBObject("fieldName",new BasicDBObject("$size",0)));
searchObject.put("$nor", searchArguments);
MongoCursor<Document> curs = (MongoCursor<Document>) col.find(searchObject);
while (curs.hasNext()){
Document doc = curs.next();
Object name1 = doc.get("fieldName");
System.out.println(name1.toString());
j++;
}
I am getting the following error while running:
java.lang.ClassCastException: com.mongodb.FindIterableImpl cannot be cast to com.mongodb.client.MongoCursor
Replace this:
MongoCursor<Document> curs = (MongoCursor<Document>) col.find(searchObject);
while (curs.hasNext()) {
}
With this:
FindIterable<Document> docs = col.find(searchObject);
for (Document d : docs) {
}
The suggestion from #Veeram (in the comments above) is correct too.
Try
MongoCursor<Document> curs = col.find(searchObject).iterator(); to get cursor.
You don't have to get hold of cursor. You can use the methods provided in the FindIterable which iterates the cursor for you. example at the end.
Also you are mixing Document (3.x) and BasicDBObject (2.x). You can replace your search builder with
Filters searchObject = Filters.nor(Filters.exists("fieldName"), Filters.size("fieldName", 0);
FindIterable<Document> curs = col.find(searchObject);
curs.forEach();
More info here by the author.

mongo BulkWriteOperation with upsert option doesn't return upserted entries for updates

I'm using mongo-java-driver-3.0.4 jar in my application. My mongodb version is 3.2.10.
Basically what i'm trying is to do a bulk write operation to create or insert documents. I'm trying this with the upsert option. What i notice is the following:
Whenever new documents are created, the BulkWriteResult#getUpserts() returns me the List<BulkWriteUpsert> with the created documents.
However when i'm trying to update existing documents, BulkWriteResult#getUpserts() returns me an empty array.
I use the following snippet:
DBCollection coll = getDBCollection();
BulkWriteOperation bulkWriteOperation = coll.initializeUnorderedBulkOperation();
for() { //in a loop to populate the bulkWriteOperation
DBObject obj = getDbObject();
bulkWriteOperation.find(getQueryObject()).upsert().replaceOne(obj);
}
BulkWriteResult result = bulkWriteOperation.execute(writeConcern)
This looks to be a bug in the driver but i'm not sure, since as per the api i see this description:
Gets an unmodifiable list of upserted items, or the empty list if
there were none.
which i understood as get the list of items which are either updated or inserted.
The upserts will only be populated when the upsert resulted in an insert. Here's a MCVE that demonstrates:
public class BulkUpsertTest {
public static void main(String args[]) throws UnknownHostException {
MongoClient m = new MongoClient("localhost");
DBCollection coll = m.getDB("test").getCollection("bulkUpsertTest");
coll.drop(); // drop the collection so that first iteration is insert
test(coll); // first iteration insert
test(coll); // second iteration update
}
public static void test(DBCollection coll) {
BulkWriteOperation bulkWriteOperation = coll.initializeUnorderedBulkOperation();
for(int i = 0; i < 3; i++) {
DBObject query = new BasicDBObject("_id", i);
DBObject obj = new BasicDBObject("x", i);
bulkWriteOperation.find(query).upsert().replaceOne(obj);
}
BulkWriteResult result = bulkWriteOperation.execute();
List<BulkWriteUpsert> upserts = result.getUpserts();
System.out.println("result: " + result);
if (upserts != null) {
System.out.println("Upserts size: " + upserts.size());
} else {
System.out.println("Upserts is null");
}
}
}
Output (note differences in counts for matchedCount, modifiedCount, and upserts for the 2 cases:
result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=0, removedCount=0, modifiedCount=0, upserts=[BulkWriteUpsert{index=0, id=0}, BulkWriteUpsert{index=1, id=1}, BulkWriteUpsert{index=2, id=2}]}
Upserts size: 3
result: AcknowledgedBulkWriteResult{insertedCount=0, matchedCount=3, removedCount=0, modifiedCount=3, upserts=[]}
Upserts size: 0
From java docs for BulkWriteUpsert
Represents an upsert request in a bulk write operation that resulted
in an insert. It contains the index of the upsert request in the
operation and the value of the _id field of the inserted document.
So it contains the write operations that resulted in insert but not update.
In case of updates this property may be relevant to you BulkWriteResult.nModified
More information here on BulkWriteResult

Get affected rows after Mongo update

How can I instruct Mongo to return the affected rows after doing an update?
Currently it returns
{ "serverUsed" : "localhost:27017" , "ok" : 1 , "n" : 12 , "updatedExisting" : true}
which means 12 records was updated, but I want to find out which records they were.
One workaround is to do two queries, first to find the _id's, and second to actually update them but this is not good enough.
Many thanks
R.
Use the findAndModify() API. For example, following the guideline from this blog post MongoDB findAndModify() examples using Mongo Shell and Java Driver, you can write your update query as:
// findAndModify operation. Update colour to blue for cars having speed < 45
DBObject query = new BasicDBObject("speed",
new BasicDBObject("$lt", 45));
DBObject update = new BasicDBObject();
update.put("$set", new BasicDBObject("color", "Blue"));
DBCursor cursor = coll.find();
try {
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
} finally {
cursor.close();
}
coll.findAndModify(query, update);

mongoDB: cursor notimeout setting isn't working in java client

i set an 'notimeout' option to a dbcursor in java:
BasicDBObject nearbyQueries = new BasicDBObject("$gt", 0)
.append("$lte", 2);
DBCursor trueClassInstances = locationsCollection.find(new BasicDBObject("distanceFromHotel", nearbyQueries)).addOption(Bytes.QUERYOPTION_NOTIMEOUT).limit(100000);
double counter = 0;
int currentPresent = 0;
for (DBObject instance : trueClassInstances) {
...
}
even with this option i set, this exception is thrown:
Exception in thread "main" com.mongodb.MongoCursorNotFoundException: Query failed with error code -5 and error message 'Cursor 1876954464377 not found on server XXXXXX:27017' on server XXXXXXXX:27017
at com.mongodb.connection.GetMoreProtocol.receiveMessage(GetMoreProtocol.java:115)
at com.mongodb.connection.GetMoreProtocol.execute(GetMoreProtocol.java:68)
at com.mongodb.connection.GetMoreProtocol.execute(GetMoreProtocol.java:37)
at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:155)
at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:219)
at com.mongodb.connection.DefaultServerConnection.getMore(DefaultServerConnection.java:194)
at com.mongodb.operation.QueryBatchCursor.getMore(QueryBatchCursor.java:197)
at com.mongodb.operation.QueryBatchCursor.hasNext(QueryBatchCursor.java:93)
at com.mongodb.MongoBatchCursorAdapter.hasNext(MongoBatchCursorAdapter.java:46)
at com.mongodb.DBCursor.hasNext(DBCursor.java:152)
at locationExtraction.DistanceClassification.FeatureAnalyzer.main(FeatureAnalyzer.java:27)
FeatureAnalyzer.java:27 is the for loop line.
this problem appear in other project with similar setting...
what am i doing wrong? maybe my choice of 'for' loop instead of this kind of iteration can cause this strange behavior?
while(cursor.hasNext())
{
DBObject next = cursor.next();
}
Thanks
Looks like you are not able to process each batch within time limit. Try reducing batch size so that each batch could be consumed before time runs out. This should help.
cursor.addOption(com.mongodb.Bytes.QUERYOPTION_NOTIMEOUT).batchSize(100)
so the problem is solved.
this is very strange but there is a problem with using 'for' loop for iterating on cursor. so dont do it like i did it, use 'while' loop:
while(cursor.hasNext())
{
DBObject next = cursor.next();
}
before use cursor.hasNext() and cursor.next() to do business logical, just before you get the mongo cursor, invoke FindIterable object's noCursorTimeout(true) method. for example:
FindIterable<Document> findIterable = sentenceColl.find(condition);
// set no timeout
findIterable.noCursorTimeout(true);
MongoCursor<Document> mongoCursor = findIterable.iterator();
while (mongoCursor.hasNext()) {
mongoCursor.next();
}
try this:
Iterator<BasicDBObject> it = null;
it = coll.find("{"field": {$in:#}}", fieldList).with(
new QueryModifier() {
public void modify(DBCursor cursor) {
cursor.setOptions(Bytes.QUERYOPTION_NOTIMEOUT);
}
}
).as(BasicDBObject.class);

Using mongo-java-driver to manipulate arrays

I'm using mongo-java-driver-2.11.2. I got stuck with array manipulation. I succeed in creating an array but failed with an exception of bad key: "$PUSH" while trying to insert an element into the array. Here is my code:
DBCursor cursor = iiCollection.find(new BasicDBObject(TOKEN, token.getToken()));
if(cursor.hasNext()){
DBObject o = cursor.next();
int freq = (Integer) o.get(FREQ);
iiCollection.update(o, new BasicDBObject(FREQ, freq + 1)
.append("$push", new BasicDBObject(DOCS, docId)));
}
else{
iiCollection.insert(new BasicDBObject(TOKEN, token.getToken())
.append(FREQ, 1)
.append(DOCS, new String[]{docId}));
}
The 'else' branch is ok, but the first branch won't work, please help me figure out the problem.
The following code should do the trick:
iiCollection.update(o, new BasicDBObject("$set",new BasicDBObject("freq", freq+1)).append("$push", new BasicDBObject("docs", 1)));
The reason for this is that when you execute the code like below,
new BasicDBObject("freq", freq+1).append("$push", new BasicDBObject("docs", 1))
it converts to
{ "freq" : 3 , "$push" : { "docs" : 1}}
which is incorrect.

Categories